使用非托管DLL中的非托管结构而无需在C#中复制

时间:2014-10-01 14:04:10

标签: c# dll pinvoke

我有一个用非托管语言编写的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)]
    

    而不是IntPtr。但是内存需要由C#管理,而不是它。

  • 使用IntPtrMarshal.PtrToStructure,但会尝试将内存复制到其他位置:

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

(使用PtrToStructure ExecutionEngineException来电失败。

1 个答案:

答案 0 :(得分:0)

尝试修改声明 - 例如删除Size参数(但不是SizeConst),并确保C代码实际上提供了内嵌的byval数组值,这些值的数组具有恰当的条目数(这是{{{ 1}})。例如。这对我有用:

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*不适合您。然后,您可以创建一个包含PtrToStructure的包装类,并提供触发IntPtr的属性 - 例如。

Marshal.Write