Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Programmieren allgemein (https://www.delphipraxis.net/40-programmieren-allgemein/)
-   -   aus c-Programm eine Delphi-dll aufrufen (https://www.delphipraxis.net/84477-aus-c-programm-eine-delphi-dll-aufrufen.html)

Susanne 17. Jan 2007 13:31


aus c-Programm eine Delphi-dll aufrufen
 
hallo,

vielleicht kann mir ja jemand weiterhelfen. Mein Problem besteht darin, dass ich von einem c-Programm auf eine in delphi erstellte dll zugreifen möchte.

Hier der delphi-code
Delphi-Quellcode:
library notiz;

uses
  SysUtils,
  Classes,
  ComObj,
  ActiveX,
  StdVcl,
  Windows,
  Messages,
  Variants,
  Graphics,
  Controls,
  Forms,
  Dialogs,
  AutoArchive_TLB,
  StdCtrls,
  Inifiles,
  ExtCtrls,
  uLogFile,
  uProgHilfe;

{$R *.res}

function anhaengen(dbID, docID, text: String): String;
var
  test   : String;
  sl_test : TStringList;
  Dms    : HypDms;
  Notiz  : IHypDocDosFile;
begin
  CoInitialize(nil);
  ShowMessage(dbid+' '+docID+' '+text);
  ShowMessage('hallo1');
  try
    Dms := CoHypDms.Create;
    ShowMessage('AA-Biblio initialisiert!');
  except
    ShowMessage('Die AA-Biblio konnten nicht aktiviert werden!');
    exit;
  end;
  ShowMessage('hallo2');
  notiz:= IHypDocDosFile(Dms.OpenDocument(dbID, strtoint(docID)));
  ShowMessage('hallo3');
  if (notiz <> nil) then begin
    ShowMessage('hallo4');
    if notiz.ExistComponent('CC_TEXT', 1) then begin
      ShowMessage('hallo5');
      test:= notiz.SaveComponentToString('CC_TEXT', 1);
    end;
  end else
  ShowMessage('hallo10');
  notiz:= nil;
  CoUnInitialize;
  ShowMessage('ende');
end;


exports
  anhaengen;

begin
end.

und ich versuche nun von dem C-Programm aus darauf zuzugriefen und das funktioniert nicht.

Der Teil in c sieht so aus:

HMODULE handle;
handle = LoadLibrary("notiz.dll");
if (handle != NULL)
{
//dies ist mit Sicherheit falsch definiert, da
//das alles strings sind, die die dll erwartet und zurückgibt,
//aber ich hab keine Ahnung, wie ich die umschreiben muss
typedef int (*DLLFUNC)(char*, char*, char*);
DLLFUNC func = (DLLFUNC)GetProcAddress(handle, "anhaengen");
if (func != NULL)
{
//das hier macht er nicht!
int i = func("Test", "5512", "Test c_code");
}
}
FreeLibrary(handle);

sirius 17. Jan 2007 13:34

Re: aus c-Programm eine Delphi-dll aufrufen
 
1. Du musst statt Strings PChar nehmen (Das eentspricht dem *char in C
2. Du musst die Funktion mit "stdcall" definieren, damit alle Übergabe-Variablen aus dem Stack geholt werden (dort wo sie auch C hinschreibt).

Susanne 17. Jan 2007 13:51

Re: aus c-Programm eine Delphi-dll aufrufen
 
danke, ich habe die Strings durch PChar ersetzt und das funktioniert auch soweit, dass die dll ihre Arbeit ordentlich tut. Die dll zeigt mir das als letztes an: ShowMessage('ende');

Danach kommt aber eine Fehlermeldung, mit der ich nichts anfangen kann:

Debug Error
File: i386\chkesp.c

The Value of ESP was not properly saved across a function call. This is usually a Result of calling a function declared with one calling convention with a function pointer declared with a different calling convetion

Was soll mir das sagen?

Muetze1 17. Jan 2007 13:52

Re: aus c-Programm eine Delphi-dll aufrufen
 
Ersetze das empfohlene stdcall in der Delphi DLL mit cdecl; und erstell es erneut.

/EDIT: Zur Erklärung: C verwendet per default (DLLFUNC) die Cdecl Aufufkonvention und nicht die StdCall.

Susanne 17. Jan 2007 13:55

Re: aus c-Programm eine Delphi-dll aufrufen
 
vielen dank, jetzt funktioniert es!

sirius 17. Jan 2007 13:57

Re: aus c-Programm eine Delphi-dll aufrufen
 
Zitat:

Zitat von Muetze1
ersetze das emfpohlene stdcall in der Delphi DLL mit cdecl; und erstell es erneut.

:wall: ach ja, ach ja :coder2:


Edit: C will sich halt doch selber um seinen Stack-Pointer kümmern.

Susanne 17. Jan 2007 15:23

Re: aus c-Programm eine Delphi-dll aufrufen
 
eine Frage hätte ich dann doch noch.

meine dll soll einen Rückgabewert an das c-programm zurückgeben (verkürzt dargestellt):

Delphi-Quellcode:
library notiz;

uses
  SysUtils,
  ...

{$R *.res}

function anhaengen(dbID, docID, text: PChar): PChar; cdecl;
var
  ...
begin
  Result:= '';
  if notiz.LoadComponentFromString(test , 'CC_TEXT', 1, true) then
    Result:=''
  else
    Result:='Error - laden der Komponente';
end;


exports
  anhaengen;

begin
end.
in meinem c-programm sieht das ja so aus:
char i = func("Test", "5512", "Test c_code");

ich hätte jetzt angenommen, dass wenn kein Fehler in der dll war, wäre i jetzt leer und ansonsten würde da eben dieser Error-Text drin stehen. Das stand nun aber immer nur Zahlen drin z.B. mal -128.

Wo liegt da noch das Problem?

sirius 17. Jan 2007 15:27

Re: aus c-Programm eine Delphi-dll aufrufen
 
Reicht es vielleicht auch, einfach einen Integerwert (als eine Art Fehlercode zu übergeben). Ist einfacher.

Oder fürs erste:
Result:=PChar('Error');

mkinzler 17. Jan 2007 15:29

Re: aus c-Programm eine Delphi-dll aufrufen
 
Versuch mal:

Code:
char i[80] = func("Test", "5512", "Test c_code");

Susanne 17. Jan 2007 15:36

Re: aus c-Programm eine Delphi-dll aufrufen
 
ok, dann eben als integer - danke

bei dem anderen bekomme ich folgende Fehlermeldung: 'char' kann nicht in 'char [80]' konvertiert werden

DGL-luke 17. Jan 2007 15:58

Re: aus c-Programm eine Delphi-dll aufrufen
 
sollte es nicht so heißen:

Code:
*char c = func(...);
//oder
char[80] c = func(...); //da solltens dann aber auch 80 sein.
Alle C-Stringverarbeitungs-Funktionen sollten mit beidem eigentlich klarkommen.

Muetze1 17. Jan 2007 16:49

Re: aus c-Programm eine Delphi-dll aufrufen
 
Bei der Rückgabe eines PChars liegt das Problem mit dem Speichermanager vor. Die DLL alloziiert den Speicher für den String, du gibst die Adresse dessen zurück - aber beim verlassen der Funktion verliert der String seine Gültigkeit und der Speichermanager gibt den Platz wieder frei. Dies geschieht bei den Vorschlägen mit dem konstanten Array für die Rückgabe als Empfangsplatz genauso wie bei dem Vorschlag von sirius, welcher sogar offensichtlich einen temporären TypeCast durchführt.

Abhilfe würde hier nur die Übergabe eines entsprechend grossen Buffers helfen beim Aufruf, in welchen dann die Funktion diesen Rückgabestring reinlegt, wenn er denn genügend groß ist.

Mehr zu dem Thema hatte Luckie mal sehr schön zusammen gefaßt: [Artikel] Rückgabe von Strings aus DLLs

DGL-luke 17. Jan 2007 17:16

Re: aus c-Programm eine Delphi-dll aufrufen
 
Dass ein Result(!=lokale Variable) seine Gültigkeit verliert, wär mir neu - könnte aber durchaus sein, bei dem ganzen rumgewurschtle zwischen C und Delphi ;-)

Muetze1 17. Jan 2007 17:21

Re: aus c-Programm eine Delphi-dll aufrufen
 
Zitat:

Zitat von DGL-luke
Dass ein Result(!=lokale Variable) seine Gültigkeit verliert, wär mir neu - könnte aber durchaus sein, bei dem ganzen rumgewurschtle zwischen C und Delphi ;-)

Wenn das Result in 32 Bit reinpasst und kein Zeiger auf einen Speicherbereich darstellt, hast du recht. Aber PChar ist ein Zeiger und der Speicherbereich dazu wird vom Memorymanager angelegt. Da der Rückgabetyp ein PChar ist, wird bei der direkten Zuweisung eines konstanten Strings hier eine temporäre Typkonvertierung durchgeführt, also ist das folgende beides das gleiche:
Delphi-Quellcode:
// Result vom Typ PChar!
  Result := 'hallo ballo';
  Result := PChar('hallo ballo');
Und hier verliert der String seine Gültigkeit mit verlassen der Procedure, da die Variable aus einer Adresse besteht (da Pointer), welche ihre Gültigkeit behält und im Register übergeben wird. Nur halt an der Adresse steht nix mehr.

DGL-luke 17. Jan 2007 17:45

Re: aus c-Programm eine Delphi-dll aufrufen
 
danke.

Man muss es also explizit alloziieren? Reicht da Setlength?

Muetze1 17. Jan 2007 18:30

Re: aus c-Programm eine Delphi-dll aufrufen
 
Hängt davon ab, wo der String definiert ist, welchen du mit SetLength() setzt.

a) Wenn der String lokal innerhalb der Funktion deklariert ist, klappt dies nicht.
b) wenn der String global in der DLL deklariert ist würde es klappen. Dann kannst du auch einen temporären PChar() Typecast machen für das Result, weil durch die globale Variable in der DLL verliert der erzeugte Zeiger auf den String Inhalt nicht seine Gültigkeit mit verlassen der Procedure.

