We have COM Server (as dll, we have only dll file), that implements two COM interfaces. One of interfaces allows us to get messages (as structure) from some device. Depending on message we need to process corresponding structure. Every structure is pointer to a structure VARIANT. This structure is of type byte array (VT_ARRAY | VT_UI1).
All structures contains structure Header
and some other information. Structure Header
contains field MsgId
. Depending on MsgId
we need to process other structures.
In order to get structures from dll to c# we use reflection.
Now, it's time for example:
// Header (sub-struct of main struct) -
[StructLayout(LayoutKind.Sequential)]
public struct Header
{
public int MsgId;
}
// main struct
[StructLayout(LayoutKind.Sequential)]
public struct Main
{
[MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.Struct)]
public Header h;
public int count;
}
Every 100 ms we get data from device via reflection:
private bool ReadMessage(ref object msg)
{
object _msg = null;
object[] args = new Object[] { _msg };
ParameterModifier byRefParamMod = new ParameterModifier(1);
byRefParamMod[0] = true;
ParameterModifier[] pmArray = { byRefParamMod };
var value = SomeWrapper.CallMethod(ClientInstance, "ReadMessage", args, pmArray);
msg = args[0];
return ((int)value == S_OK) ? true : false;
}
Then we cast our msg
as byte[]
and then we need to convert array of bytes into our struct. At this place we have some problem with marshaling.
As i have wrote in order to define which structure we need to process (another words we need to marshaling) first of all we need to marshal Header
structure that is contained in all structures (another words in all messages from device).
In order to marshal Header
struct we use the method proposed in C# array within a struct (with some editing):
public static T DeserializeMsg<T>(byte[] msg) where T : struct
{
// Pin the managed memory while, copy it out the data, then unpin it
GCHandle handle = GCHandle.Alloc(msg, GCHandleType.Pinned);
T theStructure = (T)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T));
handle.Free();
return theStructure;
}
How we are using one:
private void ProcessMessage(byte[] message)
{
Header msgHeader = new Header();
msgHeader = DeserializeMsg<Header>(message);
}
Everything is OK! Here we get msgHeader.MsgId
and then we need to get another structure:
private void ProcessMessage(byte[] message)
{
Header msgHeader = new Header();
msgHeader = DeserializeMsg<Header>(message);
switch (msgHeader.MsgId)
{
case START:
Main msgMain = new Main();
// Here we get exception (see below)
msgMain = DeserializeMsg<Main>(message);
break;
default:
break;
}
}
Here we get exception: Cannot marshal field 'h' of type 'Main'. Invalid managed/unmanaged type combination (this value type must be paired with Struct)
We have tried to change MarshalAsAttribute
declaration of inner struct h
to
[MarshalAsAttribute(UnmanagedType.SafeArray, SafeArraySubType=VarEnum.VT_SAFEARRAY)]
and
[MarshalAsAttribute (UnmanagedType.SafeArray, SafeArrayUserDefinedSubType=typeof(Header))]
but it does not work. How can we get data from Main
strucure if it is possible?