簡體   English   中英

使用非托管DLL中的非托管結構而不在C#中復制

[英]Using an unmanaged struct from an unmanaged DLL without copying in C#

我有一個用非托管語言編寫的DLL,它返回一個指向C結構的指針。 AC#程序必須在結構中填寫一些細節。 接下來,必須將相同的指針(不是副本)從同一DLL提供給另一個方法。現在,C#程序從C結構中收集數據。

數據類型:

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode, Pack = 1, Size = 18 * 2 + 24 * 256)]
public class Context {
    public UInt16 Magic;
    public UInt16 Method;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2*16)]
    public UInt16[] Status;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8*256)]
    public Field[]  InputFields;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8*256)]
    public Field[]  OutputFields;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8*256)]
    public Field[]  MetaData;
}

[StructLayout(LayoutKind.Sequential,CharSet=CharSet.Unicode,Pack=1,Size=256)]
public class Field {
    public UInt16 Kind;
    public UInt16 Status;
    public UInt16 Length;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 125)]
    public string Data;
}

方法:

[DllImport("x.dll")]
//[return: MarshalAs(UnmanagedType.LPStruct)]
public static extern IntPtr  CreateContext    ( UInt16 ContextKind );
[DllImport("x.dll")]
public static extern UInt16  DestroyContext   ( IntPtr Context );
[DllImport("x.dll")]
public static extern UInt16  Execute          ( [In, MarshalAs(UnmanagedType.LPStruct)] Context Context );

如何在我的C#程序中填寫/讀出由DLL(不是C#)管理的內存?

我試過了:

  • 使用

     [return: MarshalAs(UnmanagedType.LPStruct)] 

    而不是IntPtr 但是內存需要由C#管理而不是由內存管理。

  • 使用IntPtrMarshal.PtrToStructure但它嘗試將內存復制到另一個位置:

     IntPtr C = CreateContext(1); if (C == null) return; Context Ctx = (Context)Marshal.PtrToStructure(C, typeof( Context ) ); Ctx.Method = 2; 

(在帶有ExecutionEngineExceptionPtrToStructure調用上失敗)。

嘗試修改聲明-例如,刪除Size參數(但不SizeConst ),並確保C代碼實際上提供了內聯的按字節排列的值數組,這些數組中的條目數量正確(這是UnmanagedType.ByValArray, SizeConst的要求UnmanagedType.ByValArray, SizeConst )。 例如,這對我有用:

// Init array fields (required; supposed to happen on the C side)
ctx.Status = new UInt16[2 * 16];
ctx.InputFields = new Field[8 * 256];
ctx.OutputFields = new Field[8 * 256];
ctx.MetaData = new Field[8 * 256];

// Test data to survive the roundtrip (also supposed to happen on the C side)
ctx.Method = 42;
ctx.InputFields[42] = new Field() { Data = "Hi." };
ctx.OutputFields[42] = new Field() { Data = "Also hi." };


IntPtr buf = Marshal.AllocCoTaskMem(1024 * 100);

Marshal.StructureToPtr(ctx, buf, false);

var ctx_new = new Context();
Marshal.PtrToStructure(buf, ctx_new);

Marshal.FreeCoTaskMem(buf);


Console.WriteLine(ctx_new.Method);
Console.WriteLine(ctx_new.InputFields[42].Data);
Console.WriteLine(ctx_new.OutputFields[42].Data);

失敗的話,如果PtrToStructure不適合您,請嘗試使用Marshal.Write*方法直接寫入內存。 然后,您可以創建一個包裝類,該包裝類將保存IntPtr並提供觸發Marshal.Write屬性-例​​如

public UInt16 Status {
    get { return unchecked((ushort)Marshal.ReadInt16(m_Ptr, 2)); }
    set { Marshal.WriteInt16(m_Ptr, 2, unchecked((short)value)); }
}

暫無
暫無

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

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