В дополнение к уже опубликованному ответу, я подумал, что должен использовать удобный трюк, который я использую для загрузки всех функций DLL в программу с помощью указателей функций, без написания отдельного вызова GetProcAddress для каждой функции. Я также хотел бы вызвать функции непосредственно, как попытки в OP.
Начните с определением общего типа указателя функции:
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» к именам: я хотел, чтобы они отличались от фактических имен функций. Теперь мы можем сгладить неприглядный синтаксис структуры и получить нужные имена, используя некоторые макросы:
#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, которую я использовал.
Также теоретически может быть дополнением, вставленным в union/struct, что приведет к сбою всего. Однако указатели имеют тот же размер, что и требование выравнивания в Windows. A static_assert
, чтобы гарантировать, что структура/объединение не имеет отступов, может быть еще в порядке.
поиск статических/динамических ссылок. –
Спасибо, я посмотрю на это –
Я вставляю свой код, но когда я вталкиваю его сюда, формат беспорядочен, поэтому я в конечном итоге отступаю все на 4 строки. –