堆已损坏。 C# dllimport,delphi PChar return 值
A heap has been corrupted. C# dllimport, delphi PChar return value
我导入了dll。所有其他部分都有效,但是导入方法的字符串 return 值给出了这个:
Unhandled exception at 0x7748EA5F (ntdll.dll) in ***.exe: 0xC0000374: A heap has been corrupted (parameters: 0x774C4270).
它仍然是 return 字符串,但我担心这会导致以后出现其他一些难以调试的错误。根据我的测试,感觉它可以是任何东西,这就是造成这种情况的原因。
这是我的导入代码:
[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);
这有效,但会导致错误。调试时,只需按 "continue" 即可让它处理并显示结果。结果正确。
这是在 delphi dll
中的做法
function GetString(Hnd,Index : Integer) : PChar; stdcall;
begin
Result:=TControl(Hnd).Stack.GetString(Index);
end;
我有相同类型的整数、双精度、布尔值和 dll 中的所有其他代码,没有错误。所以我认为它会造成某种溢出或错误的内存分配大小。
注意:如果我创建控制台应用程序,它只会失败,不会因错误而中断,如果我 运行 控制台没有调试器 ( ctrl+f5 ),它可以工作,仍然没有错误。当我从表单应用程序调用它时生成堆错误。
TL;DR;此代码有效,但显示堆错误,而 returning ints、bools 等完美运行。
当您 return 一个字符串作为函数 return 一个 p/invoke 函数的值时,编组器负责释放该内存。它假定内存是在 COM 堆上分配的,例如CoTaskMemAlloc
。您的字符串不符合该要求。
- 您可以更改 Delphi 代码以那样分配内存。
- 您可以 return
IntPtr
并使用 Marshal.PtrToStringAnsi
手动编组。问题仍然是是否需要释放内存,如果需要如何释放。
- 您可以 return 一个 COM BSTR,但这只适用于 Delphi 作为输出参数而不是函数 return 值。参见 Why can a WideString not be used as a function return value for interop?
- 您可以要求调用者分配内存并让被调用者填充它。
我不能肯定地看到你的代码,但如果你 returning PChar(s)
其中 s
是一个局部变量,我不会感到惊讶。这意味着您将 returning 释放内存的地址。
这里的底线是,从被调用方到调用方传递字符串(或者实际上是数组或其他动态结构)比传递简单的值类型要复杂得多。您将需要重新考虑如何执行此操作。
我导入了dll。所有其他部分都有效,但是导入方法的字符串 return 值给出了这个:
Unhandled exception at 0x7748EA5F (ntdll.dll) in ***.exe: 0xC0000374: A heap has been corrupted (parameters: 0x774C4270).
它仍然是 return 字符串,但我担心这会导致以后出现其他一些难以调试的错误。根据我的测试,感觉它可以是任何东西,这就是造成这种情况的原因。
这是我的导入代码:
[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);
这有效,但会导致错误。调试时,只需按 "continue" 即可让它处理并显示结果。结果正确。
这是在 delphi dll
中的做法function GetString(Hnd,Index : Integer) : PChar; stdcall;
begin
Result:=TControl(Hnd).Stack.GetString(Index);
end;
我有相同类型的整数、双精度、布尔值和 dll 中的所有其他代码,没有错误。所以我认为它会造成某种溢出或错误的内存分配大小。
注意:如果我创建控制台应用程序,它只会失败,不会因错误而中断,如果我 运行 控制台没有调试器 ( ctrl+f5 ),它可以工作,仍然没有错误。当我从表单应用程序调用它时生成堆错误。
TL;DR;此代码有效,但显示堆错误,而 returning ints、bools 等完美运行。
当您 return 一个字符串作为函数 return 一个 p/invoke 函数的值时,编组器负责释放该内存。它假定内存是在 COM 堆上分配的,例如CoTaskMemAlloc
。您的字符串不符合该要求。
- 您可以更改 Delphi 代码以那样分配内存。
- 您可以 return
IntPtr
并使用Marshal.PtrToStringAnsi
手动编组。问题仍然是是否需要释放内存,如果需要如何释放。 - 您可以 return 一个 COM BSTR,但这只适用于 Delphi 作为输出参数而不是函数 return 值。参见 Why can a WideString not be used as a function return value for interop?
- 您可以要求调用者分配内存并让被调用者填充它。
我不能肯定地看到你的代码,但如果你 returning PChar(s)
其中 s
是一个局部变量,我不会感到惊讶。这意味着您将 returning 释放内存的地址。
这里的底线是,从被调用方到调用方传递字符串(或者实际上是数组或其他动态结构)比传递简单的值类型要复杂得多。您将需要重新考虑如何执行此操作。