在外部(Delphi创建的)DLL中,我需要从C#应用程序调用以下函数。
function ReadMsg(handle: longword; var Msg: TRxMsg): longword; stdcall; external 'MyDll.dll' name 'ReadMsg';
“TRxMsg”类型是变体记录,定义如下:
TRxMsg = record
case TypeMsg: byte of
1: (accept, mask: longword);
2: (SN: string[6]);
3: (rx_rate, tx_rate: word);
4: (rx_status, tx_status, ctl0, ctl1, rflg: byte);
end;
为了从C#调用函数,我声明了包含字节数组的辅助结构“my9Bytes”,并定义它应该被编组为9个字节长的数组(这正是Delphi记录的大小)。
private struct my9Bytes {
[MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.U1, SizeConst = 9)]
public byte[] data;
}
然后我使用“my9bytes”结构声明了导入的“ReadMsg”函数。
[DllImport("MyDll.dll")]
private static extern uint ReadMsg(uint handle, ref my9Bytes myMsg);
我可以毫无问题地调用函数...然后我需要创建与原始“TRxMsg”变体记录相对应的结构,并将我的辅助“myMsg”数组转换为此结构。
我不知道任何C#等价的Delphi变量数组,所以我使用了继承并创建了以下类。
public abstract class TRxMsg {
public byte typeMsg;
}
public class TRxMsgAcceptMask:TRxMsg {
public uint accept, mask;
//...
}
public class TRxMsgSN:TRxMsg {
public string SN;
//...
}
public class TRxMsgMRate:TRxMsg {
public ushort rx_rate, tx_rate;
//...
}
public class TRxMsgStatus:TRxMsg {
public byte rx_status, tx_status, ctl0, ctl1, rflg;
//...
}
最后,我创建了适当的对象,并使用从“myMsg”数组手动转换的值对其进行初始化(我为此使用了BitConverter)。
这确实工作正常,这个解决方案在我看来有点过于复杂,并且应该可以更直接地执行此操作,而无需辅助“my9bytes”结构或单个值的继承和手动转换。因此,我想请您提供最佳方法的建议。
非常感谢!
答案 0 :(得分:1)
您通常使用结构声明中的[StructLayout(LayoutKind.Explicit)]属性来处理此问题。在这种情况下,为union成员提供单独的struct声明。如果它不是SN成员,几乎工作。 CLR不允许您覆盖字符串或数组成员,它允许不受限制地访问垃圾收集堆。
这是函数的笨蛋,这里适合手动编组。
答案 1 :(得分:1)
由于您的记录是可变的,您将按照描述进行一些手动编组。您可以通过创建具有显式布局的目标类型,然后在打开TypeMsg字节后对它们进行blitting来简化它。
您可以使用显式布局来布局目标类型(类或结构):
http://en.csharp-online.net/Common_Type_System%E2%80%94Explicit_Layout_Example
然后使用Marshal.PtrToStructure将接收的数据blit到它们上面
答案 2 :(得分:0)
您是否尝试过System.Runtime.InteropServices.Marshal类?它提供了许多Readxxx方法来访问非托管内存。