2013-11-28 2 views
3

im неспособный определить правильный код C# для работы с библиотекой C++, которая определяет сложную структуру с объединением и массивами, я продолжаю получать некоторое исключение памяти при выполнении кода на C++, и im im sure из-за этого.C# pinvoke structs with union и массивы

С структура ++ выглядит следующим образом, игнорируя перечислений и другое определение структуры (я буду размещать их в случае необходимости, но они довольно extense)

typedef struct 
{ 
DG_CCTALK_APP_EVT_CODE eEventCode; 
DG_CCTALK_APP_EVT_TYPE eEventType; 
int   iTime; 
int   iHandle; 
unsigned char  uchAddress; 
DG_CCTALK_BILL_INFO sBillInfo; 
int   iBillAmount; 
union 
{ 
    long  lData; 
    int  iData; 
    unsigned char ucArray[128]; 
    char  *cString; 
    void  *pvoid; 
} uData; 
void   *_private; // for use by cctalk app layer 
} DG_CCTALK_APP_EVT, *PDG_CCTALK_APP_EVT; 

И C# код заключается в следующем:

[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Explicit)] 
    public struct Anonymous_92d21a81_2a8b_42f4_af32_f7606aa9cf40 
    { 
     //deberian llevar todos 0, pero el array genera problemas al ponerle 0, y cambiandolo explota en otro lado... 

     /// int 
     [System.Runtime.InteropServices.FieldOffsetAttribute(0)] 
     public int lData; 

     /// int 
     [System.Runtime.InteropServices.FieldOffsetAttribute(0)] 
     public int iData; 

     /// unsigned char[128] 
     [System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.ByValArray, SizeConst = 128, ArraySubType = System.Runtime.InteropServices.UnmanagedType.I1)] 
     [System.Runtime.InteropServices.FieldOffsetAttribute(4)] 
     public byte[] ucArray; 

     /// char* 
     [System.Runtime.InteropServices.FieldOffsetAttribute(0)] 
     public System.IntPtr cString; 

     /// void* 
     [System.Runtime.InteropServices.FieldOffsetAttribute(0)] 
     public System.IntPtr pvoid; 
    } 

    [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] 
    public struct DG_CCTALK_APP_EVT 
    { 

     /// DG_CCTALK_APP_EVT_CODE->_DG_CCTALK_APP_EVT_CODE 
     public DG_CCTALK_APP_EVT_CODE eEventCode; 

     /// DG_CCTALK_APP_EVT_TYPE->_DG_CCTALK_APP_EVT_TYPE 
     public DG_CCTALK_APP_EVT_TYPE eEventType; 

     /// int 
     public int iTime; 

     /// int 
     public int iHandle; 

     /// unsigned char 
     public byte uchAddress; 

     /// DG_CCTALK_BILL_INFO->_DG_CCTALK_BILL_INFO 
     public DG_CCTALK_BILL_INFO sBillInfo; 

     /// int 
     public int iBillAmount; 

     /// Anonymous_92d21a81_2a8b_42f4_af32_f7606aa9cf40 
     public Anonymous_92d21a81_2a8b_42f4_af32_f7606aa9cf40 uData; 

     /// void* 
     public System.IntPtr _private; 
    } 

Делегаты и функция импорта

public delegate void DG_CCTALK_APP_EVT_HANDLER(ref DG_CCTALK_APP_EVT pEVT); 

[System.Runtime.InteropServices.DllImportAttribute("cctalk.dll", EntryPoint = "cctalk_app_enable_device", CallingConvention = CallingConvention.Cdecl)] 
public static extern int cctalk_app_enable_device(ref DG_CCTALK_APP_EVT param0); 

UPDATE: новый код после asnwer, до сих пор не приста king: Ошибка: «Попытка чтения или записи защищенной памяти Это часто указывает на то, что другая память повреждена»

Что странно, я могу использовать структуру при первом вызове события, я распечатываю все поля и они кажутся правильными (lData и iData имеют одинаковые значения, cData имеет строку, которую я отправил), но после окончания события программа умирает. Похоже, мой C# код нарушающего структуры или что-то ...

[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential, CharSet = System.Runtime.InteropServices.CharSet.Ansi)] 
    public struct DG_CCTALK_APP_EVT 
    { 

     /// DG_CCTALK_APP_EVT_CODE->_DG_CCTALK_APP_EVT_CODE 
     public DG_CCTALK_APP_EVT_CODE eEventCode; 

     /// DG_CCTALK_APP_EVT_TYPE->_DG_CCTALK_APP_EVT_TYPE 
     public DG_CCTALK_APP_EVT_TYPE eEventType; 

     /// int 
     public int iTime; 

     /// int 
     public int iHandle; 

     /// unsigned char 
     public byte uchAddress; 

     /// DG_CCTALK_BILL_INFO->_DG_CCTALK_BILL_INFO 
     public DG_CCTALK_BILL_INFO sBillInfo; 

     /// int    
     public int iBillAmount;    

     [MarshalAs(UnmanagedType.ByValArray, SizeConst = 128)] 
     public byte[] uData; 

     #region setters y getters para mapeo 

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

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

     public byte[] ucArray 
     { 
      set 
      { 
       Array.Copy(value, uData, 128); 
      } 
      get 
      { 
       return uData; 

      } 
     } 

     public IntPtr cString 
     { 
      set 
      { 
       Array.Copy(BitConverter.GetBytes(value.ToInt32()), uData, 32); 
      } 
      get 
      { 
       return (IntPtr)BitConverter.ToInt32(uData, 0); 
      } 
     } 

     public IntPtr pvoid 
     { 
      set 
      { 
       Array.Copy(BitConverter.GetBytes(value.ToInt32()), uData, 32); 
      } 
      get 
      { 
       return (IntPtr)BitConverter.ToInt32(uData, 0); 
      } 
     } 


     #endregion 

     /// void* 
     public System.IntPtr _private; 
    } 

