Nice programing

DLL에서 동적으로 함수로드

nicepro 2020. 10. 18. 19:33
반응형

DLL에서 동적으로 함수로드


.dll 파일을 조금 살펴보고 사용 방법을 이해하고 사용 방법을 이해하려고합니다.

funci ()라는 정수를 반환하는 함수가 포함 된 .dll 파일을 만들었습니다.

이 코드를 사용하여 .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;
}

그러나 .dll을 가져온 것으로 생각되는이 .cpp 파일을 컴파일하려고 할 때 다음 오류가 발생합니다.

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이 헤더 파일과 다르다는 것을 알고 있으므로 이와 같은 함수를 가져올 수는 없지만 시도한 것을 보여줄 수있는 최선의 방법입니다.

내 질문은 "hGetProcIDDLL"포인터를 사용하여 .dll 내의 함수에 액세스하는 방법입니다.

이 질문이 의미가 있기를 바라며, 아직 잘못된 나무를 짖지 않습니다.


LoadLibrary당신이 생각하는대로하지 않습니다. 현재 프로세스의 메모리에 DLL을로드하지만 정의 된 함수를 마술처럼 가져 오지는 않습니다 ! 함수 호출은 LoadLibrary런타임에 호출되는 동안 컴파일 타임에 링커에 의해 해결되기 때문에 가능하지 않습니다 (C ++는 정적으로 형식화 된 언어 임을 기억하십시오 ).

동적으로로드 된 함수의 주소를 가져 오려면 별도의 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() {
   // ...
}

As Lundin notes, it's good practice to free the handle to the library if you don't need them it longer. This will cause it to get unloaded if no other process still holds a handle to the same DLL.


In addition to the already posted answer, I thought I should share a handy trick I use to load all the DLL functions into the program through function pointers, without writing a separate GetProcAddress call for each and every function. I also like to call the functions directly as attempted in the OP.

Start by defining a generic function pointer type:

typedef int (__stdcall* func_ptr_t)();

What types that are used aren't really important. Now create an array of that type, which corresponds to the amount of functions you have in the DLL:

func_ptr_t func_ptr [DLL_FUNCTIONS_N];

In this array we can store the actual function pointers that point into the DLL memory space.

Next problem is that GetProcAddress expects the function names as strings. So create a similar array consisting of the function names in the DLL:

const char* DLL_FUNCTION_NAMES [DLL_FUNCTIONS_N] = 
{
  "dll_add",
  "dll_subtract",
  "dll_do_stuff",
  ...
};

Now we can easily call GetProcAddress() in a loop and store each function inside that array:

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
  }
}

If the loop was successful, the only problem we have now is calling the functions. The function pointer typedef from earlier isn't helpful, because each function will have its own signature. This can be solved by creating a struct with all the function types:

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;

And finally, to connect these to the array from before, create a union:

typedef union
{
  functions_struct  by_type;
  func_ptr_t        func_ptr [DLL_FUNCTIONS_N];
} functions_union;

Now you can load all the functions from the DLL with the convenient loop, but call them through the by_type union member.

But of course, it is a bit burdensome to type out something like

functions.by_type.dll_add_ptr(1, 1); whenever you want to call a function.

As it turns out, this is the reason why I added the "ptr" postfix to the names: I wanted to keep them different from the actual function names. We can now smooth out the icky struct syntax and get the desired names, by using some macros:

#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)

And voilà, you can now use the function names, with the correct type and parameters, as if they were statically linked to your project:

int result = dll_add(1, 1);

Disclaimer: Strictly speaking, conversions between different function pointers are not defined by the C standard and not safe. So formally, what I'm doing here is undefined behavior. However, in the Windows world, function pointers are always of the same size no matter their type and the conversions between them are predictable on any version of Windows I've used.

Also, there might in theory be padding inserted in the union/struct, which would cause everything to fail. However, pointers happen to be of the same size as the alignment requirement in Windows. A static_assert to ensure that the struct/union has no padding might be in order still.

참고URL : https://stackoverflow.com/questions/8696653/dynamically-load-a-function-from-a-dll

반응형