我该如何从C#调用这个原生dll函数?

时间:2009-11-09 23:56:36

标签: c# .net delphi pinvoke delphi-7

这是本机(Delphi 7)功能:

function Foo(const PAnsiChar input) : PAnsiChar; stdcall; export;
var
  s : string;
begin
    s := SomeInternalMethod(input);
    Result := PAnsiChar(s);
end;

我需要从C#调用它,但是在编译时不知道dll的名称 - 所以我必须使用LoadLibrary来实现它。

这就是我的C#代码到目前为止的样子:

[DllImport("kernel32.dll")]
public extern static IntPtr LoadLibrary(String lpFileName);

[DllImport("kernel32.dll")]
public extern static IntPtr GetProcAddress(IntPtr handle, string funcName);

[UnmanagedFunctionPointer(CallingConvention.StdCall)]
private delegate string FooFunction(string input);

...

IntPtr dllHandle = LoadLibrary(dllName);
IntPtr fooProcAddr = GetProcAddress(dllHandle, "Foo");

FooFunction foo = (FooFunction)Marshal.GetDelegateForFunctionPointer(
    fooProcAddr, typeof(FooFuncion)
);

string output = foo(myInputString);

现在,这确实有效 - 至少,delphi代码正确接收字符串,C#代码接收输出字符串。

但是,当从C#代码中调用delphi代码时,我注意到了一些奇怪的问题 - 调试器在不应该跳过行时跳过行。

而且我担心我会泄漏记忆 - 有人清理那些PChars吗?

任何人都可以就如何做到这一点给我一些反馈/意见吗?

2 个答案:

答案 0 :(得分:3)

你可以做的唯一合理的事情是废弃这个功能并重写它。这是不可能的。 sFoo()函数的本地字符串变量,因此当您离开Foo()时,将释放字符串占用的内存。您返回的指针指向无效的内存位置,该位置偶然包含字符串数据。如果使用内存管理器在释放指针时清除内存,则它甚至不再包含数据。如果重用内存,它将包含其他内容,如果释放包含该内存块的块,您将获得AV。

StackOverflow上有更多关于如何从DLL返回字符序列数据的问题。使用与Windows API开展业务的方式兼容的字符串类型,COM字符串,或将预分配的缓冲区传递给您的函数并使用数据填充它。在后一种情况下,您可以像使用每个类似的API函数一样使用函数。

答案 1 :(得分:2)

对于内存泄漏检测,您可以使用开源FastMM4 memory manager for Delphi

  

“FastMM快速闪电   替换内存管理器   Embarcadero Delphi Win32应用程序   在多线程中可以很好地扩展   应用程序,不容易记忆   碎片化,并支持共享   内存不使用外部   .DLL文件。“

这对于dll之间的速度,泄漏检查和内存共享非常有用。

同样非常有用的是FastMM4 Options Interface,它有助于配置FastMM4。