Форум программистов «Весельчак У»
  *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

  • Рекомендуем проверить настройки временной зоны в вашем профиле (страница "Внешний вид форума", пункт "Часовой пояс:").
  • У нас больше нет рассылок. Если вам приходят письма от наших бывших рассылок mail.ru и subscribe.ru, то знайте, что это не мы рассылаем.
   Начало  
Наши сайты
Помощь Поиск Календарь Почта Войти Регистрация  
 
Страниц: [1]   Вниз
  Печать  
Автор Тема: Представление структур в С# и использование их при обращении к ДЛЛ  (Прочитано 7752 раз)
0 Пользователей и 1 Гость смотрят эту тему.
sabbatazh
Помогающий

ua
Offline Offline

« : 13-02-2013 17:08 » 

Здравствуйте уважаемые Знатоки!!! Прошу вашей помощи!
Застрял в попытке перевода части программы написанной в Delphi 7 на С# (а именно обращение к железу через ДЛЛ ...)
Суть такова: в Delphi 7 есть класс который получает указатель из ДЛЛ, ну и сопутствующие переменные ...
Код: (Delphi)
type
        Lusbbase = class
                // ôóíêöèè îáùåãî íàçíà÷åíèÿ äëÿ ðàáîòû ñ USB óñòðîéñòâàìè
                Function OpenLDevice(VirtualSlot : WORD) : BOOL; virtual; stdcall; abstract;
                Function CloseLDevice : BOOL; virtual; stdcall; abstract;
                Function ReleaseLInstance : BOOL; virtual; stdcall; abstract;
                // ïîëó÷åíèå äåñêðèïòîðà óñòðîéñòâà USB
                Function GetModuleHandle : THandle; virtual; stdcall; abstract;
                // ïîëó÷åíèå íàçâàíèÿ èñïîëüçóåìîãî ìîäóëÿ
                Function GetModuleName(ModuleName : pAnsiChar) : BOOL; virtual; stdcall; abstract;
                // ïîëó÷åíèå òåêóùåé ñêîðîñòè ðàáîòû øèíû USB
                Function GetUsbSpeed(UsbSpeed : pBYTE) : BOOL; virtual; stdcall; abstract;
                // óïðàâëåíèÿ ðåæèìîì íèçêîãî ýëåêòðîïîòðåáëåíèÿ ìîäóëÿ
                Function LowPowerMode(LowPowerFlag : BOOL) : BOOL; virtual; stdcall; abstract;
                // ôóíêöèÿ âûäà÷è ñòðîêè ñ ïîñëåäíåé îøèáêîé
                Function GetLastErrorInfo(LastErrorInfo : pLAST_ERROR_INFO_LUSBAPI) : BOOL; virtual; stdcall; abstract;
        end;

