Returning a string from delphi dll to calling C # in 64-bit

I have a C # application that calls the native Delphi DLL using the following code:

WITH#

[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; 

which works great in a 32-bit application. But when I compile both C # exe and Delphi dll for 64 bits, I get a strange problem. After calling GetString in the Delphi debugger, I see that an exception occurs somewhere in the .NET code, and the following line appears in the debugger output window: "Critical error detected c0000374" . Google says this error is related to heap corruption. I tried using ref / var parameter modifiers instead of out / out. Not lucky yet. Why am I getting this error? Should I use a different calling convention for 64-bit?

BTW. The following combination works fine:

WITH#

 [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; 

works great. But I need to return the string as an out parameter.

+6
source share
1 answer

You cannot pass a string from native managed this way. Your code is incorrect in the 32-bit version, you just can handle it. The second version of the code is also incorrect. It only works.

You need to either:

  • Allocate from the shared heap so that managed code can free this heap. The general heap for p / invoke is the COM heap.
  • Allocate memory on the managed side and copy the contents to this buffer from the inside.

Option 2 is always preferable. It looks like this:

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

From your home side you will have

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

Then call it this:

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

If you want to try option 1, it looks like this on the managed side:

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

and like this native:

 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; 

When managed code copies the contents of str to a string value, it calls the CoTaskMemFree pointer that you returned.

And it's trivially easy to call:

 string str; int retval = GetString(out str); 
+17
source

Source: https://habr.com/ru/post/943438/


All Articles