FreePascal 64位DLL并调用C#Application

Jon*_*Jon 6 c# delphi freepascal lazarus

我正在尝试编译一个64位的dll,用于64位C#应用程序.我有一个简单的类和一个简单的应用程序来尝试和测试它,它无论我尝试做什么都会失败.这是代码:

德尔福

library project1;

{$mode objfpc}{$H+}

uses
  Classes;


function Encrypt(aName:PChar):PChar;stdcall;
begin
  Result := aName;
end;


exports Encrypt;

begin
end.
Run Code Online (Sandbox Code Playgroud)

C#

 [DllImport("project1.dll")]
    [return: MarshalAs(UnmanagedType.LPStr)]
    public static extern String Encrypt([MarshalAs(UnmanagedType.LPStr)] String aName);
Run Code Online (Sandbox Code Playgroud)

任何人都可以看到它有什么问题,如果没有花哨创造同样的简单场景试图让这个工作,我就在我的系绳的尽头!

Dav*_*nan 10

这个问题是C#marshaller将一个临时的内存块传递给函数aName.当函数返回时,该内存被销毁.但是你也要求C#marshaller将这个相同的内存块编组成一个C#字符串.

无论如何,从本机DLL函数返回以null结尾的字符串并不是一个好习惯.你有几个选择:

  1. StringBuilder在C#端使用a 为字符串预分配内存.这要求您以某种方式获得所需的大小.这是互操作字符串的最常用方法.
  2. 将字符串作为COM返回,BSTRC#marshaller知道如何编组和处理a BSTR,并且可以访问COM分配器来执行此操作.我不知道BSTR在FreePascal中使用,但在Delphi中你只是使用WideString.你还需要告诉C#marshaller你要回来了BSTR.

我个人偏好选项2.但是有一个皱纹,那就是不同的编译器对函数返回值使用不同的ABI,正如这个问题所讨论的:为什么WideString不能用作interop的函数返回值?简单的方法是在参数中返回字符串而不是使用函数返回值.

代码如下所示:

帕斯卡尔

procedure Encrypt(Input: WideString; out Output: WideString); stdcall;
begin
  Output := Input;
end;
Run Code Online (Sandbox Code Playgroud)

C#

[DllImport("project1.dll")]
public static extern void Encrypt(
    [MarshalAs(UnmanagedType.BStr)] string input;
    [MarshalAs(UnmanagedType.BStr)] out string output
);
Run Code Online (Sandbox Code Playgroud)