// ...
ILE140 = class(Lusbbase)
  public
                // ôóíêöèè äëÿ ðàáîòû ñ ÀÖÏ
                Function GET_ADC_PARS(AdcPars : pADC_PARS_E140) : BOOL; virtual; stdcall; abstract;
                Function SET_ADC_PARS(AdcPars : pADC_PARS_E140) : BOOL; virtual; stdcall; abstract;
                Function START_ADC : BOOL; virtual; stdcall; abstract;
                Function STOP_ADC : BOOL; virtual; stdcall; abstract;
                Function ADC_KADR(Data : pSHORT) : BOOL; virtual; stdcall; abstract;
                Function ADC_SAMPLE(AdcData : pSHORT; AdcChannel : WORD) : BOOL; virtual; stdcall; abstract;
                Function ReadData(ReadRequest : pIO_REQUEST_LUSBAPI) : BOOL; virtual; stdcall; abstract;

                // ôóíêöèè äëÿ ðàáîòû ñ ÖÀÏ
                Function GET_DAC_PARS(DacPars : pDAC_PARS_E140) : BOOL; virtual; stdcall; abstract;
                Function SET_DAC_PARS(DacPars : pDAC_PARS_E140) : BOOL; virtual; stdcall; abstract;
                Function START_DAC : BOOL; virtual; stdcall; abstract;
                Function STOP_DAC : BOOL; virtual; stdcall; abstract;
                Function WriteData(WriteRequest : pIO_REQUEST_LUSBAPI) : BOOL; virtual; stdcall; abstract;
                Function DAC_SAMPLE(DacData : pSHORT; DacChannel : WORD) : BOOL; virtual; stdcall; abstract;
                Function DAC_SAMPLES(DacData1, DacData2 : pSHORT) : BOOL; virtual; stdcall; abstract;

                // ôóíêöèè äëÿ ðàáîòû ñ öèôðîâûìè ëèíèÿìè
                Function ENABLE_TTL_OUT(EnableTtlOut : BOOL) : BOOL; virtual; stdcall; abstract;
                Function TTL_IN(TtlIn : pWORD) : BOOL; virtual; stdcall; abstract;
                Function TTL_OUT(TtlOut : WORD) : BOOL; virtual; stdcall; abstract;

                // ôóíêöèè äëÿ ðàáîòû ñ ïîëüçîâàòåëüñêîé èíôîðìàöèåé ÏÏÇÓ
                Function ENABLE_FLASH_WRITE(IsFlashWriteEnabled : BOOL) : BOOL; virtual; stdcall; abstract;
                Function READ_FLASH_ARRAY(UserFlash : pUSER_FLASH_E140) : BOOL; virtual; stdcall; abstract;
                Function WRITE_FLASH_ARRAY(UserFlash : pUSER_FLASH_E140) : BOOL; virtual; stdcall; abstract;

                // ôóíêöèè äëÿ ðàáîòû ñî ñëóæåáíîé èíôîðìàöèåé ÏÏÇÓ
                Function GET_MODULE_DESCRIPTION(ModuleDescription : pMODULE_DESCRIPTION_E140) : BOOL; virtual; stdcall; abstract;
                Function SAVE_MODULE_DESCRIPTION(ModuleDescription : pMODULE_DESCRIPTION_E140) : BOOL; virtual; stdcall; abstract;

                // ôóíêöèè äëÿ ïðÿìîãî äîñóïà ê ìèêðîêîíòðîëëåðó
                Function GetArray(Buffer : pBYTE; Size, Address : WORD) : BOOL; virtual; stdcall; abstract;
                Function PutArray(Buffer : pBYTE; Size, Address : WORD) : BOOL; virtual; stdcall; abstract;

        end;
        pILE140 = ^ILE140;
