I am trying to compile a 64bit dll for use with a 64bit C# application. I have a simple class and a simple app to try and test it and it falls over no matter what I try and do. Here is the code:
Delphi
library project1;
{$mode objfpc}{$H+}
uses
Classes;
function Encrypt(aName:PChar):PChar;stdcall;
begin
Result := aName;
end;
exports Encrypt;
begin
end.
C#
[DllImport("project1.dll")]
[return: MarshalAs(UnmanagedType.LPStr)]
public static extern String Encrypt([MarshalAs(UnmanagedType.LPStr)] String aName);
Can anyone see anything wrong with it and if not fancy creating the same simple scenario to try and get this to work, I'm at the end of my tether!
The problem with this is that the C# marshaller passes a temporary block of memory into the function as aName
. This memory is the destroyed when the function returns. But you are also asking the C# marshaller to marshal this same block of memory into a C# string.
It's not good practice to return a null-terminated string from a native DLL function anyway. You have a couple of options:
StringBuilder
on the C# side to pre-allocate the memory for the string. This requires you to get hold of the required size somehow. This is the most common way to interop strings.BSTR
and the C# marshaller knows how to marshall and dispose a BSTR
, and has access to the COM allocator to do so. I have no knowledge about using BSTR
in FreePascal but in Delphi you simply use WideString
. You also need to tell the C# marshaller that you are returning a BSTR
.I personally have a preference for option 2. There is one wrinkle though and that is that different compilers use a different ABI for function return values, as discussed at this question: Why can a WideString not be used as a function return value for interop? The easy way around that is to return the string in a parameter rather than using the function return value.
The code looks like this:
Pascal
procedure Encrypt(Input: WideString; out Output: WideString); stdcall;
begin
Output := Input;
end;
C#
[DllImport("project1.dll")]
public static extern void Encrypt(
[MarshalAs(UnmanagedType.BStr)] string input;
[MarshalAs(UnmanagedType.BStr)] out string output
);
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