如何将带有Union的c++结构体转换为c#
本文关键字:结构体 c++ 转换 Union | 更新日期: 2023-09-27 17:49:40
伙计们,在调用DLL中的函数后,我在检索结构成员值方面遇到了困难。我试图将c++代码转换为c#,但我不确定它是否正确。请帮助我了解我在这里的错误(如果有的话)以及如何改正。
我这里的问题是我不能正确地检索INNER STRUCTS (Union)的值后,我从DLL调用ReceiveMessage函数。例如m_objMsg.MsgData.StartReq。MsgID总是0。但是当我尝试使用c++ .exe程序时,MsgID有正确的值。(0)
c++代码:extern int ReceiveMessage(SESSION, int, Msg*);
typedef struct
{
char SubsId[15];
int Level;
char Options[12];
} ConxReq;
typedef struct
{
char MsgId[25];
} StartReq;
typedef struct
{
long Length;
short Type;
union
{
ConxReq oConxReq;
StartReq oStartReq;
} Data;
} Msg;
/////////////////////////////////////////////////////
Msg oMsg;
int rc=ReceiveMessage(Session, 0, &oMsg);
switch(rc)
{
case 0:
switch(oMsg.Type)
{
case 0: // ConxReq
…
break;
case 1: // StartReq
…
break;
…
}
下面是我把它转换成c#的尝试:
[DllImport("MyDLL.dll",
CallingConvention = CallingConvention.Cdecl,
CharSet = CharSet.Ansi)]
protected static extern Int32 ReceiveMessage(IntPtr session,
Int32 nTimeOut,
[MarshalAs(UnmanagedType.Struct)] ref Msg ptrMsg);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct ConxReq
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 15)]
public string SubsId;
public Int32 Level;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 12)]
public string Options;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct StartReq
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 25)]
public string MsgId;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
protected struct Msg
{
public int Length;
public Int16 Type;
public Data MsgData;
}
StructLayout(LayoutKind.Explicit, CharSet = CharSet.Ansi)]
public struct Data
{
[FieldOffset(0)]
public ConxReq oConxReq;
[FieldOffset(0)]
public StartReq oStartReq;
}
Msg m_objMsg = new Msg();
m_objMsg.MsgData = new Data();
m_objMsg.MsgData.oConxReq = new ConxReq();
m_objMsg.MsgData.oStartReq = new StartReq();
int rc = ReceiveMessage(m_Session, nTimeOut, ref m_objMsg);
then the SWITCH Condition
如果我在c++和c#的UNION中添加这个结构体…我有一个错误,说明"…"不正确对齐"或"…重叠…"
c++ConxNack oConxNack;
typedef struct
{
int Reason;
} ConxNack;
[StructLayout(LayoutKind.Sequential)]
public struct ConxNack
{
public int nReason;
}
[FieldOffset(0)]
public ConxNack oConxNack;
提前感谢您的时间和帮助…
Akash是对的,看看这里:http://social.msdn.microsoft.com/Forums/en/csharplanguage/thread/60150e7b-665a-49a2-8e2e-2097986142f3
另一种选择是创建两个结构体,并在确定其类型后使用适当的强制转换。
hth
马里奥在c++中,我们知道UNION的所有成员共享相同的内存块,并且一次只能有一个对象的成员。为了在c#中实现这一点,我们需要将LayoutKind设置为Explicit,并将每个成员的所有起始点设置为0。
在我前面的例子中,显示一个错误消息,指出对象类型的偏移量被非对象类型不正确地对齐或重叠。
答案是我们不能将FieldOffSet的所有成员设置为0,因为不允许将引用类型与值类型结合起来。——感谢汉斯·帕桑的解释
我所做的是创建UNION成员结构的副本,并将所有String成员变量的类型更改为字节。我使用字节,因为这是一个值类型,所以我可以把这个结构体放入FieldOffSet(0)。请注意,我调整了下一个成员变量的FieldOffSet,所以我仍然可以得到我的字符串变量的相同大小。还有结构体的大小,因为我最后有一个字节成员。感谢Akash Kava和Mario The Spoon给了我一个想法,并提供了一个有用的链接。
在DLL中调用函数并传递此Struct Obj (ref m_objMsg)作为参数后,我需要提取值。一种方法是拥有一个指向非托管内存中结构的地址的指针,并将该指针转换为new(我的原始结构)。
NEW STRUCTS (BYTES)
////////////////////////////////////////////////////////////////
[StructLayout(LayoutKind.Explicit, CharSet = CharSet.Ansi, Size = 31)]
public struct ConxReq
{
[FieldOffSet(0)]
public byteSubsId;
[FieldOffSet(15)]
public Int32 Level;
[FieldOffSet(19)]
public byte Options;
}
[StructLayout(LayoutKind.Explicit, Size = 4)]
public struct ConxNack
{
[FieldOffSet(0)]
public int nReason;
}
[StructLayout(LayoutKind.Explicit, CharSet = CharSet.Ansi, Size = 25)]
public struct StartReq
{
[FieldOffSet(0)]
public byte MsgId;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
protected struct Msg
{
public int Length;
public Int16 Type;
public Data MsgData;
}
StructLayout(LayoutKind.Explicit, CharSet = CharSet.Ansi)]
public struct Data
{
[FieldOffset(0)]
public ConxReq oConxReq;
[FieldOffset(0)]
public ConxNack oConxNack;
[FieldOffset(0)]
public StartReq oStartReq;
}
////////////////////////////////////////////////////////////////
MY ORIGINAL STRUCTS
////////////////////////////////////////////////////////////////
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct MyConxReq
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 15)]
public string SubsId;
public Int32 Level;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 12)]
public string Options;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct MyStartReq
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 25)]
public string MsgId;
}
[StructLayout(LayoutKind.Sequential)]
public struct MyConxNack
{
public int nReason;
}
///////////////////////////////////////////////////////////////
Since I have a Msg.Type, i know what kind of struct (type) I could cast the object.
Like for example
ReceiveMessage(m_Session, nTimeOut, ref oMsg);
switch (oMsg.Type)
{
case 0: // ConxReq
IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(oMsg.MsgData.ConxReq); // use the new struct (bytes)
Marshal.StructureToPtr(oMsg.MsgData.ConxReq, ptr, false);
MyConxReq oMyConxReq = new MyConxReq;
oMyConxReq = (MyConxReq) Marshal.PtrToStructure(ptr, typeof(MyConxReq)); // convert it to the original struct
Marshal.FreeHGlobal(ptr);
Then you can use now the oMyConxReq object to acccess the member variables directly.
如果你有其他或更好的方法,请告诉我……如果我做的是正确的,或者如果我遗漏了什么,请告诉我。
非常感谢!!:)
你必须使用StructLayout(LayoutKind.Explicit)和FieldOffsets来联合。