Всем привет!
При импорте функций из DLL без использования библиотеки импорта приходится много усилий тратить на то, чтобы объявить тип функции, потом получить адрес вызываемой функции, потом преобразовать её к нужному типу, и только потом её можно вызывать. Нельзя ли как-то упростить этот процесс? Наверняка ведь есть какие-то шаблоны, генераторы кода импорта функций и т.д.
Буду признателен за помощь.
Типы импортируемых функций из DLL
Модераторы: Hawk, Romeo, Absurd, DeeJayC, WinMain
Поумнеть несложно, куда труднее от дури избавиться.
Могу поделиться шаблоном, который сам когда-то делал...
Этот шаблон инкапсулирует указатель на вызываемую функцию и одновременно является типизатором для этой функции, т.е. тебе уже не нужно будет определять тип функции через оператор typedef.
В качестве параметра шаблона задаёшь тип вызываемой функции.
На примере функции Beep() из Kernel32.dll это будет выглядеть так:
Для удобства загрузки/выгрузки DLL могу предложить довольно простой класс:
Код: Выделить всё
[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]
В качестве параметра шаблона задаёшь тип вызываемой функции.
На примере функции Beep() из Kernel32.dll это будет выглядеть так:
Код: Выделить всё
HMODULE hModule = ::GetModuleHandle(_T("Kernel32.dll"));
TFuncPtr<BOOL(DWORD, DWORD)> fnBeep = ::GetProcAddr(hModule, "Beep");
(*fnBeep)(440, 500);
Код: Выделить всё
[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]
Спасибо, WinMain. Действительно удобно, когда не нужно лишний раз использовать typedef и приводить адрес функции к заданному типу.
Ещё бы как-нибудь автоматизировать процесс кодирования самих типов для функций и получения их адреса, чтобы не вручную этот код набивать.
Ещё бы как-нибудь автоматизировать процесс кодирования самих типов для функций и получения их адреса, чтобы не вручную этот код набивать.
Поумнеть несложно, куда труднее от дури избавиться.
А в чём собственно проблема?
У тебя же есть программа Dependency walker или моя утилита EnumFunc.exe, с помощью которых ты можешь создать текстовый файл со списком экспортируемых функций для нужной тебе DLL. Потом напиши небольшую процедуру, которая будет читать этот файл и название каждой функции помещать внуть строки программного кода, например:
TFuncPtr<...> p<НАЗВАНИЕ ФУНКЦИИ>Ptr = ::GetProcAddress(hModule, "<НАЗВАНИЕ ФУНКЦИИ>");
Другой вопрос: какой тип указать для данной функции?
Я использую для этого одну хитрость: я заставляю компилятор генерировать типы для каждой из функций, правда несколько необычным способом...
Например, у тебя есть DLL и есть заголовочный файл к ней с описанием прототипов функций...
Запускаешь этот код на компиляцию, и компилятор тебе выдаст сообщения об ошибках:
Можешь свой небольшой парсер написать, который будет выделять из строк сообщений об ошибках типы твоих функций.
У тебя же есть программа 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'
Можешь свой небольшой парсер написать, который будет выделять из строк сообщений об ошибках типы твоих функций.
Действительно оригинально, провоцировать компилятор на сообщение об ошибке ради получения правильного кода.
Спасибо, WinMain. Воспользуюсь твоими советами.
Спасибо, WinMain. Воспользуюсь твоими советами.
Поумнеть несложно, куда труднее от дури избавиться.
Кстати, похожая тема уже обсуждалась на этом форуме несколько лет назад. Ради интереса можешь почитать...
http://forum.developing.ru/showthread.php?t=3476
http://forum.developing.ru/showthread.php?t=3476