sirius 18. Jan 2007 07:53

Re: aus c-Programm eine Delphi-dll aufrufen
 
Zitat:

Zitat von Muetze1
// Result vom Typ PChar!
Result := 'hallo ballo';
Result := PChar('hallo ballo');[/delphi]
Und hier verliert der String seine Gültigkeit mit verlassen der Procedure, da die Variable aus einer Adresse besteht (da Pointer), welche ihre Gültigkeit behält und im Register übergeben wird. Nur halt an der Adresse steht nix mehr.

Ich bin mir nicht sicher, aber könntes es auch so sein, dass gerade bei solchen Festlegungen, die Zeichenkette ('hallo hallo') im Datensegment (oder im Codesegment; falls man überhaupt noch von Segmenten sprechen kann; auf jeden Fall nicht Stack) der DLL liegt. Und dies ist solange gültig, wie die DLL im Speicher liegt. Den Zeiger, den du mit PChar('hallo hallo') erstellst, zeigt also irgendwo direkt in den Code der DLL, wo die Zeichenkette liegt. Und bis freelibrary ist dieser gültig.

Anders wäre der Fall, wenn man eine Variable übergebn will (z.B. vom Stack), da müsste man in der DLL mit getmem/stralloc arbeiten und im C dann wieder "free"-n.


