zoukankan      html  css  js  c++  java
  • 如何动态获取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)

  • 相关阅读:
    Java实现 洛谷 P1009 阶乘之和
    Java实现 洛谷 P1009 阶乘之和
    mysql8.0 安装 修改密码 允许远程连接
    Starting MySQL.. ERROR! The server quit without updating PID file (/usr/local/mysql/data/vm10-0-0-19
    优秀c++开源项目集合
    可能是国内最火的开源项目 —— C/C++ 篇
    CodeBlocks环境搭建及创建第一个C++程序
    手动安装MySQL8.0
    【Linux】CentOS 7.4 安装 MySQL 8.0.12 解压版
    CentOS7下安装mysql5.7
  • 原文地址:https://www.cnblogs.com/smartNeo/p/14770381.html
Copyright © 2011-2022 走看看