C++ декларации:

typedef void (*DG_CCTALK_APP_EVT_HANDLER)(PDG_CCTALK_APP_EVT pEVT); 

DGCCTALK_PREFIX DG_ERROR cctalk_app_init(DG_CCTALK_APP_EVT_HANDLER pfnHandler); 
DGCCTALK_PREFIX DG_ERROR cctalk_app_add_link(char *szPortName); 

Примечание папа DGCCTALK_PREFIX определяется как __declspec (dllexport)

C# код для тех, кто:

public delegate void DG_CCTALK_APP_EVT_HANDLER(ref DG_CCTALK_APP_EVT pEVT); 

[System.Runtime.InteropServices.DllImportAttribute("cctalk.dll", EntryPoint =  "cctalk_app_init", CallingConvention = CallingConvention.Cdecl)] 
public static extern int cctalk_app_init(DG_CCTALK_APP_EVT_HANDLER pfnHandler); 

[System.Runtime.InteropServices.DllImportAttribute("cctalk.dll", EntryPoint = "cctalk_app_add_link", CallingConvention = CallingConvention.Cdecl)] 
public static extern int cctalk_app_add_link(System.IntPtr szPortName); 

C# код вызова их

var handler = new DG_CCTALK_APP_EVT_HANDLER(this._handler); 
var resultado = cctalk_app_init(handler); 
cctalk_app_add_link(Marshal.StringToHGlobalAnsi("port=" + port)); 

C# заголовок обработчика

private void _handler(ref DG_CCTALK_APP_EVT pEVT) 
+1

Зачем использовать смещение 4? И какова ошибка? Как вы называете эту функцию? –

+0

Первоначально оно было 0, но это вызвало бы недопустимое исключение aling. У меня здесь нет ошибки, но речь идет о доступе к памяти, в которой у программы не было разрешения на чтение/запись. – user1777914

+0

Также я не называю метод с этой структурой явным образом, это событие, запущенное из библиотеки C++, которое отправляет мне эту структуру (здесь нет ошибки), но после завершения события, когда код C++ запускается снова, он прерывается. – user1777914

ответ

3

Как вы выяснили, маршалисту не нравится, когда вы пытаетесь наложить массив на другие поля в вашем переводе союза. Вы подавили проблему, изменив смещение для массива как 4, а не 0. Но это не помогает.

Теперь, поскольку маршалер не будет иметь дело с профсоюзом для вас, вам придется делать это вручную. Я бы справился с этим, опуская не-массивные члены объединения.

[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; 
} 

Обратите внимание, что я удалил большую часть подробностей вашей декларации.Я подозреваю, что тип автоматически генерируется инструментом. Но все это многословие делает его очень трудным для чтения.

Это приведет к правильной компоновке структуры, но оставит вас с работой, чтобы сделать доступ к другим полям в объединении.

Итак, как мы можем прочитать эти поля? Ну, данные содержатся в байтовом массиве uData, так что это просто вопрос чтения значений оттуда. Например, можно добавить следующее свойство в DG_CCTALK_APP_EVT структуры:

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. 

Обратите внимание, что сеттер будет стерта, но все первые 4 байта массива, так как он переписывает uData. Я не знаю достаточно о протоколе собственного кода, чтобы убедиться, что это то, что вы хотите. Если это не то, что вы хотите, то вы можете написать сеттер так:

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

Вы теперь добавили ++ сторону C границы-взаимодействия. Наиболее очевидная проблема заключается в том, что у вашего делегата C# есть неправильное соглашение о вызове. Он использует stdcall, но код C++ ожидает cdecl. Этого достаточно, чтобы объяснить катастрофический крах после возврата вашего обратного вызова. Вам нужно убедиться, что ваш делегат указывает соглашение о вызове.

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

Ваш перевод cctalk_app_add_link это тоже неправильно. Оно должно быть:

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

Done, как это вы можете просто передать строку и, таким образом, избежать утечки памяти с вашей текущей реализации.

+0

Вы устанавливаете переменную SizeConst на 128, но это будет содержать только массив, определенный в объединении uData, а как насчет других полей? Разве это не должно быть больше? 4 байта для lData (или 8, так как он длинный?) | 4 байта для iData 128 байтов для ucArray 4 байта для cString (или 8 при работе на 64-битной машине, поскольку это указатель) 4 байта для pvoid (как указано выше) – user1777914

+0

Я обновил свой вопрос с помощью ur ответ, но он все еще не работает, я действительно потерялся здесь. – user1777914

+0

Нет, союз накладывает элементы. Следовательно, FieldOffset 0 для всех членов, созданных вашим инструментом. Я не совсем уверен, что означает «не работает». Вы же не делали то, что я сказал. Ваш код имеет 144, а не 128. –

-1

Проверьте Int и длинные поля. Я думаю, что C ИНТ 16 бит в то время как C# ИНТ 32:

  • Если это правда, то iTime, iHandle и iBilAmount определяются как межд как в C# и C определений.
  • Если я не прав, то lData определяется как долго в C, но Int в C# (если это проблема, то не забудьте исправить смещения полей, которые появляются после того, как lData).
+1

Нет. В Windows, C++ int 32 бит –

+0

Это союз! Все смещения равны 0. Никаких исправлений не требуется. В C++ для Windows, int и long имеют ширину 4 байта. –

+0

Извините, вы правы. Я пропустил массив длиной 128 и не видел, что он использует Windows. – MaMazav

Смежные вопросы