и это мероприятие я перенес на С#
Код: (C#)
   public abstract class _ILUSBBASE : ILUSBBASE
        {
        [MethodImpl(MethodImplOptions.Synchronized)]
                // функции общего назначения для работы с USB устройствами
        public abstract bool OpenLDevice(ushort VirtualSlot);
        public abstract bool CloseLDevice();
        public abstract bool ReleaseLInstance();
                // получение дескриптора устройства USB
        public abstract IntPtr GetModuleHandle();
                // получение названия используемого модуля
        public abstract bool GetModuleName(string ModuleName);
                // получение текущей скорости работы шины USB
        public abstract bool GetUsbSpeed( ref byte UsbSpeed);
                // управления режимом низкого электропотребления модуля
        public abstract bool LowPowerMode(bool LowPowerFlag);
                // функция выдачи строки с последней ошибкой
        public abstract bool GetLastErrorInfo(ref LAST_ERROR_INFO_LUSBAPI LastErrorInfo);
        };
    public interface ILUSBBASE { }
//-----------------------------------------------------------------------------
// интерфейс для модуля E14-140
//-----------------------------------------------------------------------------
[StructLayout(LayoutKind.Sequential)]
        public abstract class ILE140 : ILUSBBASE
        {
        [MethodImpl(MethodImplOptions.Synchronized)]
                // функции для работы с АЦП
        public abstract bool GET_ADC_PARS(ref ADC_PARS_E140 AdcPars);
        public abstract bool SET_ADC_PARS(ref ADC_PARS_E140 AdcPars);
        public abstract bool START_ADC();
        public abstract bool STOP_ADC();
        public abstract bool ADC_KADR(ref short Data);
        public abstract bool ADC_SAMPLE(ref short AdcData, ushort AdcChannel);
        public abstract bool ReadData(ref IO_REQUEST_LUSBAPI ReadRequest);

                // функции для работы с ЦАП
        public abstract bool GET_DAC_PARS(ref DAC_PARS_E140 DacPars);
        public abstract bool SET_DAC_PARS(ref DAC_PARS_E140 DacPars);
        public abstract bool START_DAC();
        public abstract bool STOP_DAC();
        public abstract bool WriteData(ref IO_REQUEST_LUSBAPI WriteRequest);
        public abstract bool DAC_SAMPLE(ref short DacData, ushort DacChannel);
        public abstract bool DAC_SAMPLES(ref short DacData1,ref short DacData2);

                // функции для работы с ТТЛ линиями
        public abstract bool ENABLE_TTL_OUT(bool EnableTtlOut);
        public abstract bool TTL_IN(ref ushort TtlIn);
        public abstract bool TTL_OUT(ushort TtlOut);

                // функции для работы с пользовательской информацией ППЗУ
        public abstract bool ENABLE_FLASH_WRITE(bool IsUserFlashWriteEnabled);
        public abstract bool READ_FLASH_ARRAY(ref USER_FLASH_E140 UserFlash);
        public abstract bool WRITE_FLASH_ARRAY(ref USER_FLASH_E140 UserFlash);

                // функции для работы со служебной информацией ППЗУ
        public abstract bool GET_MODULE_DESCRIPTION(ref MODULE_DESCRIPTION_E140 ModuleDescription);
        public abstract bool SAVE_MODULE_DESCRIPTION(ref MODULE_DESCRIPTION_E140 ModuleDescription);

                // функции для прямого досупа к микроконтроллеру
        public abstract bool GetArray(ref byte _Buffer, ushort Size, ushort Address);
        public abstract bool PutArray(ref byte _Buffer, ushort Size, ushort Address);
        };
все структуры объявлены верно!... а вот данные структуры я правильно объявил?!
потому что при обращении к ДЛЛ и C#, а есть ошибка ибо:
в Делпхи все работает на ура!
Код: (Delphi)
pModule : ILE140;
Function CreateLInstance(DeviceName : pAnsiChar) : Pointer; External 'Lusbapi.dll'
//...
pModule := CreateLInstance(pCHAR('e140'));
в С# бока:
Код: (C#)
 [DllImport("Lusbapi.dll",
                    CallingConvention = CallingConvention.StdCall,
                    CharSet = CharSet.Ansi)]
        public static extern IntPtr CreateLInstance(string DeviceName);

ILE140 pModule = null;
pModule = (ILE140)CreateLInstance("e140");// Ошибка конвертации!!!!!!! (но сама функция CreateLInstance("e140") значения возвращает!)
Подскажите что я делаю не так?!
Огромное СПАСИБО!
Записан
Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #1 : 13-02-2013 17:33 » 

sabbatazh, нельзя приводить IntPtr к структуре. В C# нет указателей. Нужно использовать класс Marshal и его методы для доступа к unmanaged памяти - он осуществит правильное копирование структуры из unmanaged памяти в managed память.
Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
sabbatazh
Помогающий

ua
Offline Offline

« Ответ #2 : 13-02-2013 18:47 » 

Dimka, а можно пример... ну просто не доходит уже!(((
Как Вы считаете правильно я составил структуры!)))
Записан
Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #3 : 13-02-2013 19:27 » 

sabbatazh, я сообщил название класса. Всё остальное элементарно находится в MSDN:

http://msdn.microsoft.com/en-us/library/4ca6d5z7.aspx

Что касается структур, то я не вижу структур. Класс вижу с методами. К DLL это отношения не имеет, если там extern "C" экспорт.
« Последнее редактирование: 13-02-2013 19:29 от Dimka » Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
sabbatazh
Помогающий

ua
Offline Offline

« Ответ #4 : 14-02-2013 08:31 » 

Dimka, Спасибо!
Вот как раз этот класс имеет отношение к ДЛЛ - ведь получает указатель, но начину ДЛЛ я не знаю ибо производитель не открыл код...
Записан
Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #5 : 14-02-2013 08:45 » 

sabbatazh, у меня такое ощущение, что это шаманство - подставить интерфейс под указатель. Каким таким волшебным образом CLR догадается, какой метод к чему привязать в unmanaged памяти?

Как импортируется DLL? Сюда код заголовочного файла, а не собственные описания.
Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
sabbatazh
Помогающий

ua
Offline Offline

« Ответ #6 : 14-02-2013 11:37 » 

Dimka, у меня этого кода нет... (((( уже привел все чем располагаю...
Записан
Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #7 : 14-02-2013 11:57 » 

sabbatazh, т.е. есть программа на Delphi и DLL? DLL загружается вручную или линкуется при помощи lib-файла?
Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #8 : 14-02-2013 18:13 » 

Суть в том, что экспорт классов из DLL - это не кросс-языковый универсальный способ.

Экспортируются только функции и глобальные переменные (возможно типизированные структурами).

В C++ некоторые функции являются членами классов. Это никак не отменяет тот факт, что они экспортируются как функции. Просто у них особый тип вызова thiscall - всем подобен stdcall, но в регистре ECX передаётся адрес экземпляра.

В C# импортировать можно только статические функции. Хотя и возможно указать CallingConvention.ThisCall и указать EntryPoint, соответствующий функции-члену. Воспользоваться этим не получится, поскольку вызов такой функции (объявленной статической в C#) не сопровождается передачей указателя на экземпляр. Указатель на экземпляр можно передать при помощи делегата, но делегат соответствует указателю на функцию (т.е. переменной с адресом функции), что не то же самое, что и экспортированная функция.

Можно загрузить DLL динамически и получить указатель на экспортируемую функцию-член, но как этот указатель превратить в делегат, у которого Target соответствует экземпляру - я не знаю. Marshal умеет делать делегаты без Target, т.е. соответствующие статическим функциям.

Ко всему прочему, экспортируемые C++ функции имеют довольно неочевидные EntryPoints. Что-то вроде "?myFunction@MyClass@@ABCDE".


В данном случае самым правильным было бы сделать DLL-обёртку на C++.NET, в которую импортировать исходную DLL со всеми нужными классами, и реализовать внутри .NET-классы-обёртки, соответствующие импортированным классам. Затем эту DLL-обёртку подключить к C#-проекту как зависимую сборку (assembly).
Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
sabbatazh
Помогающий

ua
Offline Offline

« Ответ #9 : 15-02-2013 10:06 » 

Dimka, DLL подгружается в ручную!(((
Суть в том, что экспорт классов из DLL - это не кросс-языковый универсальный способ.

Экспортируются только функции и глобальные переменные (возможно типизированные структурами).

В C++ некоторые функции являются членами классов. Это никак не отменяет тот факт, что они экспортируются как функции. Просто у них особый тип вызова thiscall - всем подобен stdcall, но в регистре ECX передаётся адрес экземпляра.

В C# импортировать можно только статические функции. Хотя и возможно указать CallingConvention.ThisCall и указать EntryPoint, соответствующий функции-члену. Воспользоваться этим не получится, поскольку вызов такой функции (объявленной статической в C#) не сопровождается передачей указателя на экземпляр. Указатель на экземпляр можно передать при помощи делегата, но делегат соответствует указателю на функцию (т.е. переменной с адресом функции), что не то же самое, что и экспортированная функция.

Можно загрузить DLL динамически и получить указатель на экспортируемую функцию-член, но как этот указатель превратить в делегат, у которого Target соответствует экземпляру - я не знаю. Marshal умеет делать делегаты без Target, т.е. соответствующие статическим функциям.

Ко всему прочему, экспортируемые C++ функции имеют довольно неочевидные EntryPoints. Что-то вроде "?myFunction@MyClass@@ABCDE".


В данном случае самым правильным было бы сделать DLL-обёртку на C++.NET, в которую импортировать исходную DLL со всеми нужными классами, и реализовать внутри .NET-классы-обёртки, соответствующие импортированным классам. Затем эту DLL-обёртку подключить к C#-проекту как зависимую сборку (assembly).
Просто думал что есть возможность на C# универсальный подход... а не городить огород...
Я вот и подумал что на С++ начинку и развязку функций сделаю... а из С# уже вызывать функции отвязанные от класса!
Спасибо!
Записан
Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #10 : 15-02-2013 10:53 » 

Цитата: sabbatazh
Я вот и подумал что на С++ начинку и развязку функций сделаю... а из С# уже вызывать функции отвязанные от класса!
Есть два подхода. Либо сделать обёртку, которая преобразует экспорт классов в экспорт структур и функций. Затем эту обёртку импортировать в C#, где уже писать обёртку из классов. Либо сразу на C++.NET делать обёртку из .NET-классов.

По трудозатратам второй вариант и дешевле, и не надо делать лишнюю прослойку из extern "C" элементов. Поэтому "развязку" делать не рекомендую, если клиент заведомо .NET'ный. Если же клиенты могут быть разными, то выгоднее потратиться на более универсальный экспортируемый интерфейс.
Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
sabbatazh
Помогающий

ua
Offline Offline

« Ответ #11 : 22-02-2013 19:50 » 

Dimka, Спасибо все работает! тема закрыта!)
Записан
Страниц: [1]   Вверх
  Печать  
 

Powered by SMF 1.1.21 | SMF © 2015, Simple Machines