Edit: Oi, hier gabs ja schon eine Seite zwei. Den letzten Eintrag @Muetze hatt ich noch nicht gelesen. Aber genau der, trifft ja auf die Fragestellung zu.

Muetze1 18. Jan 2007 08:48

Re: aus c-Programm eine Delphi-dll aufrufen
 
Zitat:

Zitat von sirius
Anders wäre der Fall, wenn man eine Variable übergebn will (z.B. vom Stack), da müsste man in der DLL mit getmem/stralloc arbeiten und im C dann wieder "free"-n.

Aber da der Stack hier von zwei unterschiedlichen Speichermanagern verwaltet wird und es somit eigentlich sogar zwei Stacks gibt, ist dies nicht möglich. Das C Programm kennt keine Alloc-Info von dem Stack der DLL und umgekehrt. Daher kann das C Programm keinen Speicher auf dem Stack der DLL freigeben.

sirius 18. Jan 2007 09:10

Re: aus c-Programm eine Delphi-dll aufrufen
 
Zitat:

Zitat von Muetze1
Aber da der Stack hier von zwei unterschiedlichen Speichermanagern verwaltet wird und es somit eigentlich sogar zwei Stacks gibt, ist dies nicht möglich. Das C Programm kennt keine Alloc-Info von dem Stack der DLL und umgekehrt. Daher kann das C Programm keinen Speicher auf dem Stack der DLL freigeben.

