Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Programmieren allgemein (https://www.delphipraxis.net/40-programmieren-allgemein/)
-   -   C Funktion, um zwei Strings zu verbinden in DLL (https://www.delphipraxis.net/75245-c-funktion-um-zwei-strings-zu-verbinden-dll.html)

Luckie 16. Aug 2006 14:09


C Funktion, um zwei Strings zu verbinden in DLL
 
Zur Übung versuche ich gerade eine DLL in C zu schreiben, die mir zwei Strings verbindet und diese dann aus einem Delphi-Programm zu nutzen. Ich mache das erstmal mit dem VC2005, weil ich im Moment mit dem GCC und Eclipse nicht weiterkomme.

Meine C-Funktion in der DLL sieht so aus:
Code:
extern "C" EXPORT int addstr(char *str1, char *str2, char *buffer)
{   
    strcat(str1, str2);
   strcpy(buffer, str1);   
   return strlen(buffer);
}
Der Delphi Code dazu:
Delphi-Quellcode:
type
  TAdd = function(a, b: Integer): Integer; stdcall;
  TAddStr = function(str1, str2: PChar; var Buffer: PChar): Integer; stdcall;

procedure TForm1.Button1Click(Sender: TObject);
var
  hLib: THandle;
  s: String;
  Add: TAdd;
  AddStr: TAddStr;
  res: Integer;
  str1, str2: String;
  Buffer: PChar;
begin
  s := IncludeTrailingBackSlash(ExtractFilePath(ParamStr(0)))+ 'FirstDLL.dll';
  hLib := LoadLibrary(PChar(s));
  if hLib <> 0 then
  begin
    @Add := GetProcAddress(hLib, 'add');
    if Assigned(Add) then
    begin
       res := Add(40, 2);
       ShowMessage(IntToStr(res));
    end
    else
      ShowMessage(SysErrorMessage(GetLastError));

    @AddStr := GetProcAddress(hLib, 'addstr');
    if Assigned(AddStr) then
    begin
      str1 := 'Hello ';
      str2 := 'World';
      GetMem(Buffer, length(str1) + length(str2));
      try
        res := AddStr(PChar(str1), PChar(str2), Buffer);
        ShowMessage(string(Buffer));
      finally
        FreeMem(Buffer);
      end;
    end
    else
      ShowMessage(SysErrorMessage(GetLastError));
    FreeLibrary(hLib);
  end
  else
    ShowMessage(SysErrorMessage(GetLastError));
end;
Aber ich bekomme in der DLL eine AccessViolation.

NicoDE 16. Aug 2006 14:15

Re: C Funktion, um zwei Strings zu verbinden in DLL
 
Zitat:

Zitat von Luckie
extern "C" EXPORT

Kannst du dir sparen wenn du eine DEF-Datei verwendest (EXPORTS).
Fehlt da nicht noch ein __stdcall, oder ist der Compiler so konfiguriert, dass es die Standardaufrufkonvention ist?

ps: das 'var' vor Buffer ist zuviel.

Der_Unwissende 16. Aug 2006 14:20

Re: C Funktion, um zwei Strings zu verbinden in DLL
 
Hi,
hast du es mal mit _fstrcat versucht? Könnte in deinem Fall sonst ein mögliches Problem werden. Bin nicht C-Crack genug um das hier mit Sicherheit sagen zu können, aber es gab ja Zeiten, in denen man ohne far-Pointer nur im Bereich von 64? KByte die Adressen haben durfte, bei einem Aufruf einer Dll kann das natürlich weniger garantiert werden.

Gruß Der Unwissende

Luckie 16. Aug 2006 14:21

Re: C Funktion, um zwei Strings zu verbinden in DLL
 
Wo kommt das __stdcall hin? Wenn ich es so mache:
Code:
extern "C" EXPORT int __stdcall addstr(char *str1, char *str2, char *buffer)
Dann habe ich wieder das name mangling. Eine DEF Datei verwende ich nicht, deswegen das extern "C".

Hm, bei API Funktionen macht man das aber doch auch so mit dem var in Delphi oder?

NicoDE 16. Aug 2006 14:25

Re: C Funktion, um zwei Strings zu verbinden in DLL
 
Zitat:

Zitat von Luckie
Dann habe ich wieder das name mangling. Eine DEF Datei verwende ich nicht, deswegen das extern "C".

Name Mangling hast du so oder so (ob nun _foo oder _foo@x ist doch egal).
Nimm eine DEF.

Zitat:

Zitat von Luckie
Hm, bei API Funktionen macht man das aber doch auch so mit dem var in Delphi oder?

*char ist ein PAnsiChar, keine Referefenz auf eine PAnsiChar-Variable.

Luckie 16. Aug 2006 14:29

Re: C Funktion, um zwei Strings zu verbinden in DLL
 
Wie geht das mit der DEF Datei? Wo muss die hin in dem VC2005 Projektpfad? Und was muss in ihr drinen stehen? Und muss ich dem VC25005 noch irgendwie sagen, dass er die DEF-Datei verwenden soll?

Zitat:

*char ist ein PAnsiChar, keine Referefenz auf eine PAnsiChar-Variable.
Wie machen denn das API Funktionen?

Flocke 16. Aug 2006 14:45

Re: C Funktion, um zwei Strings zu verbinden in DLL
 
Zitat:

Zitat von Luckie
Meine C-Funktion in der DLL sieht so aus:
Code:
extern "C" EXPORT int addstr(char *str1, char *str2, char *buffer)
{   
    strcat(str1, str2);
   strcpy(buffer, str1);   
   return strlen(buffer);
}

Zitat:

Zitat von Luckie
Aber ich bekomme in der DLL eine AccessViolation.

Das liegt wohl daran, dass du dir in C "str1" überschreibst. Richtiger wäre:
Code:
extern "C" EXPORT int addstr(char *str1, char *str2, char *buffer)
{   
    strcpy(buffer, str1);   
    strcat(buffer, str2);
    return strlen(buffer);
}

Luckie 16. Aug 2006 14:57

Re: C Funktion, um zwei Strings zu verbinden in DLL
 
Yippieyeaho. Es geht. :firejump:

Also als erstes __stdcall in den Projektoptionen eingestellt. Dann eine DEFF-Datei erstellt:
Code:
LIBRARY FirstDLL.dll

EXPORTS
   add
   addstr
Diese dem Linker bekannt gemacht: /DEF:FirstDLL.DEF und kompiliert. Rausgekommen ist eine DLL mit nicht dekorierten Funktionsnamen. Aufrugf von Delphi, wie gehabt ohne das var in der Funktionsdeklaration. Puh, jetzt kan ich auch Feierabend machen. ;)

Drei Dinge noch:
Wie machen das API-Funktionen, dass man da in Delphi das Schlüsselwort var benutzen muss.
Wie sicher ist die C-Funktion bezüglich BufferOverflows?
Und zu guter letzt, wie muss ich es machen, damit meine C-Funktion die erforderliche Größe des Buffers zurückgibt, falls dieser zu klein ist beim Aufruf?

Dax 16. Aug 2006 15:12

Re: C Funktion, um zwei Strings zu verbinden in DLL
 
Zitat:

Zitat von Luckie
Wie machen das API-Funktionen, dass man da in Delphi das Schlüsselwort var benutzen muss.

Delphi:
Delphi-Quellcode:
function Foo(var c: Cardinal): Cardinal;
C:
Code:
DWORD Foo(PDWORD c);
Sprich: Delphi macht var-Parameter implizit zu Pointern. Deswegen kann man vielen API-Funktionen, die irgendwas mit Records zu tun haben, diese direkt übergeben, ohne wie in C erst einen Pointer draus machen zu müssen.

Luckie 16. Aug 2006 15:14

Re: C Funktion, um zwei Strings zu verbinden in DLL
 
Aha. Bleiben noch Fragen zwei und drei. ;)

