VB.NET Pinvoke:如何修复传递到DLL的结构地址?

时间:2018-12-14 11:53:36

标签: .net vb.net pinvoke marshalling

我有一个用C编写的DLL(使用VC ++ 2017编译)。有几个函数接受指向结构的指针。

在init调用期间,它将保存传入的地址。在以后的调用中,DLL期望传入的地址与第一个init调用相同。

在vb.net中,我定义了一个结构(打包为4),检查了内存布局,将其传递给DLL后,它与C完全相同。

但是,每次我使用结构(ByRef)调用函数时,地址可能会更改,也可能不会更改(移动4个字节)。

我错过了什么吗?还是有可能在VB.NET中做到这一点?

代码如下,c结构(这是旧代码,我希望不要更改它),

struct A
{
    char a[9] ;
    char b[9] ;
    char c[2] ;
    char d[9] ;
    int e;
    int f;
    char g[2] ;
    char h[9] ;
    int i;
    int j;
    char k[2] ;
    int l;
    char m[41] ;
    char n[41] ;
    char o[10] ;
} ;

这就是我在VB.NET中定义的

<StructLayout(LayoutKind.Sequential, Pack:=4)>
Structure A
    <VBFixedArray(9), System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.ByValArray, SizeConst:=9)> Dim a() As Byte
    <VBFixedArray(9), System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.ByValArray, SizeConst:=9)> Dim b() As Byte
    <VBFixedArray(2), System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.ByValArray, SizeConst:=2)> Dim c() As Byte
    <VBFixedArray(9), System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.ByValArray, SizeConst:=9)> Dim d() As Byte
    Dim e As Integer
    Dim f As Integer
    <VBFixedArray(2), System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.ByValArray, SizeConst:=2)> Dim g() As Byte
    <VBFixedArray(9), System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.ByValArray, SizeConst:=9)> Dim h() As Byte
    Dim i As Integer
    Dim j As Integer
    <VBFixedArray(2), System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.ByValArray, SizeConst:=2)> Dim k() As Byte
    Dim l As Integer
    <VBFixedArray(41), System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.ByValArray, SizeConst:=41)> Dim m() As Byte
    <VBFixedArray(41), System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.ByValArray, SizeConst:=41)> Dim n() As Byte
    <VBFixedArray(10), System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.ByValArray, SizeConst:=10)> Public o() As Byte
End Structure

这些是C原型:

__declspec( dllexport ) int __stdcall init(struct A * param1)
__declspec( dllexport ) int __stdcall dosomething(struct A * param1)

这些是VB.NET原型

Public Declare Function init Lib "A.dll" (ByRef param1 As A) As Integer
Public Declare Function dosomething Lib "A.dll" (ByRef param1 As A) As Integer

Dim a As New A
'ok
init(a)
' ok
dosomething(a)
' The second call to dosomething, the param1's address changed by 4 bytes
dosomething(a)

以上仅为简化版本。您会想到在不同的调用过程中C中的param1的地址会发生变化。

有没有办法解决这个问题?

谢谢。

1 个答案:

答案 0 :(得分:2)

在每个调用中,封送处理的结构的地址都不同是很自然的。那是因为封送程序必须创建一个非托管结构才能发送到非托管代码。托管结构与非托管结构的布局不同,因此这是必需的。即使托管和非托管结构具有兼容的布局(即该结构是可蓝调的),该地址也可能会更改,因为.net内存管理器可以移动对象。

但是,您可以负责封送处理。分配一些非托管内存(例如,通过调用Marshal.AllocHGlobal,然后使用Marshal.StructureToPtr用该结构的封送版本填充该内存。然后,您可以将该非托管内存的地址传递给非托管代码。您已经完成了对非托管代码的所有调用,请调用Marshal.PtrToStructure以读取对该结构所做的任何修改。

也许更大的问题是,为什么在两次通话之间感到需要稳定地址。我发现很难想象这样的情况,即可以合理地期望呼叫者。您的非托管代码是否有可能因为要求调用者而获得自由?