[英]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#管理而不是由內存管理。
使用IntPtr
和Marshal.PtrToStructure
但它嘗試將內存復制到另一個位置:
IntPtr C = CreateContext(1); if (C == null) return; Context Ctx = (Context)Marshal.PtrToStructure(C, typeof( Context ) ); Ctx.Method = 2;
(在帶有ExecutionEngineException
的PtrToStructure
調用上失敗)。
嘗試修改聲明-例如,刪除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.