Member 14150748 Ответов: 2

Чтение сложной структуры из библиотеки DLL C++ в проект C#


Приветствия.

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

Один из них включает в себя указатель на Strike, который внутренне имеет другие структуры.

Это код на языке C++

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;
   [MarshalAs(UnmanagedType.Bool)]
   public bool Milli;

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

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

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

[StructLayout(LayoutKind.Sequential)]
public struct DevParam
{
   [MarshalAs(UnmanagedType.Bool)]
   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;
   }
};


Что мне нужно, так это получить структуру из C++.

Что я уже пробовал:

Я попытался добавить ссылку на структуру

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

// At main
DevParam DP; // Got ID previously
bool res = Class1.GetParameters(ID, out DP);
Console.WriteLine("Result: " + res);


Также попробовал использовать IntPtr

// 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);


В обоих случаях результат "res" истинен (означает, что функция была вызвана правильно), но я не получаю структуру. В первом решении члены структуры имеют значения по умолчанию (все 0 с числами, все false с bool), а во втором Ptr равен 0, что означает, что они не получили правильный адрес памяти.

Кто-нибудь поможет? Я работаю в этом уже несколько недель, и я полностью застрял.

Примечание: у вас нет исходного кода файла .dll
Примечание 2: у вас недостаточно опыта работы с импортом dll. Было бы неплохо объяснение "для чайников"
Примечание 3: извините за такой длинный пост

11917640 Member

В C# типа bool не равен по WinAPI типа bool. C# char не равен C++ char. Проверьте все типы в определениях PInvoke C#. Проверьте также, правильно ли работает функция GetParameters при вызове из C++.

Member 14150748

Я знаю, что есть различия. Но у меня не было проблем с другими методами (как вы можете видеть, добавил маршалы со всеми булами, возврат не имеет большого значения, я могу изменить его на int). Что касается символа, то я читал, что эквивалент-это байт. я думал объявить его как байт, но затем использовать преобразование.К с символом.

Моя главная проблема-это структура. У меня нет опыта работы с ними, не говоря уже о PInvoke (это мой первый раз, когда я его использую). Я не знаю, что еще делать, уже несколько недель пытаюсь, чувствую разочарование, усталость и почти сдаюсь (а я не могу, потому что это для работы)

11917640 Member

В PInvoke нет "маленьких" ошибок. Каждое неверное определение означает неопределенное поведение.

11917640 Member

Во втором вызове dParam указателя IntPtr ссылка должна быть dParam указателя IntPtr. Итак, много мелких ошибок в коде, проверьте все тщательно, особенно совпадение типов C# - C++ - WinAPI.

Member 14150748

Без ref результат метода ложен (и должен быть истинным). вот почему я его добавил.

просто пробовал много вариантов. Я не совсем понимаю, что делаю на самом деле. Просто нужно что-то, что работает

Member 14150748

Не берите в голову. Снял его и получил адрес.

Посмотрим, сработает ли это сейчас.

11917640 Member

Функция GetParameters имеет параметр DevParam*. ref IntPtr в C# означает, что вы передаете указатель на указатель (DevParam**). Не имеет значения, какое возвращаемое значение вы получаете сейчас, все это неопределенное поведение, если вы не исправите все ошибки в коде.

Member 14150748

Возможно, так оно и было. Теперь это работает. Спасибо <3

Member 14150748

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

2 Ответов

Рейтинг:
12

Member 14150748

А вот и решение проблемы:

// 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);


Спасибо!


Рейтинг:
0

steveb

Я не вижу нигде в вашем коде, где вы вызываете функцию GetParameters (). Кроме того, вам нужно знать точное название точки входа, и "mangledFunction" звучит неправильно (хотя я не гуру C#). если у вас нет исходного кода для DLL и если он использует искажение C++, вы можете использовать "DEPENDS.EXE" инструмент для поиска точного написания имени точки входа


Member 14150748

Извините, у меня были некоторые опечатки. Фиксированный вопрос.