C ++从DLL动态加载任意函数到std :: function [英] C++ Dynamically load arbitrary function from DLL into std::function

查看:373
本文介绍了C ++从DLL动态加载任意函数到std :: function的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

例如,我想将两个函数编译为dll:

  // test.dll 

int plusFive(int value){
返回值+ 5;
}

void printHello(){
std :: cout<< 你好! << 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));
}


解决方案

使用 WinAPI 功能在 windows.h 中提供(描述取自 MSDN开发中心)。




  • LoadLibrary -将指定的模块加载到调用进程的地址空间中。返回模块的句柄。

  • GetProcAddress -从指定的动态链接库中检索导出的函数或变量的地址( DLL)。返回导出函数或变量的地址。



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

  // main.cc 

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

模板< typename T>
std :: function< T> loadDllFunc(const std :: string& dllName,const std :: string& funcName){
//加载DLL。
HINSTANCE hGetProcIDDLL = LoadLibrary(dllName.c_str());

//检查DLL是否已加载。
if(hGetProcIDDLL == NULL){
std :: cerr<< 无法加载DLL \<< dllName<< \<< std :: endl;
出口(EXIT_FAILURE);
}

//在DLL中定位函数。
FARPROC lpfnGetProcessID = GetProcAddress(hGetProcIDDLL,funcName.c_str());

//检查是否找到函数。
if(!lpfnGetProcessID){
std :: cerr<< 无法在DLL中找到函数\ funcName << \ \ <<< dllName << \ < std :: endl;
出口(EXIT_FAILURE);
}

//从函数指针创建函数对象。
std :: function< T> func(reinterpret_cast< __ stdcall T *>(lpfnGetProcessID));

返回函数;
}

DLL源应该这样写:

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

//使用 extern C声明函数原型,以防止名称篡改。
//使用__declspec(dllexport)声明函数以表示导出意图。
extern C {
__declspec(dllexport)int __stdcall plusFive(int);
__declspec(dllexport)void __stdcall printHello();
}

int plusFive(int value){
返回值+ 5;
}

void printHello(){
std :: cout<< 你好! << std :: endl;
}

然后使用 loadDllFunc 像这样:

  // main.cc 

int main(){
自动功能1 = loadDllFunc< int(int)>( test.dll, plusFive);
auto func2 = loadDllFunc< void()>( test.dll, printHello);

std :: cout<< func1的结果:<< func1(1)<< std :: endl;
func2();
}

输出:

  func1的结果:6 
您好!

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

  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)

 模板< typename T> 
struct TypeParser {};

模板< 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));
}
};

模板< typename T>
std :: function< T> loadDllFunc(const std :: string& dllName,const std :: string& funcName){
//加载DLL。
HINSTANCE hGetProcIDDLL = LoadLibrary(dllName.c_str());

//检查DLL是否已加载。
if(hGetProcIDDLL == NULL){
std :: cerr<< 无法加载DLL \<< dllName<< \<< std :: endl;
出口(EXIT_FAILURE);
}

//在DLL中定位函数。
FARPROC lpfnGetProcessID = GetProcAddress(hGetProcIDDLL,funcName.c_str());

//检查是否找到函数。
if(!lpfnGetProcessID){
std :: cerr<< 无法在DLL中找到函数\ funcName << \ \ <<< dllName << \ < std :: endl;
出口(EXIT_FAILURE);
}

//从函数指针创建函数对象。
返回TypeParser< T> :: createFunction(lpfnGetProcessID);
}


How can I load an arbitrary dynamic-link library (dll) function into a std::function object using a single function?

For example I would like to compile two functions into a dll:

// test.dll

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

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

And load them both at runtime using a single function like this:

// main.cc

#include <functional>

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

解决方案

Use the WinAPI functions provided in windows.h (descriptions taken from MSDN Dev Center).

  • LoadLibrary - Loads the specified module into the address space of the calling process. Returns a handle to the module.
  • GetProcAddress - Retrieves the address of an exported function or variable from the specified dynamic-link library (DLL). Returns the address of the exported function or variable.

Use this function to load a specific function and return a std::function object:

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

The DLL source should be written like this:

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

And then use loadDllFunc like this:

// 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();
}

Output:

Result of func1: 6
Hello!

As a sidenote the DLL can be compiled using GCC (4.7.2) like this:

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

Edit:

I'm not sure that the cast in loadDllFunc gives the correct type:

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

It seems to cast it to __stdcall int (*)(int) when it should be int (__stdcall *)(int).

Here is another way to implement loadDllFunc using an auxiliary parser class. This solution will correctly cast the function pointer to 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的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