繁体   English   中英

如何将带有Union的C ++ Struct转换为C#?

[英]How to convert a C++ Struct with Union into C#?

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

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

非常感谢您的时间和帮助......

您必须使用StructLayout(LayoutKind.Explicit)和FieldOffsets来进行联合。

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

另一种选择是创建两个结构并在知道它是哪种类型后使用适当的强制转换。

心连心

马里奥

在C ++中,我们知道UNION的所有成员共享相同的内存块,并且一次只能有一个对象的一个​​成员。 为了在C#中实现它,我们需要使用LayoutKind来显式并将每个成员的所有起始点设置为0。

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

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

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

在调用DLL中的函数并将此结构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.  

如果你有其他或更好的方法,请告诉我...请告诉我,如果我做的是正确的,或者我错过了什么。

非常感谢!!! :)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM