Delphi-PRAXiS
Seite 1 von 2  1 2      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Algorithmen, Datenstrukturen und Klassendesign (https://www.delphipraxis.net/78-algorithmen-datenstrukturen-und-klassendesign/)
-   -   String-Übergabe von Delphi-DLL an C# (https://www.delphipraxis.net/174093-string-uebergabe-von-delphi-dll-c.html)

Gargamel 3. Apr 2013 09:23

String-Übergabe von Delphi-DLL an C#
 
Hi

Wenn ich in C# einen String aus einer Delphi-DLL haben möchte, stürzt die C#-Anwendung ab.
Jetzt wurde mir gesagt, daß ich es so machen soll:

Delphi-Quellcode:
[DllImport("delphiDLL.dll")]
private static extern IntPtr delphi_getString();

...

IntPtr pStr = delphi_getString();
string str = Marshal.PtrToStringAnsi(pStr);
// now pinvoke to free string if needed
delphi_freeString(pStr);

...
Die Funktion delphi_getString sieht so aus:

Delphi-Quellcode:
function delphi_getString():PAnsiChar; cdecl; exports delphi_getString;

function delphi_getString():PAnsiChar;
var text:string;
Begin
  text:='TestString';
  result:=PAnsiChar(AnsiString(text));
End;
Was mich jetzt interessiert, ist die letzte Anweisung in dem C#-Code.

delphi_freeString(pStr);

Soweit ich weiß, werden doch in Delphi lokale Variablen und Pointer automatisch aus dem Speicher entfernt, wenn die Funktion beendet ist. Auch IntPtr pStr sollte in C# nur solange gültig sein, bis auch dort die Funktion beendet ist.
Oder irre ich mich hier komplett?


Danke

DeddyH 3. Apr 2013 09:44

AW: String-Übergabe von Delphi-DLL an C#
 
Und wenn Du die DLL-Funktion so umschreibst, dass sie einen PAnsiChar und dessen Länge als Parameter übergeben bekommt und dann lediglich befüllt? Ich möchte bezweifeln, dass ein Zeiger auf eine lokale Stringvariable innerhalb der DLL als Funktionsrückgabe funktioniert.

Der schöne Günther 3. Apr 2013 09:49

AW: String-Übergabe von Delphi-DLL an C#
 
So ganz habe ich das in Delphi jetzt immer noch nicht verstanden, aber wenn die Funktion endet, dann gibt es auch keine Referenz mehr auf den String und er wird entfernt.
Delphi-Quellcode:
PAnsiChar
kopiert, laut Hilfe, den String nicht, deine Funktion gibt letztendlich eine Adresse auf etwas zurück, dass wohl direkt wieder abgeräumt wird.

Mit
Delphi-Quellcode:
StrNew
scheint man explizit den String auf den Heap zu schreiben und dort zu lassen.

Geschickter wäre es allerdings, in deinem Programm den Speicher anzulegen, die Adresse an die DLL zu schicken und den Speicher auch selbst wieder im Programm freizuräumen, dann bräuchtest du das
Delphi-Quellcode:
delphi_freeString()
auch nicht.

geskill 3. Apr 2013 10:54

AW: String-Übergabe von Delphi-DLL an C#
 
Du musst in Delphi einen WideString benutzen, der passt mit den String Datentyp in C# überein.

CCRDude 3. Apr 2013 11:25

AW: String-Übergabe von Delphi-DLL an C#
 
DeddyH hat die Methode schlechthin schon angeführt - damit kommt man als DLL überall an. Nicht umsonst verwendet die WinAPI selber genau dieselbe :)

DeddyH 3. Apr 2013 11:43

AW: String-Übergabe von Delphi-DLL an C#
 
Ungetestet:
Delphi-Quellcode:
const
  ERROR_NO_ERROR        = 0;
  ERROR_BUFFER_TOO_SMALL = 1;

function delphi_getString(Buffer: PAnsiChar; var BufSize: DWORD): DWORD; cdecl;
var
  Test: AnsiString;
begin
  Test := 'Hallo Welt';
  if BufSize < Length(Test) then  
    Result := ERROR_BUFFER_TOO_SMALL;
  else
    begin
      StrPCopy(Buffer, Test);
      Result := ERROR_NO_ERROR;
    end;
  BufSize := Length(Test);
