简体   繁体   English

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

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

I have a DLL written in unmanaged language that returns a pointer to a C structure. 我有一个用非托管语言编写的DLL,它返回一个指向C结构的指针。 AC# program must fill in some details into the structure. AC#程序必须在结构中填写一些细节。 Next the same pointer (not a copy) must be given to another method from the same DLL Now the C# program collects the data from the C structure. 接下来,必须将相同的指针(不是副本)从同一DLL提供给另一个方法。现在,C#程序从C结构中收集数据。

The datatype: 数据类型:

[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;
}

Methods: 方法:

[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 );

How can I fill in / read out the memory that is mananged by the DLL (not C#) in my C# program? 如何在我的C#程序中填写/读出由DLL(不是C#)管理的内存?

I tried: 我试过了:

  • Using 使用

     [return: MarshalAs(UnmanagedType.LPStruct)] 

    instead of IntPtr . 而不是IntPtr But memory needs to be managed by C# for that which it is not. 但是内存需要由C#管理而不是由内存管理。

  • Use IntPtr and Marshal.PtrToStructure but it tries to copy the memory to another location: 使用IntPtrMarshal.PtrToStructure但它尝试将内存复制到另一个位置:

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

(fails on the PtrToStructure call with an ExecutionEngineException ). (在带有ExecutionEngineExceptionPtrToStructure调用上失败)。

Try fixing your declarations - eg remove the Size parameter (but not SizeConst ), and make sure the C code actually provides inline, byval arrays of values that have exactly the right number of entries in them (which is the requirement for UnmanagedType.ByValArray, SizeConst ). 尝试修改声明-例如,删除Size参数(但不SizeConst ),并确保C代码实际上提供了内联的按字节排列的值数组,这些数组中的条目数量正确(这是UnmanagedType.ByValArray, SizeConst的要求UnmanagedType.ByValArray, SizeConst )。 Eg this works for me: 例如,这对我有用:

// 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);

Failing that, try using the Marshal.Write* methods to write to the memory directly if PtrToStructure does not work for you. 失败的话,如果PtrToStructure不适合您,请尝试使用Marshal.Write*方法直接写入内存。 You can then create a wrapper class that would hold the IntPtr and provide properties that would trigger the Marshal.Write - eg 然后,您可以创建一个包装类,该包装类将保存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