【问题标题】:A heap has been corrupted. C# dllimport, delphi PChar return value堆已损坏。 C# dllimport,delphi PChar 返回值
【发布时间】:2015-08-01 16:16:57
【问题描述】:

我已导入 dll。所有其他部分都可以工作,但是导入方法的字符串返回值给出了这个:

***.exe 中 0x7748EA5F (ntdll.dll) 处的未处理异常:0xC0000374:堆已损坏(参数:0x774C4270)。

它仍然返回字符串,但我担心这会在以后导致其他一些难以调试的错误。根据我的测试,感觉它可能是任何东西,这就是造成这种情况的原因。

这是我的导入代码:

[DllImport("kernel32.dll")]
public static extern IntPtr LoadLibrary(string dllToLoad);
[DllImport("kernel32.dll")]
public static extern IntPtr GetProcAddress(IntPtr hModule, string procedureName);

[UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Ansi)]
private delegate String GetStringDelegate(int handle, int index);

private static GetStringDelegate getString { get; set; }

var addressOfGetString = NativeMethods.GetProcAddress(_handle, "GetString");
getString = (GetStringDelegate)Marshal.GetDelegateForFunctionPointer(addressOfGetString, typeof(GetStringDelegate));

用法

getString(Handle, 1);

这可行,但会导致错误。在调试时,只需按“继续”即可对其进行处理并显示结果。结果是正确的。

这是在delphi dll中完成的

function GetString(Hnd,Index : Integer) : PChar; stdcall;
begin
 Result:=TControl(Hnd).Stack.GetString(Index);
end;

对于整数、双精度数、布尔值和 dll 中的所有其他内容,我都有相同类型的代码,没有错误。所以我认为它会造成某种溢出或错误大小的内存分配。

注意:如果我创建控制台应用程序,它只会失败,不会因错误而中断,如果我在没有调试器( ctrl+f5 )的情况下运行控制台,它可以工作,仍然没有错误。当我从表单应用程序调用它时会产生堆错误。

TL;DR;此代码有效,但显示堆错误,而返回整数、布尔值等则完美。

【问题讨论】:

  • PCHAR 在最新版本的 Delphy 中是 Unicode,所以 [UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Ansi)] 应该是 [UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Unicode)]
  • @xanatos 除非,通常情况下,用户仍在 D2007 或更早版本
  • @DavidHeffernan 我写过“最新版本”
  • 如果我把它改成unicode,字符串是一些中文字母。
  • @Katu 这表明该文本确实是 ANSI 并且 CharSet.Ansi 适合此 DLL

标签: c# delphi dllimport


【解决方案1】:

当您将字符串作为 p/invoke 函数的函数返回值返回时,编组器负责释放该内存。它假设内存是在 COM 堆上分配的,例如与CoTaskMemAlloc。您的字符串不符合该要求。

  • 您可以更改 Delphi 代码以通过这种方式分配内存。
  • 您可以返回IntPtr 并使用Marshal.PtrToStringAnsi 手动编组。然后问题仍然存在,是否需要释放内存,如果需要,如何释放。
  • 您可以返回一个 COM BSTR,但这仅适用于 Delphi 作为输出参数而不是函数返回值。见Why can a WideString not be used as a function return value for interop?
  • 您可以要求调用者分配内存并让被调用者填充它。

我无法确定您的代码的全部内容,但如果您返回 PChar(s),其中 s 是一个局部变量,我不会感到惊讶。这意味着您将返回已释放内存的地址。

这里的底线是从被调用者到调用者传递字符串(或者实际上是数组或其他动态结构)比传递简单的值类型要复杂得多。您将需要重新考虑如何做到这一点。

【讨论】:

  • 我明白了。我不是 dll 的作者,但我会指出他的答案。谢谢。
  • 显然是框架 4.5 导致了问题。你的笔记很好,与作者讨论后,注意到它以前在其他.Net应用程序上工作过,所以我不得不采取其他方向。不得不开箱即用。更改为 .net 4 有帮助.. 还是谢谢
  • 不,完全错了。您的代码在所有版本的 .net 上都损坏了,回到版本 1。您刚刚摆脱了它。它只是偶然打破了最新的框架。我不明白如果你不想理解这个问题,为什么你会问这个问题。当我告诉你编组器正在调用CoTaskMemFree时,你不相信我吗?!
  • 谢谢你踢我一点!您将返回类型从string 更改为IntPtr 然后Marshal.PtrToStringAnsi 的返回值起作用的解决方案。
  • 我很高兴你能留下来。很高兴切换到IntPtr 成功了。请随意质疑我的话,持怀疑态度总是明智的。出于同样的原因,问问自己,在这种情况下,您一定已经从骨子里知道切换 .net 框架感觉不对! ;-)
猜你喜欢
  • 1970-01-01
  • 2011-12-03
  • 2011-08-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-11-06
  • 2012-11-08
  • 1970-01-01
相关资源
最近更新 更多