9

単一の関数を使用して、任意のダイナミックリンクライブラリ(dll)関数をオブジェクトにロードするにはどうすればよいですか?std::function

たとえば、2つの関数を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"));
}
4

1 に答える 1

9

( MSDN Dev Centerから取得した説明)で提供されているWinAPI関数を使用します。windows.h

  • 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);
}
于 2013-03-06T14:07:55.367 に答える