C++ 将任意函数从 DLL 动态加载到 std::function

标签 c++ dll c++11 dllexport std-function

如何使用单个函数将任意动态链接库 (dll) 函数加载到std::function 对象中?

例如我想将两个函数编译到一个dll中:

// test.dll

int plusFive(int value) {
    return value + 5;
}

void printHello() {
    std::cout << "Hello!" << std::endl;
}

并在运行时使用单个函数加载它们,如下所示:

// main.cc

#include <functional>

int main() {
    std::function<int(int)> func1(loadDllFunc("test.dll", "plusFive"));
    std::function<void()> func2(loadDllFunc("test.dll", "printHello"));
}

最佳答案

使用 windows.h 中提供的 WinAPI 函数(描述取自 MSDN Dev Center)。

  • LoadLibrary - 将指定的模块加载到调用进程的地址空间中。返回模块的句柄。
  • GetProcAddress - 从指定的动态链接库 (DLL) 检索导出函数或变量的地址。返回导出函数或变量的地址。

使用此函数加载特定函数并返回一个 std::function 对象:

// main.cc

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

template <typename T>
std::function<T> loadDllFunc(const std::string& dllName, const std::string& funcName) {
    // Load DLL.
    HINSTANCE hGetProcIDDLL = LoadLibrary(dllName.c_str());

    // Check if DLL is loaded.
    if (hGetProcIDDLL == NULL) {
        std::cerr << "Could not load DLL \"" << dllName << "\"" << std::endl;
        exit(EXIT_FAILURE);
    }

    // Locate function in DLL.
    FARPROC lpfnGetProcessID = GetProcAddress(hGetProcIDDLL, funcName.c_str());

    // Check if function was located.
    if (!lpfnGetProcessID) {
        std::cerr << "Could not locate the function \"" << funcName << "\" in DLL\"" << dllName << "\"" << std::endl;
        exit(EXIT_FAILURE);
    }

    // Create function object from function pointer.
    std::function<T> func(reinterpret_cast<__stdcall T*>(lpfnGetProcessID));

    return func;
}

DLL源代码应该这样写:

// test.cc (test.dll)
#include <iostream>

// Declare function prototypes with "extern C" to prevent name mangling.
// Declare functions using __declspec(dllexport) to signify the intent to export.
extern "C" {
    __declspec(dllexport) int __stdcall plusFive(int);
    __declspec(dllexport) void __stdcall printHello();
}

int plusFive(int value) {
    return value + 5;
}

void printHello() {
    std::cout << "Hello!" << std::endl;
}

然后像这样使用loadDllFunc:

// main.cc

int main() {
    auto func1 = loadDllFunc<int(int)>("test.dll", "plusFive");
    auto func2 = loadDllFunc<void()>("test.dll", "printHello");

    std::cout << "Result of func1: " << func1(1) << std::endl;
    func2();
}

输出:

Result of func1: 6
Hello!

作为旁注,DLL 可以使用 GCC (4.7.2) 进行编译,如下所示:

g++ -shared -o test.dll test.cc -std=c++11

编辑:

我不确定 loadDllFunc 中的转换是否给出了正确的类型:

std::function<T> func(reinterpret_cast<__stdcall T*>(lpfnGetProcessID));

它似乎将其转换为 __stdcall int (*)(int),而它应该是 int (__stdcall *)(int)

这是使用辅助解析器类实现 loadDllFunc 的另一种方法。此解决方案将正确地将函数指针转换为 int (__stdcall *)(int)

template <typename T>
struct TypeParser {};

template <typename Ret, typename... Args>
struct TypeParser<Ret(Args...)> {
    static std::function<Ret(Args...)> createFunction(const FARPROC lpfnGetProcessID) {
        return std::function<Ret(Args...)>(reinterpret_cast<Ret (__stdcall *)(Args...)>(lpfnGetProcessID));
    }
};

template <typename T>
std::function<T> loadDllFunc(const std::string& dllName, const std::string& funcName) {
    // Load DLL.
    HINSTANCE hGetProcIDDLL = LoadLibrary(dllName.c_str());

    // Check if DLL is loaded.
    if (hGetProcIDDLL == NULL) {
        std::cerr << "Could not load DLL \"" << dllName << "\"" << std::endl;
        exit(EXIT_FAILURE);
    }

    // Locate function in DLL.
    FARPROC lpfnGetProcessID = GetProcAddress(hGetProcIDDLL, funcName.c_str());

    // Check if function was located.
    if (!lpfnGetProcessID) {
        std::cerr << "Could not locate the function \"" << funcName << "\" in DLL\"" << dllName << "\"" << std::endl;
        exit(EXIT_FAILURE);
    }

    // Create function object from function pointer.
    return TypeParser<T>::createFunction(lpfnGetProcessID);
}

关于C++ 将任意函数从 DLL 动态加载到 std::function,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15249465/

相关文章:

c++ - 使用创建的 DLL 文件(从应用程序调用函数)

c# - native dll 的 .net 包装器 - 如何将运行时错误的风险降至最低?

c++ - 使用浮点文字初始化 const int

c++ const 使用。我可以删除它吗?

c++ - 在 metatrader4 中创建一个基本的 dll

c++ timer ,相当于 .NET System.Threading.Timer

c++ - 如何将模式从Dev-C++中的c++ 98模式更改为支持C++ 0x(基于范围)的模式?

c++ - 从类方法 C++0x 返回 unique_ptr

c++ - 向字符串添加换行符

c++ - 通过 using-directive 在 using-declaration 中查找名称