导入的本机函数在 .NET 4.0 中不起作用
我正在将项目从 .net 3.5 迁移到 .net 4.0 并遇到以下问题。 有 2 个 DllImport 语句:
<DllImport("hid64.dll")> _
Public Sub GenerateHardwareID( _
<MarshalAs(UnmanagedType.LPArray, SizeParamIndex:=1)> ByVal Buffer As Byte(), _
ByVal BufferLength As Int32)
End Sub
<DllImport("hid64.dll")> _
Public Function BufferToString( _
<MarshalAs(UnmanagedType.LPArray, SizeParamIndex:=1)> ByVal Buffer As Byte(), _
ByVal BufferLength As Int32) As <MarshalAs(UnmanagedType.LPWStr)> String
End Function
对于 .NET 3.5,这两个函数都可以很好地工作。但对于 .NET 4.0,BufferToString 函数的调用会中断程序的执行,而不会引发任何异常。
我尝试了 DllImport 属性的 CallingConvention、CharSet 等字段: http://msdn.microsoft.com/en-us /library/system.runtime.interopservices.dllimportattribute.aspx 没有成功。
此变体:
<DllImport("hid64.dll", CharSet:=CharSet.Auto, PreserveSig:=False, SetLastError:=True)> _
Public Function BufferToString( _
<MarshalAs(UnmanagedType.LPArray)> ByVal Buffer As Byte(), _
ByVal BufferLength As Int32) As <MarshalAs(UnmanagedType.LPWStr)> String
End Function
不会中断程序的执行,但函数返回“Nothing”。
I am migrating project from .net 3.5 to .net 4.0 and faced the following issue.
There are 2 DllImport statements:
<DllImport("hid64.dll")> _
Public Sub GenerateHardwareID( _
<MarshalAs(UnmanagedType.LPArray, SizeParamIndex:=1)> ByVal Buffer As Byte(), _
ByVal BufferLength As Int32)
End Sub
<DllImport("hid64.dll")> _
Public Function BufferToString( _
<MarshalAs(UnmanagedType.LPArray, SizeParamIndex:=1)> ByVal Buffer As Byte(), _
ByVal BufferLength As Int32) As <MarshalAs(UnmanagedType.LPWStr)> String
End Function
For the .NET 3.5 both functions work well. But for the .NET 4.0 the call of the BufferToString function breaks execution of the programm without raising any exception.
I played around with CallingConvention, CharSet and so on fields of the DllImport attribute:
http://msdn.microsoft.com/en-us/library/system.runtime.interopservices.dllimportattribute.aspx
without success.
This variant:
<DllImport("hid64.dll", CharSet:=CharSet.Auto, PreserveSig:=False, SetLastError:=True)> _
Public Function BufferToString( _
<MarshalAs(UnmanagedType.LPArray)> ByVal Buffer As Byte(), _
ByVal BufferLength As Int32) As <MarshalAs(UnmanagedType.LPWStr)> String
End Function
does not break execution of the programm but the function returns 'Nothing'.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
这是我认为最有可能的解释。
BufferToString
函数的返回值是一个字符串。 p/invoke 编组器必须将其从本机编组为托管。它通过假设本机代码返回由 COM 分配器分配的空终止字符指针来实现这一点。当它完成将内容传输到 .net 字符串时,它会在指针上调用CoTaskMemFree
。如果该内存不是由 COM 分配器分配的,那么此时您可能会看到失败。为了解决这个问题,您有几种选择。您可以更改
BufferToString
的 p/invoke 以返回IntPtr
。使用Marshal.PtrToStringUni
将内容复制到 .net 字符串。然后,您就需要负责处理非托管内存。据推测,非托管库为您提供了一种机制来做到这一点。如果您编写了非托管库,那么您可以使用替代解决方案。将 p/invoke 保留为当前状态,但更改非托管库以使用 CoTaskMemAlloc 分配返回值。这将与 p/invoke 编组器的假设相匹配。
Here's what I believe to be the most likely explanation.
The
BufferToString
function has a return value that is a string. The p/invoke marshaller has to marshal that from native to managed. It does that by assuming that the native code returns a null-terminated character pointer that was allocated by the COM allocator. When it has finished transferring the content to a .net string it callsCoTaskMemFree
on the pointer. If that memory was not allocated by the COM allocator then you may see failures at this point.To get around the problem you have a few options. You could change the p/invoke for
BufferToString
to returnIntPtr
. Copy the contents to a .net string withMarshal.PtrToStringUni
. This then leaves you with the responsibility of disposing of the unmanaged memory. Presumably the unmanaged library offers you a mechanism to do that.If you wrote the unmanaged library then you can use an alternative solution. Leave the p/invoke exactly as it currently is but change the unmanaged library to allocate the return value using
CoTaskMemAlloc
. That then will match with the p/invoke marshaller's assumptions.