簡體   English   中英

使用C#中的數組成員的Marshal結構

[英]Marshal struct with array member in C#

我正在使用帶有P / Invoke的C#來訪問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); <--

運行時遇到了致命錯誤。 錯誤的地址是0x79f82af6,在線程0x464上。 錯誤代碼是0xc0000005。 此錯誤可能是CLR中的錯誤,也可能是用戶代碼的不安全或不可驗證部分中的錯誤。 此錯誤的常見來源包括COM-interop或PInvoke的用戶編組錯誤,這可能會破壞堆棧。

我正確地獲得了NumUsers屬性,但是當解組數組時似乎發生了錯誤。 有什么想法嗎?

如果在用作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位問題。

那是因為尚未分配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列表的指針,返回值為count。 那里的內存泄漏很好。

無論如何,我可能會在這里嘗試一種不安全的方法,然后只是走過結果,並確保一切都被釋放。 (我仍然認為你應該拍攝作者)。

我認為你的原始代碼可能不是那么錯。 您可能只是使用了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);
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM