自由帕斯卡 64 位 DLL 和调用 C# 应用程序

7
我将尝试为一款64位C#应用程序编译64位dll。我有一个简单的类和一个简单的应用程序来测试,但无论我做什么都会失败。以下是代码:

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);

有人能看出什么问题吗?如果没有,那么想要创建相同的简单场景来尝试使其工作,我已经无计可施了!


请查看JVCL和JclDotNet单元。 - Adam Craig Johnston
1个回答

11
这样做的问题在于C# marshaller将一个临时的内存块作为`aName`传递给函数。当函数返回时,这个内存将被销毁。但你还要求C# marshaller将同一块内存转换成一个C#字符串。

从本质上说,从本地DLL函数返回空终止字符串并不是良好的实践。你有几个选项:

  1. 在C#端使用StringBuilder预先分配字符串所需的内存。这需要你以某种方式获取所需的大小。这是交互字符串最常见的方法。
  2. 将字符串作为COM BSTR返回,C# marshaller知道如何调用和释放BSTR,并可以访问COM分配器来执行此操作。我不了解在FreePascal中使用BSTR的情况,但在Delphi中,你只需要使用WideString即可。你还需要告诉C# marshaller你正在返回一个BSTR

我个人更喜欢选项2。但有一个问题,不同的编译器对函数返回值使用不同的ABI,如这个问题所讨论的那样:为什么不能将WideString用作交互的函数返回值?简单的方法是将字符串作为参数返回,而不是使用函数返回值。

代码如下:

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
);

@Jon 这是在Delphi中的做法(BSTR == WideString),但我不知道在FP中是否也适用。如果在FP中不能这样做,那么调用SysAllocString可能就足够简单了,但你需要获取UTF-16文本。看一下这个答案,就可以知道如何做到:https://dev59.com/g2435IYBdhLWcg3woRjG - David Heffernan
我刚刚将PChar更改为WideString并成功构建了DLL,但C#应用程序仍然不喜欢它。 - Jon
在C#端,您需要使用“MarshalAs(UnmanagedType.BStr)”语句。 - David Heffernan
实际上,查看一些博客文章似乎表明,在 FP 中广泛支持 WideString。 - David Heffernan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接