如何将联合C++结构转换为C#?

问题描述:

伙计们我在调用DLL中的函数后检索struct成员值时遇到困难。我试图将C++代码转换为C#,但我不确定它是否正确。请帮我理解我的错误(如果有的话)以及如何纠正。如何将联合C++结构转换为C#?

我在这里的问题是我无法正确地检索后,我从DLL调用ReceiveMessage函数后INNER STRUCTS(联盟)的值。例如,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 

,如果我加入这个结构的UNION内的C++和C#。 .. 我有一个错误,说明了“......正确对齐”或“......重叠......”

C++

ConxNack oConxNack; 

typedef struct 
{ 

    int Reason; 

} ConxNack; 


[StructLayout(LayoutKind.Sequential)]   
public struct ConxNack 
{    
    public int nReason; 
} 

[FieldOffset(0)] 
public ConxNack oConxNack; 

预先感谢您这么多的时间,并帮助......

阿卡什是正确的,看看这里:http://social.msdn.microsoft.com/Forums/en/csharplanguage/thread/60150e7b-665a-49a2-8e2e-2097986142f3

另一种选择是创建两个结构,一旦你知道哪些类型是使用合适的演员。

HTH

马里奥

+0

感谢您的回复和链接。根据链接,联盟内部有两个结构。他们所做的是在Union中明确提取两个结构和布局的所有成员变量。在我的转换中,我更倾向于将Union中每个结构的起始FieldOffSet都设置为0.是不可能的? – Ryan 2011-04-12 06:28:07

+0

唯一的区别是我仍然把每个结构中的所有成员变量。 – Ryan 2011-04-12 06:30:03

+0

你是什么意思创建两个结构和只是演员取决于类型?对不起,要求... – Ryan 2011-04-12 06:50:06

你必须使用StructLayout(LayoutKind.Explicit)和FieldOffsets使工会。

+1

谢谢阿卡什您的回复。我没有看到LayoutKind.Absolute,我使用.Explicit并将所有FieldOffSet设置为(0)使其成为UNION。 – Ryan 2011-04-12 04:20:32

+0

谢谢阿卡什!我已经在你的共同想法下提供了一个答案。 – Ryan 2011-04-14 02:49:43

在C++中,我们知道,UNION的所有成员共享相同的存储器块,并且只能有一个对象中的一个部件在一个时间。 为了在C#中实现这一点,我们需要使用LayoutKind来显式化并将每个成员的所有起始点设置为0.

在我之前的示例中,显示一条错误消息,指出对象的偏移量类型被错误地对齐或被非对象类型重叠。

答案是我们无法将所有成员设置为FieldOffSet为0,因为不允许将引用类型与值类型组合。 - 感谢Hans Passant的解释

我所做的是创建UNION成员Structs的副本并将所有字符串成员变量的类型更改为字节。 我使用了字节,因为这是一个值类型,所以我可以把这个结构放入FieldOffSet(0)。 请注意,我调整了下一个成员变量的FieldOffSet,所以我仍然可以获得与我的字符串变量相同的大小。 而且还因为我有字节成员在最后的结构大小。 感谢Akash Kava和Mario The Spoon为我提供了一个主意并为我提供了一个有用的链接。

在调用DLL中的函数并传递了这个Struct Obj(ref m_objMsg)作为参数之后,我需要提取这些值。 一种方法是有一个指向非托管内存中结构地址的指针,并将该指针转换为带有相应成员变量(我的原始结构)的新的 结构。

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. 

请让我知道,如果你有其他的或更好的方式来做到这一点......如果我所做的是正确的或 请告知,如果我错过了什么。

非常感谢你! :)