Flocke 16. Aug 2006 15:18

Re: C Funktion, um zwei Strings zu verbinden in DLL
 
Zitat:

Zitat von Luckie
Wie machen das API-Funktionen, dass man da in Delphi das Schlüsselwort var benutzen muss.

Zur Ergänzung zu Dax: das geht natürlich nicht mit Strings, da C einen anderen Speichermanager verwendet.

Zitat:

Zitat von Luckie
Wie sicher ist die C-Funktion bezüglich BufferOverflows?

Überhaupt nicht. Bessere Variante:
Code:
extern "C" EXPORT int addstr(char *str1, char *str2, char *buffer, int buflen)

    StringCbCopy(buffer, buflen, str1);
    StringCbCat(buffer, buflen, str2);
    return strlen(buffer);
}
Zitat:

Zitat von Luckie
Und zu guter letzt, wie muss ich es machen, damit meine C-Funktion die erforderliche Größe des Buffers zurückgibt, falls dieser zu klein ist beim Aufruf?

Ganz einfach: strlen(str1) + strlen(str2) mit buflen vergleichen und entsprechend handeln.

// EDIT: Parameterreihenfolge korrigiert

NicoDE 16. Aug 2006 15:27

Re: C Funktion, um zwei Strings zu verbinden in DLL
 
