返回null终止字符串

本文关键字:字符串 终止 null 返回 | 更新日期: 2023-09-27 18:02:32

我正在尝试与实现几个函数的Dll接口,其中一个函数需要一个null终止字符串和一个int,并返回一个null终止字符串。我尝试过这样与方法交互:

[DllImport(dll_loc)]
[return : MarshalAs(UnmanagedType.LPStr)]
public static extern StringBuilder GetErrorMessage([MarshalAs(UnmanagedType.LPStr)]
                                                       StringBuilder message, 
                                                       int error_code);

然后尝试像这样调用该方法:

StringBuilder message = new StringBuilder(1000);
StringBuilder out2 = new StringBuilder(1000);
out2 = GetErrorMessage(message, res0);

然而,当我尝试这样做时,抛出一个AccessViolationException告诉我我正在尝试访问受保护的内存。

我成功地声明了一个不同的方法:

[DllImport(dll_loc)]
public static extern int GetVersion([MarshalAs(UnmanagedType.LPStr)]
                                        StringBuilder version);

并以相同的方式调用它,但该方法对当前函数调用不起作用。

我还尝试返回一个IntPtr,因为文档技术上说该方法返回一个指向字符串缓冲区第一个字符的指针,但无济于事。

有没有人知道这里可能出了什么问题?这两种方法之间的不同可能导致dll尝试访问它不应该访问的内存。或者,您建议如何调试这个问题?

返回null终止字符串

返回字符串的C函数是内存管理问题。一个C字符串需要一个数组,并且该数组的内存需要在字符串被消耗后释放。这使得这样的函数很难在C程序中使用,几乎不可能与pinvoke一起使用。这也是一个典型的C错误,返回一个指向堆栈上字符串的指针。

pinvoke编组程序将根据需要尝试释放返回的字符串,以避免内存泄漏。它将使用CoTaskMemFree()。这通常不会有好的结果,很少有C代码实际使用CoTaskMemAlloc为数组分配内存。在XP上,这将导致无声内存泄漏。Vista和Win7有更严格的堆管理器,如果附加了非托管调试器,它们将调用调试中断。然后用AccessViolation攻击程序。

您可以通过声明返回类型为IntPtr并自己封送字符串来避免自动封送行为。通常使用Marshal.PtrToStringAnsi()。但你仍然面临着释放记忆的任务。你不能,你没有CRT堆的句柄你不能调用free()

返回字符串的C函数应该声明带有传递字符串缓冲区的实参。还有一个参数表示缓冲区有多大。这样的:
 int GetErrorMessage(int errorCode, char* buffer, size_t bufferSize);

现在很简单了,调用者可以为缓冲区分配内存并释放它。C函数只是将字符串复制到缓冲区中。返回值可用于说明复制了多少字符,或指示需要更大的缓冲区。不要忽略bufferSize参数,缓冲区溢出是致命的,它会调用可怕的FatalExecutionEngineError异常,这个异常和AV一样不可调试,因为直到很久以后才检测到GC堆损坏。在c#端使用StringBuilder,适当地初始化为非零容量。bufferSize的值

将所有StringBuilder变量替换为System.IntPtr变量,仅将 message变量配置为System.Runtime.InteropServices.Marshal.AllocHGlobal变量

然后用PtrToStringAutomessageout2转换为字符串

则调用System.Runtime.InteropServices.Marshal.FreeHGlobal

在处理char*wchar_t*时,我总是这样做。