使用非托管DLL中的非托管结构而不在C#中复制
本文关键字:复制 结构 DLL | 更新日期: 2023-09-27 18:25:10
我有一个用非托管语言编写的DLL,它返回一个指向C结构的指针。C#程序必须在结构中填充一些细节。接下来,必须将相同的指针(而不是副本)提供给同一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)]
而不是CCD_ 1。但是内存需要由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代码实际上提供了内联的、byval的值数组,这些数组中的条目数量正好合适(这是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);
如果做不到,请尝试使用Marshal.Write*
方法直接写入内存,如果IntPtr
0对您不起作用。然后,您可以创建一个包装器类,该类将保存IntPtr
,并提供触发Marshal.Write
的属性,例如
public UInt16 Status {
get { return unchecked((ushort)Marshal.ReadInt16(m_Ptr, 2)); }
set { Marshal.WriteInt16(m_Ptr, 2, unchecked((short)value)); }
}