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

VB.NET Pinvoke: How to fix the structure address passed into DLL?

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

在init调用时,它会保存传入的地址。在以后的调用中,DLL希望传入的地址与第一次init调用时相同。

在vb.net中,我定义了一个结构体(打包为4),我查看了内存布局,传入DLL时和C完全一样

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

我是否遗漏了什么或者甚至可以在 VB.NET 中做到这一点?

代码如下,c struct(这是遗留代码,我不希望更改它),

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)

以上只是简化版。您会认为 param1 在 C 中的地址在不同的调用期间会发生变化。

有办法解决吗?

谢谢。

编组结构的地址在每次调用中都不同是很自然的。那是因为封送拆收器必须创建一个非托管结构来发送到非托管代码。托管结构与非托管结构的布局不同,因此这是必要的。即使托管和非托管结构具有兼容的布局(即结构是 blittable 的),地址也可能会改变,因为 .net 内存管理器可以移动对象。

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

也许更大的问题是为什么觉得需要地址在调用之间保持稳定。我发现很难想象这样一种情况,即对来电者有合理的期望。您的非托管代码是否有可能通过要求调用者这样做而采取自由行为?