Получение сложной структуры из C ++ в C # - PullRequest
2 голосов
/ 01 апреля 2019

Дней назад я задал этот вопрос: Передача сложного Struct (с внутренним массивом struct) из C # в C ++

К счастью, ответ таков, и код, кажется, работает.

Теперь мне нужно сделать противоположный случай, я имею в виду, мне нужно получить структуру из DLL C ++ в мой код C #.

Я исследовал этот сайт, пытаясь использовать тип IntPtr, но он не работал. Затем, поскольку моя структура в C # была определена правильно, я попытался использовать ссылку out.

В качестве общего, собираюсь перепостить структуры, определенные на обоих языках

typedef struct _ImParam
{
  UINT Format;
  UINT Resolution;
  UINT ColorDepth;
} IM_PARAM;

typedef struct _sValues
{
  UINT Xpos;
  UINT Ypos;
  UINT Width;
  UINT Height;
  BOOL Milli; 
} S_VALUES;

typedef struct _sProperties
{
  BOOL Enable;
  S_VALUES Properties;
} S_PROPERTIES;

typedef struct _DevParam
{
  BOOL Enable;
  UINT Font;
  char Symbol;
  IM_PARAM Image1;
  IM_PARAM Image2;
  S_PROPERTIES Properties[10];
  UINT FeedMode;
} DevParam;

// more code, comments, etc. etc.

// The function I want to use
BOOL GetParameters( DWORD ID, DevParam  *dParam );

Вот как я строю структуры в C #

[StructLayout(LayoutKind.Sequential)]
public struct ImParam
{
   public uint Format;
   public uint Resolution;
   public uint ColorDepth;

   public ImParam(uint n)
   {
       Format = n;
       Resolution = 300;
       ColorDepth = 256;
   }
};

[StructLayout(LayoutKind.Sequential)]
public struct sValues
{
   public uint Xpos;
   public uint Ypos;
   public uint Width;
   public uint Height;
   public bool Milli;

   public sValues(uint n)
   {
       Xpos = n;
       Ypos = n;
       Width = n;
       Height = n;
       Milli = false;
   }
};

[StructLayout(LayoutKind.Sequential)]
public struct sProperties
{
   public bool Enable;
   public sValues Properties;

   public sProperties(int n)
   {
       Enable = false;
       Properties = new sValues(n);
   }
};

[StructLayout(LayoutKind.Sequential)]
public struct DevParam
{
   public bool Enable;
   public uint Font;
   public char Symbol;
   public ImParam Image1;
   public ImParam Image2;
   [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
   public sProperties[] Properties;
   public uint FeedMode;

   public DeviceParameters(int n)
   {
       Enable = true;
       Font = 0;
       Symbol = '?';
       Image1 = new ImParam(3);
       Image2 = new ImParam(3);
       Properties = new sProperties[10];
       for(int i = 0; i < 10; i++)
          Properties[i] = new sProperties(n);
       FeedMode = 1;
   }
};

// This is the method imported from the C++ dll
[DllImport(path, EntryPoint = "?GetParameters@@YGHKPAU_DevParam@@@Z")]
public static extern bool GetParameters(int ID, out DevParam dParam);

А вот и звонок

// Already got ID from somewhere else
DevParam DP;
bool res = Class1.GetParameters(ID, out DP);
Console.WriteLine("Result: " + res);

Код работает, так как в результате я получаю "true". Проблема в том, что он получает неправильные значения в членах структуры, помещая значения по умолчанию (всегда 0 чисел, всегда ложные логические значения), даже если раньше я использовал метод SetParam (..) (и я знаю, что он работает, потому что когда я изменить формат изображения сканер снижает скорость сканирования).

Что мне не хватает?

Примечание: у меня нет исходного кода .dll


Отредактировано:

Пробовал с этими модификациями:

// At the dll wrapper class
[DllImport(path, EntryPoint = "?GetParameters@@YGHKPAU_DevParam@@@Z")]
public static extern bool GetParameters(int ID, ref IntPtr dParam);

// At main
int size = Marshal.SizeOf(typeof(DevParam));
IntPtr Ptr = Marshal.AllocHGlobal(size);
bool res = Class1.GetParameters(ID, ref Ptr);
Console.WriteLine("Result: " + res);
var test = Marshal.PtrToStructure(Ptr, typeof(DevParam));
// No idea what I'll do here
Marshal.FreeHGlobal(Ptr);

Если я попытаюсь напечатать «test», он должен дать адрес, так как это указатель, но он нулевой. Никто не знает, как я могу извлечь данные из структуры.

Есть идеи?

1 Ответ

0 голосов
/ 02 апреля 2019

Решено!

// At the dll wrapper class
[DllImport(path, EntryPoint = "?GetParameters@@YGHKPAU_DevParam@@@Z")]
public static extern bool GetParameters(int ID, IntPtr dParam);

// At main
int size = Marshal.SizeOf(typeof(DevParam));
IntPtr Ptr = Marshal.AllocHGlobal(size);
bool res = Class1.GetParameters(ID, Ptr);
DevParam test = (DevParam)Marshal.PtrToStructure(Ptr, typeof(DevParam));

// For testing purpoises, previously changed the default values with another method
Console.WriteLine(test.Enable);

Marshal.FreeHGlobal(Ptr);

Пришлось удалить ключевое слово ref.

...