FreePascal 64 位 DLL 和调用 C# 应用程序

发布于 2024-11-15 09:10:16 字数 553 浏览 2 评论 0原文

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

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

任何人都可以看到它有什么问题吗?如果不喜欢创建相同的简单场景来尝试让它工作,我已经束手无策了!

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!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

2024-11-22 09:10:16

问题在于 C# 编组器将临时内存块作为 aName 传递到函数中。当函数返回时,该内存将被销毁。但您还要求 C# 编组器将同一内存块编组到 C# 字符串中。

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

  1. 在 C# 端使用 StringBuilder 为字符串预分配内存。这需要您以某种方式掌握所需的大小。这是互操作字符串的最常见方法。
  2. 将字符串作为 COM BSTR 返回,C# 编组器知道如何编组和处置 BSTR,并且可以访问 COM 分配器来执行此操作。我不知道如何在 FreePascal 中使用 BSTR,但在 Delphi 中您只需使用 WideString。您还需要告诉 C# 编组器您正在返回一个 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
);

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
);
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文