FreePascal 64 bit DLL and calling C# Application
Asked Answered
M

1

7

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!

Masturbation answered 13/6, 2011 at 19:24 Comment(1)
Have a look at JVCL and the JclDotNet unit.Mozzarella
S
11

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
);
Seavey answered 13/6, 2011 at 19:31 Comment(4)
@Masturbation That's what you'd do in Delphi (BSTR == WideString) but I don't know if it works the same in FP. If you can't do it that way in FP then it's probably easy enough to call SysAllocString, but you'd need to get UTF-16 text. Have a look at this answer to see how: #5309084Seavey
I just changed PChar to Widestring and built the DLL fine however the C# app still doesnt like itMasturbation
On the C# side you need to use MarshalAs(UnmanagedType.BStr).Seavey
Actually, looking at a couple of blog post seems to suggest that WideString is well supported in FP.Seavey

© 2022 - 2024 — McMap. All rights reserved.