I have a C# application which calls native Delphi dll using the following code:
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;
which works fine in a 32 bit application. But when I compile both C# exe and Delphi dll for 64 bit I get a strange problem. After a call to GetString in Delphi debugger I can see that an exception is raised somewhere in the .NET code and the following string appears in the Debugger Output window: "Critical error detected c0000374". Google says that this error is related to heap corruption. I tried using ref/var parameters modifiers instead of out/out. Still no luck. Why do I get this error? Should I use a different calling convention for 64 bit?
BTW. The following combination works fine:
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;
works fine. But I do need to return a string as an out parameter.
You cannot pass a string from native managed that way. Your code is wrong in 32 bit also, you just happen to get away with it. The second version of the code is also wrong. It only appears to work.
You need to either:
Option 2 is always preferable. It looks like this:
[DllImport("NativeDLL.dll", CharSet = CharSet.Unicode)]
public static extern int GetString(StringBuilder str, int len);
On the native side you would 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 this like so:
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 the managed code copies the contents of str
to the string value, it then calls CoTaskMemFree
on the pointer that you returned.
And this is trivially easy to call:
string str;
int retval = GetString(out str);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With