Подтвердить что ты не робот

Динамически загружать функцию из DLL

Я немного смотрю на DLL файлы, я понимаю их использование, и я пытаюсь понять, как их использовать.

Я создал файл .dll, содержащий функцию, которая возвращает целое число с именем funci()

используя этот код, я (думаю) Я импортировал DLL файл в проект (там нет жалоб):

#include <windows.h>
#include <iostream>

int main() {
  HINSTANCE hGetProcIDDLL = LoadLibrary("C:\\Documents and Settings\\User\\Desktop  \\fgfdg\\dgdg\\test.dll");

  if (hGetProcIDDLL == NULL) {
    std::cout << "cannot locate the .dll file" << std::endl;
  } else {
    std::cout << "it has been called" << std::endl;
    return -1;
  }

  int a = funci();

  return a;
}

# funci function 

int funci() {
  return 40;
}

Однако, когда я пытаюсь скомпилировать этот .cpp файл, который, я думаю, импортировал .dll. У меня есть следующая ошибка:

C:\Documents and Settings\User\Desktop\fgfdg\onemore.cpp||In function 'int main()':|
C:\Documents and Settings\User\Desktop\fgfdg\onemore.cpp|16|error: 'funci' was not     declared in this scope|
||=== Build finished: 1 errors, 0 warnings ===|

Я знаю, что .dll отличается от файла заголовка, поэтому я знаю, что могу: t импортировать такую ​​функцию, но это лучшее, что я мог придумать, чтобы показать, что я пробовал.

Мой вопрос в том, как я могу использовать указатель "hGetProcIDDLL" для доступа к функции в DLL.

Я надеюсь, что этот вопрос имеет смысл, и я снова не лаю какое-то неправильное дерево.

4b9b3361

Ответ 1

LoadLibrary не делает то, что вы думаете. Он загружает DLL в память текущего процесса, но не магически импортирует функции, определенные в нем! Это было бы невозможно, так как вызовы функций решаются компоновщиком во время компиляции, а LoadLibrary вызывается во время выполнения (помните, что С++ - это статически типизированный).

Для получения адреса динамически загружаемых функций вам понадобится отдельная функция WinAPI: GetProcAddress.

Пример

#include <windows.h>
#include <iostream>

/* Define a function pointer for our imported
 * function.
 * This reads as "introduce the new type f_funci as the type: 
 *                pointer to a function returning an int and 
 *                taking no arguments.
 *
 * Make sure to use matching calling convention (__cdecl, __stdcall, ...)
 * with the exported function. __stdcall is the convention used by the WinAPI
 */
typedef int (__stdcall *f_funci)();

int main()
{
  HINSTANCE hGetProcIDDLL = LoadLibrary("C:\\Documents and Settings\\User\\Desktop\\test.dll");

  if (!hGetProcIDDLL) {
    std::cout << "could not load the dynamic library" << std::endl;
    return EXIT_FAILURE;
  }

  // resolve function address here
  f_funci funci = (f_funci)GetProcAddress(hGetProcIDDLL, "funci");
  if (!funci) {
    std::cout << "could not locate the function" << std::endl;
    return EXIT_FAILURE;
  }

  std::cout << "funci() returned " << funci() << std::endl;

  return EXIT_SUCCESS;
}

Кроме того, вы должны экспортировать свою функцию из DLL правильно. Это можно сделать следующим образом:

int __declspec(dllexport) __stdcall funci() {
   // ...
}

Как отмечает Лундин, хорошая практика освободить дескриптор библиотеки, если вам это больше не нужно. Это приведет к его разгрузке, если никакой другой процесс по-прежнему не содержит дескриптор одной библиотеки DLL.

Ответ 2

В дополнение к уже опубликованному ответу, я думал, что должен поделиться удобным трюком, который я использую для загрузки всех функций 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" к именам: я хотел, чтобы они отличались от фактических имен функций. Теперь мы можем сгладить синтаксис 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 и небезопасны. Формально, что я здесь делаю, это поведение undefined. Однако в мире Windows указатели на функции всегда имеют одинаковый размер независимо от их типа, а преобразования между ними предсказуемы в любой версии Windows, которую я использовал.

Кроме того, теоретически может быть добавление, добавленное в union/struct, что приведет к сбою всего. Однако указатели имеют тот же размер, что и требование выравнивания в Windows. A static_assert, чтобы гарантировать, что struct/union не имеет отступов, может быть еще в порядке.