2013-04-23 3 views
6

ich eine C# Anwendung, die ruft nativen Delphi-DLL mit dem folgenden Code:in 64 Bit eine Zeichenfolge von delphi dll C# Anrufer zurückkehrend

C#

[DllImport("NativeDLL.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)] 
public static extern int GetString(string out str); 

Delphi

function GetString(out a: PChar): Integer; stdcall; 
begin 
    a := PChar('abc'); 
    Result := 1; 
end; 

was in einer 32-Bit-Anwendung funktioniert. Aber wenn ich sowohl C# exe und Delphi dll für 64 Bit kompiliere, bekomme ich ein seltsames Problem. Nach einem Aufruf von GetString in Delphi Debugger kann ich sehen, dass eine Ausnahme irgendwo im .NET-Code ausgelöst wird und die folgende Zeichenfolge im Debugger-Ausgabefenster angezeigt wird: "Kritischer Fehler erkannt c0000374". Google sagt, dass dieser Fehler auf Heap-Beschädigung zurückzuführen ist. Ich habe versucht, Ref/Var Parameter Modifikatoren anstelle von out/out. Immer noch kein Glück. Warum bekomme ich diesen Fehler? Sollte ich eine andere Aufrufkonvention für 64 Bit verwenden?

BTW. Die folgende Kombination funktioniert gut:

C#

[DllImport("NativeDLL.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)] 
public static extern string GetString(string a); 

Delphi

function GetString(a: PChar): PChar; stdcall; 
var 
    inp: string; 
begin 
    inp := a; 
    Result := PChar('test ' + inp); 
end; 

funktioniert gut. Aber ich muss eine Zeichenfolge als out-Parameter zurückgeben.

Antwort

17

Sie können auf diese Weise keine Zeichenfolge von nativ verwaltet übergeben. Ihr Code ist auch in 32 Bit falsch, Sie kommen einfach damit durch. Die zweite Version des Codes ist ebenfalls falsch. Es scheint nur zu funktionieren.

Sie müssen entweder:

  1. von einem gemeinsam genutzten Heap zuweisen, so dass, dass die verwalteten Code, dass Haufen ausplanen off kann. Der freigegebene Heap für p/invoke ist der COM-Heap.
  2. Ordnen Sie den Speicher auf der verwalteten Seite zu und kopieren Sie den Inhalt auf der systemeigenen Seite in diesen Puffer.

Option 2 ist immer vorzuziehen.Es sieht wie folgt aus:

[DllImport("NativeDLL.dll", CharSet = CharSet.Unicode)] 
public static extern int GetString(StringBuilder str, int len); 

Auf der nativen Seite Sie

function GetString(str: PChar; len: Integer): Integer; stdcall; 
begin 
    StrLCopy(str, 'abc', len); 
    Result := 1; // real code would have real error handling 
end; 

Dann nennen dies wie so haben würde:

StringBuilder str = new StringBuilder(256); 
int retval = GetString(str, str.Capacity); 

Wenn Sie versuchen wollen 1 Option, es sieht aus wie dies auf der verwalteten Seite:

[DllImport("NativeDLL.dll", CharSet = CharSet.Unicode)] 
public static extern int GetString(out string str); 

und ähnliche nativer:

function GetString(out str: PChar): Integer; stdcall; 
begin 
    str = CoTaskMemAlloc(SizeOf(Char)*(Length('abc')+1)); 
    StrCopy(str, 'abc'); 
    Result := 1; // real code would have real error handling 
end; 

Wenn die verwalteten Code kopiert den Inhalt von str auf den String-Wert, ruft sie dann CoTaskMemFree auf den Zeiger, der zurückgegeben werden.

Und das ist einfach leicht zu nennen:

string str; 
int retval = GetString(out str); 
+0

Thank you very much! Du hast mir viele Stunden des Kampfes mit diesem Problem erspart. – Max

+0

'PChar'? Glücksspiel war es "PAnsiChar" oder "PWideChar"? (Ja, wir können daraus schließen, dass es 64-Bit-XE2 ist, aber wir können nicht sicherstellen, dass dieser Code nicht mit 64-Bit-FPC kompiliert oder in ein 32-Bit-Projekt kopiert wird, usw.) –

+0

@ Arioch'Dann verwenden Sie 'PWideChar' wenn Sie bevorzugen. –

Verwandte Themen