Zitat:

Zitat von Luckie
LIBRARY FirstDLL.dll

Kannst du dir sparen, du brauchts nur die EXPORTS :)
Zitat:

Zitat von Luckie
Wie machen das API-Funktionen, dass man da in Delphi das Schlüsselwort var benutzen muss.

Code:
void Foo(DWORD * Bar)
oder
void Foo(PDWORD Bar)
Lässt sich so (1)
Delphi-Quellcode:
procedure Foo(Bar: PDWORD)
so (2)
Delphi-Quellcode:
procedure Foo(const Bar: DWORD)
so (3)
Delphi-Quellcode:
procedure Foo(var Bar: DWORD)
oder so (4)
Delphi-Quellcode:
procedure Foo(out Bar: DWORD)
übersetzen.
Die Entscheidung hängt davon ab, ob Bar nil sein darf (1) oder nicht (2-4). Im letzteren Falle zwingt man den Delphi-Entwickler eine DWORD-Variable (bzw. impliziet deren Adresse) zu übergeben.
Zitat:

Zitat von Luckie
Wie sicher ist die C-Funktion bezüglich BufferOverflows?

Die von dir verwendeten, gar nicht.
Zitat:

Zitat von Luckie
Und zu guter letzt, wie muss ich es machen, damit meine C-Funktion die erforderliche Größe des Buffers zurückgibt, falls dieser zu klein ist beim Aufruf?

Ausrechnen?
(woher soll deine C-Funktion die Größe wissen, sie wird nicht übergeben)

Luckie 16. Aug 2006 16:54

Re: C Funktion, um zwei Strings zu verbinden in DLL
 
So, ich habe die Funktion jett noch etwas erweitert:
Code:
EXPORT int addstr(char *str1, char *str2, char *buffer, int lenbuffer)
{   
   if(strlen(str1) + strlen(str2) < lenbuffer)
   {
      return strlen(str1) + strlen(str2);
   }
   else
   {
      strcpy(buffer, str1);  
      strcat(buffer, str2);
      return strlen(buffer);
   }
}
Allerdings bekomme ich jetzt eine AV beim ersten Aufruf:
Delphi-Quellcode:
   @AddStr := GetProcAddress(hLib, 'addstr');
    if Assigned(AddStr) then
    begin
      str1 := 'Hello ';
      str2 := 'World';
      len := AddStr(PChar(str1), PChar(str2), nil, 0);
      GetMem(Buffer, len);
      try
        len := AddStr(PChar(str1), PChar(str2), Buffer, sizeof(Buffer));
        ShowMessage(string(Buffer) + '[' + IntToStr(len) + ']');
      finally
        FreeMem(Buffer);
      end;
    end
It also auch noch nicht so ganz das ware. :gruebel:

Flocke 16. Aug 2006 17:04

Re: C Funktion, um zwei Strings zu verbinden in DLL
 
Ist auch noch nicht ganz richtig, der Puffer braucht ein Zeichen mehr (abschließende Null), also: strlen(str1) + strlen(str2) + 1.

Luckie 16. Aug 2006 20:50

Re: C Funktion, um zwei Strings zu verbinden in DLL
 
OK, das erklärt aber nicht, warum es beim ersten Aufruf crasht, wenn ich als Buffer nil übergebe.

Flocke 16. Aug 2006 22:50

Re: C Funktion, um zwei Strings zu verbinden in DLL
 
Dann hab' ich dich nicht richtig verstanden. Ist der Funktionszeiger den korrekt? Kannst du im CPU-Fenster in die Routine hineindebuggen? Vielleicht siehst du da, wo der Fehler auftritt.

Luckie 16. Aug 2006 23:34

Re: C Funktion, um zwei Strings zu verbinden in DLL
 
Liste der Anhänge anzeigen (Anzahl: 1)
Ich hänge das Projekt mal an.

