封送结构(包含数组)作为返回值

时间:2014-09-04 17:01:23

标签: c++ arrays vb.net pinvoke marshalling

结构的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,因为它不是我的,因此,我没有代码......)

2 个答案:

答案 0 :(得分:1)

这是一个非常悲惨的功能,必须进行pinvoke。 .NET marshaller没有任何机会,我们也没有。结构中数组的两个基本问题:没有办法猜测它们有多长,并且没有合理的猜测是谁拥有内存以及它应该如何释放。

您必须在结构声明中将它们声明为IntPtr。在你做更多的事情之前,写一个重复调用的测试程序。观察返回的IntPtr值。如果它们不同那么你需要放弃,因为你无法释放内存,所以无法使用pinvoke。将需要C ++ / CLI包装器。

如果他们没有什么不同,那么你就可以了。使用Marshal.Copy()从IntPtrs复制数组。您仍然需要知道要复制多少元素。如果您不知道,请致电C程序员。

顺便说一下, bool 的[MarshalAs]是错误的,让它成为UnmanagedType.U1

答案 1 :(得分:0)

问题在于您对DIIV的定义。您将它们声明为字节数组,但P / Invoke编组器不知道这些数组有多大;传达长度的指针到字节没有任何固有的东西。如果数组是可变长度的,那么P / Invoke编组器如何确定这个长度?如果它们总是具有已知的固定长度,那么您可以使用它,其中N是数组的固定长度:

<MarshalAs(UnmanagedType.LPArray, SizeConst = N)>

如果返回的数组具有动态大小,并且您可以在函数返回后以某种方式确定它,则将DIIV声明为IntPtr并使用{{{{ 3}}提取数据。

如果指针指向它希望您释放的函数分配的内存,则必须手动释放分配,这将涉及将这些指针发送回要释放的非托管函数。