C# pinvoke structs with union and arrays

…衆ロ難τιáo~ 提交于 2019-12-05 20:53:35

As you have discovered, the marshaler does not like you trying to overlay the array with other fields in your translation of the union. You've suppressed the problem by changing the offset for the array to be 4 rather than 0. But that doesn't help.

Now, since the marshaler won't deal with the union for you, you'll have to do it manually. I would handle this by omitting the non-array members of the union.

[StructLayout(LayoutKind.Sequential)]
public struct DG_CCTALK_APP_EVT
{
    public DG_CCTALK_APP_EVT_CODE eEventCode;
    public DG_CCTALK_APP_EVT_TYPE eEventType;
    public int iTime;
    public int iHandle;
    public byte uchAddress;
    public DG_CCTALK_BILL_INFO sBillInfo;
    public int iBillAmount;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 128)]
    public byte[] uData;
    public IntPtr _private;
}

Note that I have removed much of the verbosity of your declaration. I suspect that the type was automatically generated by a tool. But all that verbosity makes it very hard to read.

This will produce the correct layout for the struct, but will leave you with work to do to access the other fields in the union.

So, how can we read those fields? Well, the data is contained in the byte array uData, so it is just a matter of reading the values from there. For instance, you could add the following property to the DG_CCTALK_APP_EVT struct:

public int lData
{
    get { return BitConverter.ToInt32(uData, 0); }
    set { uData = BitConverter.GetBytes(value); }
}

public int iData
{
    get { return BitConverter.ToInt32(uData, 0); }
    set { uData = BitConverter.GetBytes(value); }
}

// etc.

Note that the setter will obliterate all the but first 4 bytes of the array, since it overwrites uData. I don't know enough about the protocol of the native code to be sure that this is what you want. If it's not what you want, then you might write the setter like this:

Array.Copy(BitConverter.GetBytes(value), uData, sizeof(int));

You've now added the C++ side of the interop boundary. The most obvious problem is that your C# delegate appears to have the wrong calling convention. It uses stdcall but the C++ code expects cdecl. That's certainly enough to explain a catastrophic crash after your callback returns. You'll need to make sure that your delegate specifies the calling convention.

[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void DG_CCTALK_APP_EVT_HANDLER(ref DG_CCTALK_APP_EVT pEVT);

Your translation of cctalk_app_add_link is wrong too. It should be:

[DllImport("cctalk.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int cctalk_app_add_link(string szPortName);

Done like this you can simply pass a string and thus avoid the memory leak that your current implementation has.

Check your int and long fields. I think that C int is 16 bit while C# int is 32:

  • If that's true, then iTime, iHandle and iBilAmount are defined as int in both C# and C definitions.
  • If I'm wrong, then lData is defined as long in C but int in C# (if that's the problem, then don't forget to fix the offsets of fields that appear after lData).
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!