Dax 17. Aug 2006 00:00

Re: C Funktion, um zwei Strings zu verbinden in DLL
 
Deswegen crashts:
Code:
if(strlen(str1) + strlen(str2) < lenbuffer)
Du musst den Vergelich umdrehen.

Luckie 17. Aug 2006 00:02

Re: C Funktion, um zwei Strings zu verbinden in DLL
 
Warum das? :gruebel:

Nein, ändert auch nichts. Crasht immer noch. Ich habe eher das gefühl, er mag den Nullzeiger nicht.

Dax 17. Aug 2006 00:06

Re: C Funktion, um zwei Strings zu verbinden in DLL
 
Weil du kopierst, wenn der Buffer kürzer ist als beide Strings zusammen, und 0 ist definitiv kürzer als 11. Dann kopierst du auf PChar(0) und hast den Salat..

@edit: versteh ich nicht :gruebel:

Luckie 17. Aug 2006 00:11

Re: C Funktion, um zwei Strings zu verbinden in DLL
 
Stimmt. Aber er will immer noch nicht:
Code:
EXPORT int addstr(char *str1, char *str2, char *buffer, int lenbuffer)
{   
   if(strlen(str1) + strlen(str2) > lenbuffer)
   {
      return strlen(str1) + strlen(str2);
   }
   else
   {
      strcpy(buffer, str1);  
      strcat(buffer, str2);
      return strlen(buffer);
   }
}
Ich meinte, er scheint es nicht zu mögen, dass ich nil übergebe anstatt einen gültigen Speicherbereich. Zu mindest scheint die AV daraufhinzuweisen:
Zitat:

Access violation at address 10001087 in module 'FirstDLL.dll'. Write of address 00000000.
Ächtz. Ich habe die falsche DLL genommen. Kompiliert hab eich die Debug Version und benutzt die Release Version. :wall:

Allerdings ist der Buffer jetzt leer. Und in ShowMessage sthet nur die Länge.

Dax 17. Aug 2006 00:17

Re: C Funktion, um zwei Strings zu verbinden in DLL
 
Geht es so?
Code:
EXPORT int addstr(char *str1, char *str2, char *buffer, int lenbuffer)
{   
   if(strlen(str1) + strlen(str2) >= lenbuffer)
   {
      return strlen(str1) + strlen(str2) + 1;
   }
   else
   {
      strcpy(buffer, str1);  
      strcat(buffer, str2);
      return strlen(buffer);
   }
}

Luckie 17. Aug 2006 00:21

Re: C Funktion, um zwei Strings zu verbinden in DLL
 
Nein auch nicht. :gruebel:

Dax 17. Aug 2006 00:26

Re: C Funktion, um zwei Strings zu verbinden in DLL
 
Delphi-Quellcode:
type
  TAdd = function(a, b: Integer): Integer; stdcall;
  TAddStr = function(str1, str2: PChar; Buffer: PChar; lenbuffer: Integer): Integer; stdcall;
Ich kann mich irren, aber müsste das nicht in beiden Fällen cdecl sein?

Luckie 17. Aug 2006 00:28

Re: C Funktion, um zwei Strings zu verbinden in DLL
 
Nein. Ich habe im es VS auf stdcall gestellt.

Dax 17. Aug 2006 00:31

Re: C Funktion, um zwei Strings zu verbinden in DLL
 
Oh, hab ich wohl überlesen :oops:

Aber:
Delphi-Quellcode:
len := AddStr(PChar(str1), PChar(str2), Buffer, sizeof(Buffer));
ShowMessage(string(Buffer) + '[' + IntToStr(len) + ']');
Warum fällt mir das jetzt erst auf :wall:

sizeof(Buffer) => 4

Und: warum Buffer als PChar? Machs doch als string und übergebe @Buffer[1] als Parameter.

Luckie 17. Aug 2006 00:37

Re: C Funktion, um zwei Strings zu verbinden in DLL
 
Autsch. Jetzt wo du es sagst. :wall: :wall: :wall: :wall: :wall: :wall: :wall: :wall: :wall:

Dax 17. Aug 2006 00:39

Re: C Funktion, um zwei Strings zu verbinden in DLL
 
Auf einer 128-Bit-Maschine hätts funktioniert :zwinker:


Alle Zeitangaben in WEZ +1. Es ist jetzt 08:46 Uhr.

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