В дополнение к уже опубликованному ответу я подумал, что должен поделиться удобным трюком, который я использую для загрузки всех функций DLL в программу через указатели функций, без написания отдельного вызова GetProcAddress для каждой функции.Мне также нравится вызывать функции напрямую, как это делается в операторе.
Начните с определения общего указателя типа функции:
typedef int (__stdcall* func_ptr_t)();
Какие используемые типы не очень важны.Теперь создайте массив этого типа, который соответствует количеству функций, которые у вас есть в DLL:
func_ptr_t func_ptr [DLL_FUNCTIONS_N];
В этом массиве мы можем хранить фактические указатели функций, которые указывают в пространство памяти DLL.
Следующая проблема заключается в том, что GetProcAddress
ожидает имена функций в виде строк.Поэтому создайте похожий массив, состоящий из имен функций в DLL:
const char* DLL_FUNCTION_NAMES [DLL_FUNCTIONS_N] =
{
"dll_add",
"dll_subtract",
"dll_do_stuff",
...
};
Теперь мы можем легко вызывать GetProcAddress () в цикле и сохранять каждую функцию внутри этого массива:
for(int i=0; i<DLL_FUNCTIONS_N; i++)
{
func_ptr[i] = GetProcAddress(hinst_mydll, DLL_FUNCTION_NAMES[i]);
if(func_ptr[i] == NULL)
{
// error handling, most likely you have to terminate the program here
}
}
Если цикл был успешным, единственная проблема, которую мы имеем сейчас, это вызов функций.Указатель на функцию typedef из ранее не помог, потому что каждая функция будет иметь свою собственную подпись.Эту проблему можно решить, создав структуру со всеми типами функций:
typedef struct
{
int (__stdcall* dll_add_ptr)(int, int);
int (__stdcall* dll_subtract_ptr)(int, int);
void (__stdcall* dll_do_stuff_ptr)(something);
...
} functions_struct;
И, наконец, чтобы подключить их к массиву ранее, создайте объединение:
typedef union
{
functions_struct by_type;
func_ptr_t func_ptr [DLL_FUNCTIONS_N];
} functions_union;
Теперь выМожно загрузить все функции из DLL с помощью удобного цикла, но вызывать их через член объединения by_type
.
Но, конечно, немного обременительно печатать что-то вроде
functions.by_type.dll_add_ptr(1, 1);
всякий раз, когда вы хотите вызвать функцию.
Как оказалось, именно поэтому я добавил к именам постфикс "ptr": я хотел, чтобы они отличались от реальных имен функций.Теперь мы можем сгладить синтаксис icky struct и получить нужные имена, используя несколько макросов:
#define dll_add (functions.by_type.dll_add_ptr)
#define dll_subtract (functions.by_type.dll_subtract_ptr)
#define dll_do_stuff (functions.by_type.dll_do_stuff_ptr)
И вуаля, теперь вы можете использовать имена функций с правильным типом и параметрами, как будто онибыли статически связаны с вашим проектом:
int result = dll_add(1, 1);
Отказ от ответственности: Строго говоря, преобразования между различными указателями функций не определены стандартом C и небезопасны.Итак, формально, то, что я делаю здесь, это неопределенное поведение.Однако в мире Windows указатели на функции всегда имеют одинаковый размер, независимо от их типа, и преобразования между ними предсказуемы в любой версии Windows, которую я использовал.
Кроме того, теоретически может быть заполнениевставляется в объединение / структуру, что может привести к сбою.Однако указатели имеют тот же размер, что и требование выравнивания в Windows.static_assert
, чтобы гарантировать, что структура / объединение не имеет отступов, может быть в порядке.