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

Marshaling structure (that containes arrays) as return value

本文关键字:返回值 数组 包含 结构      更新时间:2023-10-16

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

问题出在DIIV的定义中。您将它们声明为字节数组,但P/Invokemarshaller不知道这些数组有多大;指向传递长度的字节的指针并没有什么内在的东西。如果数组是可变长度的,那么P/Invokemarshaller是如何确定这个长度的?如果它们总是有一个已知的固定长度,那么你可以使用它,其中N是数组的固定长度:

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

如果返回的数组具有动态大小,并且您可以在函数返回后以某种方式确定它,那么将DIIV声明为IntPtr,并使用System.Runtime.InteropServices.Marshal的方法提取数据。

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

这是一个非常糟糕的函数,必须使用pinvoke。.NET执法官没有机会,我们也没有。结构中数组的两个基本问题是:无法猜测它们有多长,也无法合理猜测谁拥有它们的内存以及应该如何释放内存。

您必须在Structure声明中将它们声明为IntPtr。在执行更多操作之前,请编写一个重复调用的小测试程序。观察返回的IntPtr值。如果它们不同,那么你需要放弃,因为你无法释放内存,所以没有办法使用pinvoke。将需要一个C++/CLI包装程序。

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

顺便说一句,bool的[MarshalAs]是错误的,使其成为UnmanagedType.U1