Типы импортируемых функций из DLL

Модераторы: Hawk, Romeo, Absurd, DeeJayC, WinMain

Ответить
Аватара пользователя
Decoder
Сообщения: 303
Зарегистрирован: 19 фев 2008, 23:11
Откуда: Moscow

18 апр 2009, 17:08

Всем привет!
При импорте функций из DLL без использования библиотеки импорта приходится много усилий тратить на то, чтобы объявить тип функции, потом получить адрес вызываемой функции, потом преобразовать её к нужному типу, и только потом её можно вызывать. Нельзя ли как-то упростить этот процесс? Наверняка ведь есть какие-то шаблоны, генераторы кода импорта функций и т.д.
Буду признателен за помощь.
Поумнеть несложно, куда труднее от дури избавиться.
Аватара пользователя
WinMain
Сообщения: 913
Зарегистрирован: 14 янв 2005, 10:30
Откуда: Москва
Контактная информация:

19 апр 2009, 20:00

Могу поделиться шаблоном, который сам когда-то делал...

Код: Выделить всё

[size=84][color=#0000ff][size=84][color=#0000ff]template[/color][/size][/color][/size][size=84] <[/size][size=84][color=#0000ff][size=84][color=#0000ff]typename[/color][/size][/color][/size][size=84] T> [/size][size=84][color=#0000ff][size=84][color=#0000ff]class[/color][/size][/color][/size][size=84] TFuncPtr [/size]
[size=84]{ [/size]
[size=84] T *func; [/size]
[size=84][color=#0000ff][size=84][color=#0000ff] public[/color][/size][/color][/size][size=84]: [/size]
[size=84] TFuncPtr(FARPROC pfn = NULL){ [/size]
[size=84]     SetAddr(pfn); [/size]
[size=84] } [/size]
[size=84] TFuncPtr(TFuncPtr<T> & fn) { [/size]
[size=84]     SetAddr(fn); [/size]
[size=84] } [/size]
[size=84][color=#0000ff][size=84][color=#0000ff] void[/color][/size][/color][/size][size=84] SetAddr(FARPROC pfn) { [/size]
[size=84]     func = [/size][size=84][color=#0000ff][size=84][color=#0000ff]reinterpret_cast[/color][/size][/color][/size][size=84]<T*>(pfn); [/size]
[size=84] } [/size]
[size=84] T& [/size][size=84][color=#0000ff][size=84][color=#0000ff]operator[/color][/size][/color][/size][size=84] *() [/size][size=84][color=#0000ff][size=84][color=#0000ff]const[/color][/size][/color][/size][size=84] { [/size]
[size=84][color=#0000ff][size=84][color=#0000ff]     return[/color][/size][/color][/size][size=84] *func; [/size]
[size=84] } [/size]
[size=84][color=#0000ff][size=84][color=#0000ff] void [/color][/size][/color][/size][size=84][color=#0000ff][size=84][color=#0000ff]operator[/color][/size][/color][/size][size=84] =(FARPROC pfn){ [/size]
[size=84]     SetAddr(pfn); [/size]
[size=84] } [/size]
[size=84][color=#0000ff][size=84][color=#0000ff] operator[/color][/size][/color][/size][size=84] FARPROC() [/size][size=84][color=#0000ff][size=84][color=#0000ff]const[/color][/size][/color][/size][size=84] { [/size]
[size=84][color=#0000ff][size=84][color=#0000ff]     return[/color][/size][/color][/size][size=84] func; [/size]
[size=84] } [/size]
[size=84]}; [/size]
Этот шаблон инкапсулирует указатель на вызываемую функцию и одновременно является типизатором для этой функции, т.е. тебе уже не нужно будет определять тип функции через оператор typedef.
В качестве параметра шаблона задаёшь тип вызываемой функции.
На примере функции Beep() из Kernel32.dll это будет выглядеть так:

Код: Выделить всё

HMODULE hModule = ::GetModuleHandle(_T("Kernel32.dll"));
TFuncPtr<BOOL(DWORD, DWORD)> fnBeep = ::GetProcAddr(hModule, "Beep");
(*fnBeep)(440, 500);
Для удобства загрузки/выгрузки DLL могу предложить довольно простой класс:

Код: Выделить всё

[size=84][color=#0000ff][size=84][color=#0000ff]class[/color][/size][/color][/size][size=84] CLoadLibrary [/size]
[size=84]{ [/size]
[size=84][color=#0000ff][size=84][color=#0000ff]  public[/color][/size][/color][/size][size=84]: [/size]
[size=84]  CLoadLibrary() : [/size]
[size=84]  m_hModule(NULL) [/size]
[size=84]  { [/size]
[size=84]  } [/size]
[size=84]  CLoadLibrary(LPCTSTR lpFileName) [/size]
[size=84]  { [/size]
[size=84]      m_hModule = ::LoadLibrary(lpFileName); [/size]
[size=84]  } [/size]
[size=84]  ~CLoadLibrary() [/size]
[size=84]  { [/size]
[size=84][color=#0000ff][size=84][color=#0000ff]      if[/color][/size][/color][/size][size=84] (m_hModule != NULL) [/size]
[size=84]      ::FreeLibrary(m_hModule); [/size]
[size=84]  } [/size]
[size=84][color=#0000ff][size=84][color=#0000ff]  operator[/color][/size][/color][/size][size=84] HMODULE() [/size][size=84][color=#0000ff][size=84][color=#0000ff]const[/color][/size][/color][/size]
[size=84]  { [/size]
[size=84][color=#0000ff][size=84][color=#0000ff]      return[/color][/size][/color][/size][size=84] m_hModule; [/size]
[size=84]  } [/size]
[size=84]  FARPROC GetProcAddress(LPCSTR lpProcName) [/size]
[size=84]  { [/size]
[size=84][color=#0000ff][size=84][color=#0000ff]      return[/color][/size][/color][/size][size=84] (m_hModule != NULL)? [/size]
[size=84]      ::GetProcAddress(m_hModule, lpProcName) : NULL; [/size]
[size=84]  } [/size]
[size=84]  HMODULE m_hModule; [/size]
[size=84]};[/size]
Аватара пользователя
Decoder
Сообщения: 303
Зарегистрирован: 19 фев 2008, 23:11
Откуда: Moscow

20 апр 2009, 08:25

Спасибо, WinMain. Действительно удобно, когда не нужно лишний раз использовать typedef и приводить адрес функции к заданному типу.
Ещё бы как-нибудь автоматизировать процесс кодирования самих типов для функций и получения их адреса, чтобы не вручную этот код набивать.
Поумнеть несложно, куда труднее от дури избавиться.
Аватара пользователя
WinMain
Сообщения: 913
Зарегистрирован: 14 янв 2005, 10:30
Откуда: Москва
Контактная информация:

20 апр 2009, 22:30

А в чём собственно проблема?
У тебя же есть программа Dependency walker или моя утилита EnumFunc.exe, с помощью которых ты можешь создать текстовый файл со списком экспортируемых функций для нужной тебе DLL. Потом напиши небольшую процедуру, которая будет читать этот файл и название каждой функции помещать внуть строки программного кода, например:
TFuncPtr<...> p<НАЗВАНИЕ ФУНКЦИИ>Ptr = ::GetProcAddress(hModule, "<НАЗВАНИЕ ФУНКЦИИ>");

Другой вопрос: какой тип указать для данной функции?
Я использую для этого одну хитрость: я заставляю компилятор генерировать типы для каждой из функций, правда несколько необычным способом...
Например, у тебя есть DLL и есть заголовочный файл к ней с описанием прототипов функций...

Код: Выделить всё

// Путь это будут прототипы функций, объявленные в заголовочном файле...
[size=84]BOOL HowAreYou(LPTSTR, DWORD);[/size]
[size=84]UINT Hello([/size][size=84][color=#0000ff][size=84][color=#0000ff]void[/color][/size][/color][/size][size=84]);[/size]
[size=84][color=#0000ff][size=84][color=#0000ff]void[/color][/size][/color][/size][size=84] GoodBye([/size][size=84][color=#0000ff][size=84][color=#0000ff]int[/color][/size][/color][/size][size=84]);[/size]
 
[size=84]//Дальше пишешь код, который будет вызывать ошибку компилятора:[/size]
[size=84][size=84]DWORD lpFuncs[] = // это тоже можно программно сгенерировать...[/size]
[size=84]{[/size]
[size=84]  HowAreYou,[/size]
[size=84]  Hello,[/size]
[size=84]  GoodBye[/size]
[size=84]};[/size]
[/size]
Запускаешь этот код на компиляцию, и компилятор тебе выдаст сообщения об ошибках:
error C2440: 'initializing' : cannot convert from 'BOOL (__cdecl *)(LPTSTR,DWORD)' to 'DWORD'
error C2440: 'initializing' : cannot convert from 'UINT (__cdecl *)(void)' to 'DWORD'
error C2440: 'initializing' : cannot convert from 'void (__cdecl *)(int)' to 'DWORD'
Фактически в тексте сообщений об ошибках содержатся описания типов твоих функций. Убери лишнее и считай дело сделано.
Можешь свой небольшой парсер написать, который будет выделять из строк сообщений об ошибках типы твоих функций.
Аватара пользователя
Decoder
Сообщения: 303
Зарегистрирован: 19 фев 2008, 23:11
Откуда: Moscow

21 апр 2009, 08:10

Действительно оригинально, провоцировать компилятор на сообщение об ошибке ради получения правильного кода.
Спасибо, WinMain. Воспользуюсь твоими советами.
Поумнеть несложно, куда труднее от дури избавиться.
Аватара пользователя
WinMain
Сообщения: 913
Зарегистрирован: 14 янв 2005, 10:30
Откуда: Москва
Контактная информация:

22 апр 2009, 17:00

Кстати, похожая тема уже обсуждалась на этом форуме несколько лет назад. Ради интереса можешь почитать...
http://forum.developing.ru/showthread.php?t=3476
Ответить