1

这是本机(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 吗?

谁能给我一些关于如何做到这一点的反馈/建议?

4

2 回答 2

3

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

这里有更多关于 StackOverflow 如何从 DLL 返回字符序列数据的问题。要么使用与 Windows API 执行业务的方式兼容的字符串类型(COM 字符串),要么将预分配的缓冲区传递给您的函数并用数据填充该缓冲区。在后一种情况下,您可以使用与每个类似 API 函数相同的方法来使用您的函数。

于 2009-11-10T05:14:41.613 回答
2

对于内存泄漏检测,您可以使用 Delphi 的开源FastMM4 内存管理器

“FastMM 是 Embarcadero Delphi Win32 应用程序的快速替换内存管理器,可在多线程应用程序中很好地扩展,不易出现内存碎片,并且无需使用外部 .DLL 文件即可支持共享内存。”

它非常适合 dll 之间的速度、泄漏检查和内存共享。

FastMM4 选项界面也非常有用,它有助于配置 FastMM4。

于 2009-11-10T12:43:26.797 回答