如何动态获取dll中的导出函数地址(windows版本)

  在使用第三方库的时候,一般会提供两种格式的库文件:静态库(lib)和动态库(dll)。在使用动态库的时候,需要这个dll和头文件外,还需要一个lib文件(该文件和静态lib不同,其只是包含了dll中导出函数的符号);但是如果只有这个dll库文件,我们该如何办呢?这就涉及到Windows动态加载相关知识,感兴趣的同学可以阅读《Windows核心编程》这本书,里面详细介绍了相关的知识点。下面主要结合github上一个开源项目进行介绍,了解和掌握如何用Windows API实现动态加载并且通过泛型实现不同导出函数地址的加载。

一、如何通过泛型编程动态获取指定dll导出函数地址

  直接上代码,这里的源代码参考:not-enough-standards/shared_library.hpp at master · Alairion/not-enough-standards (github.com),我提取了其中Windows部分实现如下,并对相关泛型知识点做了备注。

 1 #include <Windows.h>
 2 #include <string>
 3 #include <cassert>
 4 
 5 class SharedLibrary
 6 {
 7 public:
 8     using NativeHandleType = HMODULE;
 9 
10 public:
11     explicit SharedLibrary(const std::wstring& path)
12     {
13         _handle = ::LoadLibraryW(path.c_str());
14     }
15 
16     ~SharedLibrary()
17     {
18         if (_handle) {
19             ::FreeLibrary(_handle);
20             _handle = NULL;
21         }
22     }
23 
24     SharedLibrary(const SharedLibrary&) = delete;
25     SharedLibrary& operator= (const SharedLibrary&) = delete;
26 
27     SharedLibrary(SharedLibrary&& t) noexcept
28     {
29         _handle = std::exchange(t._handle, NativeHandleType{});
30     }
31 
32 
33     SharedLibrary& operator= (SharedLibrary&& t) noexcept
34     {
35         _handle = std::exchange(t._handle, NativeHandleType{});
36 
37         return *this;
38     }
39 
40     template<typename Func, 
41         typename = std::enable_if_t<std::is_pointer_v<Func>&& std::is_function_v<std::remove_pointer_t<Func>>>>
42         // template< bool B, class T = void >
43         // using enable_if_t = typename enable_if<B, T>::type;
44         // 作用是在模版实例化的时候检测Func是否是指针类型并且去掉指针后释放满足函数定义
45     Func Load(const std::string& symbol)
46     {
47         // std::empty since c++17
48         // std::data since c++17
49         assert(!std::empty(symbol) && "load shared library symbol is empty.");
50         assert(_handle && "shared libray handle is invaild.");
51         auto unameFn = reinterpret_cast<void(*)()>(::GetProcAddress(_handle, std::data(symbol)));
52         return reinterpret_cast<Func>(unameFn);
53     }
54 
55     template<typename Func, typename = std::enable_if_t<std::is_function_v<Func>>>
56     // template< bool B, class T = void >
57     // using enable_if_t = typename enable_if<B, T>::type;
58     // 作用是在模版实例化的时候检测Func是否满足函数的定义
59     Func* Load(const std::string& symbol)
60     {
61         return Load<Func*>(symbol);
62     }
63 
64 private:
65     NativeHandleType _handle = NULL;
66 };
 1 #include <iostream>
 2 #include "shared_library.hpp"
 3 
 4 int main()
 5 {
 6     constexpr auto lib = LR"(test.dll)";
 7 
 8     SharedLibrary sharedLib(lib);
 9 
10     auto fn = sharedLib.Load<int(int, int)>("Add");
11 
12     if (fn) {
13         std::cout << "fn = " <<  fn(1, 2) << std::endl;
14     }
15 
16     return 0;
17 }

  运行结果如下:

      

二、std::enable_if有什么作用

  先来看看std::enable_if的原型:

1 template< bool B, class T = void >
2 struct enable_if;

  如果B满足条件,那么enable_if类型的值就等于T,其中using enable_if_t = typename enable_if<B, T>::type。比如:template<typename Func, typename = std::enable_if_t<std::is_function_v<Func>>>在模版实例化Load<int(int, int)>后为Load<int(__cdecl*)(int, int)>。因为Func = int(int, int)使std::is_function_v<int(int, int)>为true,进而推导出std::enable_if_t<true, void>,那么std::enable_if_v = std::enable_if<>::type=void。那么template<typename Func,

typename = std::enable_if_t<std::is_pointer_v<Func>&& std::is_function_v<std::remove_pointer_t<Func>>>> 推导也就不难了。

  其实我们可以通过栈帧信息加以验证的,上面调用demo验证截图如下:

      

参考源码仓库:not-enough-standards/shared_library.hpp at master · Alairion/not-enough-standards (github.com)

posted @ 2021-05-14 22:24  blackstar666  阅读(1120)  评论(0编辑  收藏  举报