1

我有一个第三方 DLL 用 Delphi“a.dll”(无源代码)编写。

这个 DLL 有一个带有这个签名的方法。

function GetAny(pFileName: String): String;

我无法从 c# 进行互操作调用,因为“字符串类型”在 delphi 中具有私有访问权限。

因此,在 delphi 中构建另一个 DLL 来包装该调用。

德尔福。

function GetAny(pFileName: String): String; external 'a.dll'

function GetWrapper(url : PChar) : PChar; stdcall;
begin
    Result := PChar(GetAny(url)); // I need avoid this String allocation, is throwing a exception.
end;

C#。

[DllImport("wrapper.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi)]
public static extern IntPtr GetWrapper(String url);

在“GetWrapper”中,我调用了外部“GetAny”,结果是好的(在delphi中我可以调试),但是在我把这个结果返回到c#端之前,它抛出了一个异常。

IntPtr test = GetWrapper("a");
String result = Marshal.PtrToStringAnsi(test);
4

2 回答 2

3

您的包装 DLL 也不能调用 GetAny,因为字符串是托管的 Delphi 类型,不能跨模块边界传递。

问题是 GetAny 的返回值在一个模块中分配并在不同的模块中释放。它在实现 GetAny 的 DLL 中分配,并在调用 GetAny 的 DLL 中释放。由于这两个 DLL 使用不同的内存管理器,因此您最终会尝试释放分配在不同堆上的内存。

如果可以说服实现 GetAny 的 DLL 共享内存管理器,那么您可以轻松解决该问题。

我确实质疑你提出的事实。就目前而言,除非 DLL 设计为与 ShareMem 一起使用,否则永远无法安全地调用该函数。

如果你准备泄漏内存,你可以试试这个:

德尔福

function GetAny(pFileName: string): PChar; external 'a.dll'

procedure GetWrapper(url: PChar; out value: WideString); stdcall;
var
  P: PChar;
begin
    P := GetAny(url);
    if Assigned(P) then
      Value := P
    else
      Value := '';
end;

C#

[DllImport("wrapper.dll"]
public static extern void GetWrapper(
    string url,
    [MarshalAs(UnmanagedType.BStr)]
    out string value
);
于 2013-08-25T07:38:04.797 回答
0

我已经下载了你的代码...

解决方案可能是这样的:

  • 使用带有 2 个 PChar 类型参数的“cdecl”声明在 Delphi 中创建一个包装程序

    • 第一个是IN参数
    • 第二个是OUT参数

原始德尔福功能:

function GetAny(pFileName: String): String; external 'a.dll';

Delphi – 带有包装函数的 DLL:

 procedure GetWrapper (url: PChar; var urlNew: PChar) cdecl;
 var str: string;
 begin
      urlStr = string(url);
      urNewStr := GetAny(urlStr);
      urlNew := PChar(urNewStr);
 end;

 exports
    GetWrapper;
 begin
 end.
  • 在 Visual Studio 中:

制作项目 x32 位(不是示例中的 x64)

  • 将 DLL 导入为 Cdecl

[DllImport("wrapper.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]

  • 编组

public static extern void GetWrapper ([MarshalAs(UnmanagedType.LPStr)]string url, [MarshalAs(UnmanagedType.LPStr)] out string urlNew);

  • 在 C# 中调用:

    字符串文件名;// = @"wertwertwertwertwer";

    GetWrapper("2.jpg", out fileName);

    Console.WriteLine(文件名);

在我的环境中它有效。(德尔福 5 和 VS2012)。

希望它也对你有用。

于 2013-08-26T16:32:57.443 回答