结构的C ++代码如下:
typedef struct _a astruct;
struct _a {
BYTE fi, Sec, *D, *IIV, PV;
bool Visited;
};
以及使用它的函数:
astruct DoPDC(string *InitialData);
我想通过VB.NET使用这个函数(包含在DLL中),所以我为结构编写了以下代码:
<StructLayout(LayoutKind.Sequential)> _
Public Structure astruct
Dim fi As Byte
Dim Sec As Byte
Dim D As Byte()
Dim IIV As Byte()
Dim PV As Byte
<MarshalAs(UnmanagedType.Bool)> Dim Visited As Boolean
End Structure
和函数声明:
<DllImport("astr.dll", EntryPoint:="DoPDC", BestFitMapping:=False, CallingConvention:=CallingConvention.Cdecl, ThrowOnUnmappableChar:=True, CharSet:=CharSet.Ansi)> _
Public Function DoPDC(ByVal InitialData As String()) As <MarshalAs(UnmanagedType.Struct)> astruct
End Function
所以,我得到了悲惨的错误MarshalDirectiveError
,特别是Method's type signature is not PInvoke compatible.
我之前从未见过这个错误,互联网上有太多不同的解决方案,但没有人有我的情况,我在哪里作为参数传递一个字符串数组,返回一个包含更多数组的结构!
结果,我不知道出了什么问题:参数,返回值,返回值里面的数组还是全部在一起?我很困惑...因此,我的问题是:如何修改我的VB.NET P / Invoke代码,以便它能正常工作?
(请注意,我不可能更改C ++ DLL,因为它不是我的,因此,我没有代码......)
答案 0 :(得分:1)
这是一个非常悲惨的功能,必须进行pinvoke。 .NET marshaller没有任何机会,我们也没有。结构中数组的两个基本问题:没有办法猜测它们有多长,并且没有合理的猜测是谁拥有内存以及它应该如何释放。
您必须在结构声明中将它们声明为IntPtr
。在你做更多的事情之前,写一个重复调用的测试程序。观察返回的IntPtr值。如果它们不同那么你需要放弃,因为你无法释放内存,所以无法使用pinvoke。将需要C ++ / CLI包装器。
如果他们没有什么不同,那么你就可以了。使用Marshal.Copy()从IntPtrs复制数组。您仍然需要知道要复制多少元素。如果您不知道,请致电C程序员。
顺便说一下, bool 的[MarshalAs]是错误的,让它成为UnmanagedType.U1答案 1 :(得分:0)
问题在于您对D
和IIV
的定义。您将它们声明为字节数组,但P / Invoke编组器不知道这些数组有多大;传达长度的指针到字节没有任何固有的东西。如果数组是可变长度的,那么P / Invoke编组器如何确定这个长度?如果它们总是具有已知的固定长度,那么您可以使用它,其中N
是数组的固定长度:
<MarshalAs(UnmanagedType.LPArray, SizeConst = N)>
如果返回的数组具有动态大小,并且您可以在函数返回后以某种方式确定它,则将D
和IIV
声明为IntPtr
并使用{{{{ 3}}提取数据。
如果指针指向它希望您释放的函数分配的内存,则必须手动释放分配,这将涉及将这些指针发送回要释放的非托管函数。