[英]Marshal struct with array member in C#
I'm using C# with P/Invoke to access to a DLL method. 我正在使用带有P / Invoke的C#来访问DLL方法。 The definition of the method is the following:
该方法的定义如下:
[DllImport("userManager.dll")]
static extern int GetUsers(out IntPtr userList);
Original structs: 原始结构:
typedef struct user_list {
unsigned short NumUsers;
USER_LIST_ITEM List[VARLEN];
} USER_LIST
typedef struct user_list_item {
char name[260];
unsigned char address[256];
} USER_LIST_ITEM
And the struct layout I've done is the following: 我完成的结构布局如下:
[StructLayout(LayoutKind.Sequential)]
public class USER_LIST
{
public uint NumUsers;
[MarshalAs(UnmanagedType.ByValArray)]
public USER_LIST_ITEM [] List;
}
[StructLayout(LayoutKind.Sequential)]
public class USER_LIST_ITEM
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
public string name;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
public string address;
};
But I get an error when I try to unmarshall it: 但是当我尝试解组它时,我收到一个错误:
USER_LIST userList = new USER_LIST();
// Prepare pointer
IntPtr uList = Marshal.AllocHGlobal(Marshal.SizeOf(userList));
Marshal.StructureToPtr(userList, uList, false);
result = GetUsers(out uList);
Marshal.PtrToStructure(uList, userList); <--
The runtime has encountered a fatal error.
运行时遇到了致命错误。 The address of the error was at 0x79f82af6, on thread 0x464.
错误的地址是0x79f82af6,在线程0x464上。 The error code is 0xc0000005.
错误代码是0xc0000005。 This error may be a bug in the CLR or in the unsafe or non-verifiable portions of user code.
此错误可能是CLR中的错误,也可能是用户代码的不安全或不可验证部分中的错误。 Common sources of this bug include user marshaling errors for COM-interop or PInvoke, which may corrupt the stack.
此错误的常见来源包括COM-interop或PInvoke的用户编组错误,这可能会破坏堆栈。
I get the NumUsers property right, but it seems the error occurs when unmarshalling the array. 我正确地获得了NumUsers属性,但是当解组数组时似乎发生了错误。 Any thoughts?
有什么想法吗?
If you specify an array in a structure used as an out
parameter, you need to tell the marshaler what length is the array going to be. 如果在用作
out
参数的结构中指定数组,则需要告诉编组器数组的长度。 With your code, the marshaler is probably allocating a zero-length array or just using null
, which produces the crash. 使用您的代码,封送程序可能正在分配一个零长度数组或只使用
null
,这会产生崩溃。 Unfortunately there seems to be no way to specify a variable-length out
array as a member of a structure, because MarshalAs.SizeParamIndex
only works for methods. 遗憾的是,似乎无法将变长的
out
数组指定为结构的成员,因为MarshalAs.SizeParamIndex
仅适用于方法。 You might get away with specifying a large, constant-size array using MarshalAs.SizeConst
, but generally you'd have to parse the (presumably callee-allocated) return buffer like this: 您可能会使用
MarshalAs.SizeConst
指定一个大型的恒定大小数组,但通常您必须解析(可能是被调用者分配的)返回缓冲区,如下所示:
var count = Marshal.ReadInt32 (uList) ;
var users = new List<USER_LIST_ITEM> () ;
var ptr = (long)uList + 4 ;
for (int i = 0 ; i < count ; ++i)
{
users.Add (Marshal.PtrToStructure (typeof (USER_LIST_ITEM),
new IntPtr (ptr))) ;
ptr += Marshal.SizeOf (typeof (USER_LIST_ITEM)) ;
}
You'll have to pay extra attention to alignment&padding and 32/64 bit issues. 你必须特别注意对齐和填充以及32/64位问题。
That is because List
has not been allocated yet. 那是因为尚未分配
List
。
You will need initialize all the fields. 您需要初始化所有字段。
Another problem I see is with the following: 我看到的另一个问题是:
IntPtr uList = Marshal.AllocHGlobal(Marshal.SizeOf(userList));
...
result = GetUsers(out uList);
Are you sure that out
should not be ref
? 你确定
out
不应该被ref
? Else there is no point (not sure if ref
is correct either). 否则没有意义(不确定
ref
是否正确)。
Update: Looking at your code again, you should be doing this (and avoid that memory leak poking your eye). 更新:再次查看您的代码,您应该这样做(并避免内存泄漏戳你的眼睛)。
IntPtr uList;
var result = GetUsers(out uList);
var userlist = (USER_LIST) Marshal.PtrToStructure(ulist, typeof(USER_LIST));
Marshal.FreeHGlobal(ulist); // pray here or shoot the author of the C function
Update again: 再次更新:
Your p/invoke signature is likely wrong or you are interpreting it wrong. 您的p / invoke签名可能是错误的,或者您的解释错误。
I can guess it probably something like: 我猜它可能是这样的:
int GetUsers(USER_LIST* ulist);
And that what you have is not the same thing. 而你所拥有的并不是一回事。
If this is case, the solution is easy. 如果是这种情况,解决方案很简单。
Change USER_LIST
to a class (but keep sequential layout) and use 将
USER_LIST
更改为类(但保持顺序布局)并使用
// pinvoke sig
int GetUsers(USER_LIST ulist);
var ulist = new USER_LIST();
// initialize fields
var r = GetUsers(ulist);
-- or -- - 要么 -
Call it by ref
. 请
ref
。
// pinvoke sig
int GetUsers(ref USER_LIST ulist);
var ulist = new USER_LIST();
// initialize fields
var r = GetUsers(ref ulist);
This way, you dont have to mess with manual marshalling, and I cant see anymore potential for memory leaks. 这样,你不必乱用手动编组,我不能再看到内存泄漏的可能性。
Final update: 最后更新:
Given the signature you posted, it looks like GetUsers
returns a pointer to a list of USER_LIST
with the return value being the count. 鉴于您发布的签名,看起来
GetUsers
返回指向USER_LIST
列表的指针,返回值为count。 Nice memory leak there. 那里的内存泄漏很好。
Anyways, I would probably experiment with an unsafe approach here, and just walk thru the result , and make sure everything gets freed. 无论如何,我可能会在这里尝试一种不安全的方法,然后只是走过结果,并确保一切都被释放。 (I still think you should shoot the author).
(我仍然认为你应该拍摄作者)。
I think your original code isn't probably so wrong. 我认为你的原始代码可能不是那么错。 You've probably just used the wrong overload of Marshal.PtrToStructure .
您可能只是使用了Marshal.PtrToStructure的错误重载 。
Have you tried this? 你试过这个吗?
[DllImport("userManager.dll")]
static extern int GetUsers(out IntPtr userList);
[DllImport("userManager.dll")]
static extern void UMFree(IntPtr userList);
static void Main()
{
IntPtr userList; // no need to allocate memory in managed code;
GetUsers(out userList); // memory is allocated by native function
USER_LIST u = (USER_LIST)Marshal.PtrToStructure(userList, typeof(USER_LIST));
UMFree(userList);
}
Using unsafe code: 使用不安全的代码:
public unsafe struct USER_LIST
{
public uint numUsers;
public USER_LIST_ITEM* list;
}
public unsafe struct USER_LIST_ITEM
{
public fixed byte name[260];
public fixed byte address[256];
}
class Program
{
[DllImport("userManager.dll")]
static unsafe extern int GetUsers(USER_LIST** userList);
[DllImport("userManager.dll")]
static unsafe extern int UMFree(USER_LIST* userList);
private static unsafe void Main()
{
USER_LIST* list;
GetUsers(&list);
UMFree(list);
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.