views:

297

answers:

3

Hello,

In external (Delphi-created) DLL I've got the following function that I need to call from C# application.

function ReadMsg(handle: longword; var Msg: TRxMsg): longword; stdcall; external 'MyDll.dll' name 'ReadMsg';

The "TRxMsg" type is variant record, defined as follows:

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;

In order to call the function from C#, I declared auxiliary structure "my9Bytes" containing array of bytes and defined that it should be marshalled as 9 bytes long array (which is exactly the size of the Delphi record).

private struct my9Bytes {
    [MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.U1, SizeConst = 9)]
    public byte[] data;
}

Then I declared the imported "ReadMsg" function, using the "my9bytes" struct.

[DllImport("MyDll.dll")]
private static extern uint ReadMsg(uint handle, ref my9Bytes myMsg);

I can call the function with no problem... Then I need to create structure corresponding to the original "TRxMsg" variant record and convert my auxiliary "myMsg" array into this structure.

I don't know any C# equivalent of Delphi variant array, so I used inheritance and created the following classes.

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;
    //...
}

Finally I create the appropriate object and initialize it with values manually converted from "myMsg" array (I used BitConverter for this).

This does work fine, this solution seems to me a little too complicated, and that it should be possible to do this somehow more directly, without the auxiliary "my9bytes" structures or the inheritance and manual converting of individual values. So I'd like to ask you for a suggestions for the best way to do this.

Thanks a lot!

A: 

Have you tried System.Runtime.InteropServices.Marshal class? It provides a lot of Readxxx methods to access unmanaged memory.

Kai Wang
+1  A: 

You'd normally handle this with the [StructLayout(LayoutKind.Explicit)] attribute on a structure declaration. With separate struct declarations for the union members, in this case. Which would almost work if it wasn't for the SN member. The CLR won't allow you to overlay a string or array member, it would allow unrestricted access to the garbage collected heap.

It's a mutt of a function, manual marshaling is appropriate here.

Hans Passant
+1  A: 

As your record is variable you're going to have do some manual marshalling as described. You may be able to simplify it a little by creating target types with explicit layout, and then blitting onto these after switching on your TypeMsg byte.

You can layout your target types (classes or structs) using explicit layout:

http://en.csharp-online.net/Common_Type_System%E2%80%94Explicit_Layout_Example

And then blit the received data onto them using Marshal.PtrToStructure:

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

chibacity