Na klar sind das verschiedene Stacks. Deswegen ja auch getmem bzw. Stralloc (ich glaube getmem ist besser für C). Da landet nix auf dem Stack (außer der Pointer, aber den übergebe ich ja). Aber wir arbeiten ja nicht mit Variablen sondern mit einem zur Entwicklungszeit festgelegten Zeichenkette.

Muetze1 18. Jan 2007 09:37

Re: aus c-Programm eine Delphi-dll aufrufen
 
Zitat:

Zitat von sirius
Na klar sind das verschiedene Stacks. Deswegen ja auch getmem bzw. Stralloc (ich glaube getmem ist besser für C). Da landet nix auf dem Stack (außer der Pointer, aber den übergebe ich ja). Aber wir arbeiten ja nicht mit Variablen sondern mit einem zur Entwicklungszeit festgelegten Zeichenkette.

GetMem() wird vom Speichermanager implementiert. StrAlloc() greift auf GetMem() zurück. Der Speichermanager wiederrum verwaltet seinen eigenen Speicherbereich nach seinem eigenen gutdünken. Dabei alloziiert er nicht für diesen String Speicher von Windows (nicht direkt). Der Speichermanager hat seinen Pool von Speicher und er hat das Wissen darüber wo was wie gross alloziiert ist oder nicht. Wenn nun also Speichermanager 1 etwas in seinem Bereich alloziiert und verwaltet, dieser Speicherbereich zurück gegeben wird und bei Speichermanager 2 wieder freigegeben wird, dann kratzt sich der Speichermanager 2 nur am Kopf, schliesslich kennt seine Verwaltung keinen solchen Speicherbereich und auch die Adresse ist ihm suspekt. Daher kann er diesen Speicherbereich nicht freigeben.

sirius 18. Jan 2007 09:47

Re: aus c-Programm eine Delphi-dll aufrufen
 
Ja das stimmt.
Man wird aber irgendwelche API Funktion (virtualalloc, virtualfree) finden.

Susanne 22. Jan 2007 11:07

Re: aus c-Programm eine Delphi-dll aufrufen
 
es geht jetzt nochmal um die Übergabe der Parameter aus dem c-programm an die dll - das funktioniert irgendwie nicht so ganz

also, das c-programm übergibt in der Reihenfolge: dokumentennummer, archivnummer, notiztext
in der dll kommt aber an: archivnummer, dokummentennummer, notiztext

was kann ich da machen, das ist durcheinander, nicht mal von links nach rechts oder rechts nach links.

was kann ich da machen?


hat sich erledigt - war mein Fehler


Alle Zeitangaben in WEZ +1. Es ist jetzt 17:02 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz