Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

FreePascal 64 bit DLL and calling C# Application

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!

like image 963
Jon Avatar asked Jun 13 '11 19:06

Jon


1 Answers

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:

  1. Use a 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.
  2. Return the string as a COM 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
);
like image 58
David Heffernan Avatar answered Nov 10 '22 04:11

David Heffernan