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
以读取对结构所做的任何修改。
也许更大的问题是为什么觉得需要地址在调用之间保持稳定。我发现很难想象这样一种情况,即对来电者有合理的期望。您的非托管代码是否有可能通过要求调用者这样做而采取自由行为?
我有一个用 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
以读取对结构所做的任何修改。
也许更大的问题是为什么觉得需要地址在调用之间保持稳定。我发现很难想象这样一种情况,即对来电者有合理的期望。您的非托管代码是否有可能通过要求调用者这样做而采取自由行为?