C# 中带有数组成员的元帅结构

发布于 2024-08-13 07:43:59 字数 1366 浏览 8 评论 0原文

我使用 C# 和 P/Invoke 来访问 DLL 方法。该方法的定义如下:

[DllImport("userManager.dll")]
static extern int GetUsers(out IntPtr userList);

原始结构:

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

我所做的结构布局如下:

[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;
};

但是当我尝试解组它时出现错误:

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); <--

运行时遇到致命错误。错误的地址位于线程 0x464 上的 0x79f82af6。错误代码为0xc0000005。此错误可能是 CLR 中的错​​误或用户代码的不安全或不可验证部分中的错误。此错误的常见来源包括 COM 互操作或 PInvoke 的用户编组错误,这可能会损坏堆栈。

我得到了 NumUsers 属性,但似乎在解组数组时发生了错误。有什么想法吗?

I'm using C# with P/Invoke to access to a DLL method. 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. The error code is 0xc0000005. This error may be a bug in the CLR or in the unsafe or non-verifiable portions of user code. Common sources of this bug include user marshaling errors for COM-interop or PInvoke, which may corrupt the stack.

I get the NumUsers property right, but it seems the error occurs when unmarshalling the array. Any thoughts?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

∝单色的世界 2024-08-20 07:43:59

如果您在用作 out 参数的结构中指定一个数组,则需要告诉封送拆收器该数组的长度是多少。对于您的代码,封送拆收器可能会分配零长度数组或仅使用 null,这会导致崩溃。不幸的是,似乎无法将可变长度 out 数组指定为结构体的成员,因为 MarshalAs.SizeParamIndex 仅适用于方法。您可能会使用 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)) ;
}

您必须特别注意对齐和填充以及 32/64 位问题。

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. With your code, the marshaler is probably allocating a zero-length array or just using null, which produces the crash. 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. 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:

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.

少女净妖师 2024-08-20 07:43:59

这是因为 List 尚未分配。

您将需要初始化所有字段。

我看到的另一个问题是:

IntPtr uList = Marshal.AllocHGlobal(Marshal.SizeOf(userList));
...
result = GetUsers(out uList);

您确定 out 不应该是 ref 吗?否则就没有意义(也不确定 ref 是否正确)。

更新:再次查看您的代码,您应该这样做(并避免内存泄漏刺痛您的眼睛)。

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

再次更新:

您的 p/invoke 签名可能是错误的,或者您对它的解释是错误的。

我可以猜测可能是这样的:

int GetUsers(USER_LIST* ulist);

而你所拥有的不是同一件事。

如果是这种情况,解决方案很简单。

USER_LIST 更改为一个类(但保持顺序布局)并使用

// pinvoke sig
int GetUsers(USER_LIST ulist);

var ulist = new USER_LIST();
// initialize fields
var r = GetUsers(ulist);

-- 或 --

通过 ref 调用它。

// pinvoke sig
int GetUsers(ref USER_LIST ulist);

var ulist = new USER_LIST();
// initialize fields
var r = GetUsers(ref ulist);

这样,您就不必搞乱手动编组,而且我看不到内存泄漏的可能性。

最终更新:

鉴于您发布的签名,看起来 GetUsers 返回一个指向 USER_LIST 列表的指针,返回值是计数。那里有很好的内存泄漏。

无论如何,我可能会在这里尝试一种不安全的方法,然后遍历结果,并确保所有内容都被释放。 (我仍然认为你应该射击作者)。

That is because List has not been allocated yet.

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? Else there is no point (not sure if ref is correct either).

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.

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

// pinvoke sig
int GetUsers(USER_LIST ulist);

var ulist = new USER_LIST();
// initialize fields
var r = GetUsers(ulist);

-- or --

Call it by 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. 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).

风柔一江水 2024-08-20 07:43:59

我认为您的原始代码可能没有那么错误。
您可能刚刚使用了 错误的重载 “http://msdn.microsoft.com/en-us/library/4ca6d5z7.aspx”rel="nofollow noreferrer">Marshal.PtrToStructure。

你试过这个吗?

[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);
}

使用不安全的代码:

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);
    }
}

I think your original code isn't probably so wrong.
You've probably just used the wrong overload of 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);
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文