Передача сложной структуры (с внутренним массивом struct) из C # в C

#c# #c #arrays #struct #interop

#c# #c #массивы #структура #взаимодействие

Вопрос:

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

Я получил методы dll, используя командную строку, разделив их на веб-сайте (поскольку у него 100). Конечно, я не буду использовать их все, только те, которые мне нужны. У меня не было проблем с теми, которые использовали примитивные типы данных, фактически, заставляли сканер включаться / выключаться, сканировать и тому подобное.

Моя основная проблема заключается в следующем: мне нужно установить некоторые параметры, потому что с параметрами по умолчанию я не получаю необходимую информацию (и это САМОЕ важное, что мне нужно, на самом деле). Единственный способ сделать это — использовать метод, который включает в себя 2 параметра: идентификатор (просто int) и настройку (struct). Эта структура имеет внутри не один, а два разных экземпляра structs (и один из них является массивом внутри структуры другого типа в качестве одного из параметров). Другими словами, нам нужно будет работать с 4 разными структурами.

Я объявил все свои структуры, следуя шаблону, предоставленному в файле .h, и импортировал метод. Когда я пытаюсь выполнить тест, он продолжает выдавать мне ошибку. Я полагаю, что проблема заключается в массиве структур. Я пробовал обычную передачу, маршалинг, использование pin-кода для массива, изменение типа данных, добавление «MarshalAs» со всеми переменными bool … кажется, ничего не работает.

Пытаюсь решить это уже несколько дней. Не знаю, что я делаю не так (потому что это мой первый раз, когда я импортирую методы). Я читал о C / Cli, но с этим тоже никогда не работал.

Смотрите код ниже (я немного изменил из-за конфиденциальности информации)

Это то, как определено в файле .h (C )

 // The structs (won't add all parameters, but are basically the same type)
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.

// This is how is defined
BOOL SetParameters( DWORD ID, DevParams 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;
       Front = false;
       Properties = new sValues(n);
   }
};

// Commented code is from another attemp
[StructLayout(LayoutKind.Sequential)]
public struct DevParam
{
   public bool Enable;
   public uint Font;
   public char Symbol;
   public ImParam Image1;
   public ImParam Image2;
   public IntPtr Properties;
   //public sProperties[] Properties;
   public uint FeedMode;

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

// .dll file path definition, some methods imported, etc. etc.
[DllImport(path, EntryPoint = "?SetParameters@@YGHKU_DevParam@@@Z")]
public static extern bool SetParameters(int ID, DevParam dParam);
  

И вот когда я ее вызываю (добавлен код с комментариями, чтобы показать вам мои попытки)

 static void Main(string[] args)
{
    bool res = false;
    int ID;
    sProperties[] SnP = new sProperties[10];
    for (int i = 0; i < 10; i  )
        SnP[i] = new sProperties(0);

    try
    {
        // Some code to turn on scanner, get ID value and such

        /* Attemp1: Passing the struct normaly.
         * Result: ArgumentException [HRESULT: 0x80070057 (E_INVALIDARG))]
         * try
         * {
         *     DevParam dParam = new DevParam(10);
         *     res = Class1.SetParameters(ID, dParam);
         *     Console.WriteLine(res);
         * }
         * catch (Exception e) { Console.WriteLine(e); }*/

        /* Attemp2: Marshaling each element of the array.
         * Result: The managed PInvoke signature doesnt mach the destination one
         * int S = Marshal.SizeOf(typeof(sProperties));
         * DevParam dParam = new DevParam(Marshal.AllocHGlobal(SnP.Length*S));
         * IntPtr ptr = dParam.Properties;
         * for (int i = 0; i < SnP.Length; i  )
         * {
         *     Marshal.StructureToPtr(SnP[i], ptr, false);
         *     ptr  = S;
         * }
         * try
         * {
         *     res = Class1.SetDevParam(ID, dParam);
         *     Console.WriteLine(res);
         * }
         * finally { Marshal.FreeHGlobal(dParam.sProperties); }*/

         /* Attemp3: Adding a Pin Pointer to struct
          * Result: Exception (Object has no primitive data and it can't
          *     be transfered into bits blocks) */
         GCHandle SpHandle = GCHandle.Alloc(SnP, GCHandleType.Pinned);
         try
         {
             DevParam dParam = new DevParam(SpHandle.AddrOfPinnedObject());
             res = Class1.SetParameters(ID, dParam);
             Console.WriteLine(res);
         }
         catch (Exception e) { Console.WriteLine(e); }
         finally { SpHandle.Free(); }

         // More code for testing other methods and blahblahblah
     }
     catch (Exception e) { Console.WriteLine(e); }
     Console.WriteLine("Finished");
     Console.ReadKey();
}
  

Чего я ожидаю? Получение просто логического результата, чтобы увидеть, успешно ли выполнен метод (и, конечно, если true, сканер должен был определить новые параметры)

Что я получаю? Куча исключений.

Пожалуйста, любая помощь была бы отличной. Заранее спасибо.

PD: Извините за этот длинный пост. PD2: Я довольно крутой, поэтому, пожалуйста, попробуйте объяснить это «для чайников»

Комментарии:

1. Здравствуйте и добро пожаловать в StackOverflow. Это действительно длинный пост, но, вероятно, было бы полезно опубликовать, по крайней мере, первое исключение, которое вы получаете.

2. Встроенный массив не является IntPtr. Просто используйте обычный массив[]. Для правильной маршализации требуется [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)].

3. Спасибо Педро. Эти исключения указаны в комментариях. Первый…. Это похоже на это (так говорит система)

4. Привет, Ганс. Ожидал, что вы ответите. Собираюсь попробовать с этим. Возможно, адаптация кода «Attempt2»? Я попробую завтра на работе и дам вам знать. Спасибо за отзыв

Ответ №1:

Спасибо Гансу. Кажется, это сработало!

Просто модифицировал структуру, как было предложено:

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

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

И использовал код «Attempt1».