2013-04-23 16 views
6

Mam aplikacji C#, który wywołuje natywną dll Delphi za pomocą następującego kodu:Zwracanie ciąg z DLL Delphi C# rozmówcy w 64 bit

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; 

co działa poprawnie w aplikacji 32-bitowej. Ale kiedy skompiluję zarówno C# exe, jak i Delphi dll na 64-bitowy, otrzymuję dziwny problem. Po wywołaniu funkcji GetString w debuggerze Delphi zauważam, że wyjątek został zgłoszony gdzieś w kodzie .NET, a następujący komunikat pojawia się w oknie Wyjście debugowania: "Wykryto błąd krytyczny c0000374". Google twierdzi, że ten błąd jest związany z uszkodzeniem sterty. Próbowałem używać modyfikatorów parametrów ref/var zamiast out/out. Wciąż nie ma szczęścia. Dlaczego pojawia się ten błąd? Czy powinienem użyć innej konwencji wywoływania dla 64 bitów?

BTW. Poniższy połączenie działa prawidłowo:

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; 

działa dobrze. Ale muszę zwrócić ciąg jako parametr wyjściowy.

Odpowiedz

17

Nie można przekazać ciągu znaków z macierzystego zarządzanego w ten sposób. Twój kod jest nieprawidłowy także w wersji 32-bitowej, po prostu zdarza Ci się uciec. Druga wersja kodu również jest błędna. Wydaje się, że działa.

Musisz albo:

  1. Przeznaczyć z udostępnionego sterty tak, że kod może cofnąć przydział udało się, że sterty. Wspólną stertą dla p/invoke jest stos COM.
  2. Przydziel pamięć po zarządzanej stronie i skopiuj zawartość do tego bufora po stronie natywnej.

Opcja 2 jest zawsze preferowana.Wygląda to tak:

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

Na rodzimym strony trzeba

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

Następnie nazwać to tak:

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

Jeśli chcesz wypróbować opcję 1, to wygląda to po zarządzanej stronie:

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

i tak rodzimy:

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; 

Kiedy udało kod kopiuje zawartość str do wartości ciągu, to wtedy nazywa CoTaskMemFree na wskaźniku, który powrócił.

I to jest trywialnie proste zadzwonić:

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

Dziękuję bardzo! Oszczędziłeś mi wiele godzin walki z tym problemem. – Max

+0

'PChar'? Gra losowa była to 'PAnsiChar' lub' PWideChar'? (tak, możemy wywnioskować, że jest to 64-bitowy XE2, ale nie możemy zagwarantować, że ten kod nie zostanie skompilowany za pomocą 64-bitowego FPC lub skopiowanego z kopią do projektu 32-bitowego itd.) –

+0

@ Arioch'The Następnie użyj 'PWideChar' Jeśli wolisz. –

Powiązane problemy