2012-12-18 39 views
2

时,我有一个Windows窗体应用程序在.NET 4.0下运行。此应用程序导入一个DLL,其可用于:应用程序崩溃使用字符串函数

  • 32位
  • 64位

这里是我的代码片段:

[DllImport("my64Bit.dll"), EntryPoint="GetLastErrorText"] 
private static extern string GetLastErrorText(); 

// Do some stuff... 

string message = GetLastErrorText(); 

当调用这个函数(编译为64位)应用程序崩溃。我什至不能在Visual Studio 2012中看到任何调试消息。与32位-DLL(编译为x86)相同的代码工作正常。原型为:

LPCSTR APIENTRY GetLastErrorText()

不幸的是我没有对DLL任何进一步的信息,因为它是一个第三方产品。

+0

http://stackoverflow.com/questions/3598226/how-to-map-win32-types-to-c-sharp-types-when-using-p-invoke – NickD

+0

LPCSTR是char *。尝试将CharSet = CharSet.Ansi添加到DllImport。 –

+0

@AlexFarber据我所知,默认的CharSet是Ansi。无论如何,该应用程序仍然崩溃。如前所述,32位版本运行良好。 –

回答

4

函数签名是相当麻烦的。您的代码是否会崩溃取决于您运行的操作系统。在XP上没有任何反应,在Vista和之后的版本上会出现AccessViolation异常。

目前的问题是,C函数返回一个字符串需要通过指针返回到存储字符串缓冲区通常这样做。该缓冲区需要从堆中分配,调用者需要在使用该字符串后释放该缓冲区。 pinvoke编组器实现该合约,它在将其转换为System.String后,在返回的字符串指针上调用CoTaskMemFree()。

即总是原来很差,C函数几乎从未使用CoTaskMemAlloc()来分配缓冲器。 XP堆管理器非常宽容,它只是忽略坏指针。不是后来的Windows版本,他们故意生成一个异常。一个强大的推动者“Vista吸吮”标签顺便说一句,程序员需要一段时间才能修复它们的指针错误。如果您启用了非托管调试,那么您将从堆管理器获取诊断,该警告警告指针无效。非常好的功能,但是当您调试托管代码时,不受管理的调试总是被禁用。

您可以尝试通过声明返回值的IntPtr释放串停止的PInvoke编组。然后你必须使用Marshal.PtrToStringAnsi()或它的一个朋友来编组字符串。

你还是不得不释放这个串的问题。没有办法可靠地做到这一点,你不能调用正确的释放器。唯一的希望是C函数实际返回一个指向字符串的指针,一个指针存储在数据段中,并且应该释放而不是。这可能适用于返回错误字符串的函数,前提是它没有实现像本地化那样的任何幻想。 const char *返回类型是令人鼓舞的。

您需要进行测试以确保有从不释放字符串缓冲区没有内存泄漏。容易做到,循环中调用这个函数10亿次。如果您没有将IntPtr.Zero作为返回值,并且程序不会因内存不足而异常,那么您就很好。对于64位调试,您需要密切关注测试程序的内存消耗。

2

找到它了。本机函数返回LPCSTR,即C#函数不能返回字符串。相反的IntPtr必须返回这样的:

[DllImport("my64Bit.dll"), EntryPoint="GetLastErrorText"] 
private static extern IntPtr GetLastErrorText(); 

// Do some stuff... 

IntPtr ptr = GetLastErrorText(); 
string s = Marshal.PtrToStringAnsi(ptr); 
+0

有趣的是,为什么标准的字符串编组在这种情况下不起作用。无论如何,使用IntPtr和Marshal对于了解C/C++的程序员来说总是更好。 –

相关问题