end;

Bernhard Geyer 3. Apr 2013 11:52

AW: String-Übergabe von Delphi-DLL an C#
 
Zitat:

Zitat von geskill (Beitrag 1209805)
Du musst in Delphi einen WideString benutzen, der passt mit den String Datentyp in C# überein.

C# (.NET) hat zwar als Basis auch UTF-16 Strings aber auch einen OLE-Widestring muss in den .NET-String gewandelt werden damit er gemanaged ist.

Zurück zum Thema:

Ergänz mal deinen Dll-Import um die Charset-Angabe (http://msdn.microsoft.com/en-us/libr...e.charset.aspx)

Gargamel 3. Apr 2013 12:24

AW: String-Übergabe von Delphi-DLL an C#
 
@DeddyH:

Der Code produziert leider einen Absturz.


Ich habe jetzt einfach mal den lokalen Delphi-String zu einem globalen gemacht. Damit bleibt die Variable erhalten und es wird durch die Funktion ein Zeiger geliefert, der auf den korrekten Speicherbereich verweist.

Meine eigentliche Frage zielte ja darauf ab, warum ich den Zeiger nochmal löschen soll. Hat sich ja damit erledigt.

Delphi-Quellcode:
function getString(text:PAnsiChar):PAnsiChar; cdecl; exports getString;

var finalText:string;

function getString(text:PAnsiChar):PAnsiChar;
var tempText:string;
Begin
  tempText:=WideString(text);
  finalText:=tempText + ' und etwas Delphi-Text';
  result:=PAnsiChar(AnsiString(finalText));
End;

DeddyH 3. Apr 2013 13:10

AW: String-Übergabe von Delphi-DLL an C#
 
Zitat:

Zitat von Gargamel (Beitrag 1209820)
@DeddyH:

Der Code produziert leider einen Absturz.

Bei mir (Delphi XE) nicht.

DLL:
Delphi-Quellcode:
library MyDLL;

uses
  SysUtils,
  Windows;

const
  ERROR_NO_ERROR = 0;
  ERROR_BUFFER_TOO_SMALL = 1;

function delphi_getString(Buffer: PAnsiChar; var BufSize: DWORD): DWORD; cdecl;
var
  Test: AnsiString;
begin
  Test := 'Hallo Welt';
  if BufSize < Length(Test) then
    Result := ERROR_BUFFER_TOO_SMALL
  else
    begin
      StrPCopy(Buffer, Test);
      Result := ERROR_NO_ERROR;
    end;
  BufSize := Length(Test);
end;

exports
  delphi_getString;

begin
end.
Und das Testprojekt:
Delphi-Quellcode:
function delphi_getString(Buffer: PAnsiChar; var BufSize: DWORD): DWORD; cdecl;
  external 'MyDLL.dll' name 'delphi_getString';

procedure TForm1.Button1Click(Sender: TObject);
var
  s: Ansistring;
  BufSize: DWORD;
begin
  BufSize := 0;
  delphi_getString(nil, BufSize);
  SetLength(s, BufSize);
  delphi_getString(PAnsiChar(s), BufSize);
  ShowMessage(s);
end;
Wobei die Konstanten eigentlich in eine eigene Unit gehören, die von DLL und Testprojekt eingebunden wird, aber das habe ich mir hier gespart.

Der schöne Günther 3. Apr 2013 13:15

AW: String-Übergabe von Delphi-DLL an C#
 
Der Absturz liegt daran, dass DeddyH die
Delphi-Quellcode:
cdecl
-Aufrufkonvention verwendet hat, C# aber standardmäßig
Delphi-Quellcode:
stdcall
verwendet.

Das hättest du entweder auf
Delphi-Quellcode:
stdcall
abändern oder bei deinem DllImport-Teil bei C# anpassen müssen:
Code:
[dllimport("mylib.dll",CallingConvention=CallingConvention.Cdecl)]


Alle Zeitangaben in WEZ +1. Es ist jetzt 00:14 Uhr.
Seite 1 von 2  1 2      

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