Как преобразовать структуру C ++ с Union в C #? - PullRequest
4 голосов
/ 12 апреля 2011

Ребята У меня возникают трудности при получении значений членов структуры после вызова функции в DLL.Я пытался преобразовать коды C ++ в C #, но я не уверен, правильно это или нет.Пожалуйста, помогите мне понять мои ошибки здесь (если есть) и как их исправить.

Моя проблема здесь в том, что я не могу правильно извлечь значения INNER STRUCTS (Union) после того, как я вызвал функцию ReceiveMessage изDLL.Как, например, m_objMsg.MsgData.StartReq.MsgID всегда равно 0. Но когда я пытаюсь использовать программу C ++ .exe, MsgID имеет правильное значение.(не 0)

C ++ Code:

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;

СпасибоВы так много заранее за свое время и помощь ...

Ответы [ 3 ]

1 голос
/ 12 апреля 2011

Акаш прав, посмотрите здесь: http://social.msdn.microsoft.com/Forums/en/csharplanguage/thread/60150e7b-665a-49a2-8e2e-2097986142f3

Другой вариант - создать две структуры и использовать соответствующий бросок, как только вы знаете, какой это тип.

hth

Mario

1 голос
/ 14 апреля 2011

В C ++ мы знаем, что все члены UNION совместно используют один и тот же кусок памяти и могут одновременно иметь только один член объекта. Чтобы реализовать это в C #, нам нужно использовать LayoutKind to Explicit и установить всю начальную точку каждого члена равной 0.

В моем предыдущем примере отображается сообщение об ошибке, указывающее, что смещение типа объекта неправильно выровнено или перекрыто необъектным типом.

Ответ: мы не можем установить для всех членов значение FieldOffSet, равное 0, поскольку не допускается объединение ссылочного типа с типом значения. - Благодаря объяснению Ганса Пассанта

Я создал копию структуры-члена UNION и изменил тип всех строковых переменных-членов на байты. Я использовал байты, так как это тип значения, поэтому я могу поместить эту структуру в 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.  

Пожалуйста, дайте мне знать, если у вас есть другой или лучший способ сделать это ... Пожалуйста, посоветуйте, если то, что я сделал, правильно или я что-то пропустил.

Спасибо большое !!! :)

1 голос
/ 12 апреля 2011

Вы должны использовать StructLayout (LayoutKind.Explicit) и FieldOffsets, чтобы создать объединение.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...