Чтение сложной структуры из библиотеки 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
Кажется, это сработало! Спасибо!