C# - 调用ext。 DLL函数包含Delphi“变体记录”参数

时间:2010-05-29 22:49:26

标签: c# delphi dllimport

在外部(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”结构或单个值的继承和手动转换。因此,我想请您提供最佳方法的建议。

非常感谢!

3 个答案:

答案 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到它们上面

http://msdn.microsoft.com/en-us/library/4ca6d5z7.aspx

答案 2 :(得分:0)

您是否尝试过System.Runtime.InteropServices.Marshal类?它提供了许多Readxxx方法来访问非托管内存。