(原創)一個簡潔通用的調用DLL函數的幫助類


  本次介紹一種調用dll函數的通用簡潔的方法,消除了原來調用方式的重復與繁瑣,使得我們調用dll函數的方式更加方便簡潔。用過dll的人會發現c++中調用dll中的函數有點繁瑣,調用過程是這樣的:在加載dll后還要定義一個對應的函數指針類型,再調用GetProcAddress獲取函數地址,再轉成函數指針,最后調用該函數。下面是調用dll中Max和Get函數的例子。

void TestDll()
{
typedef int(*pMax)(int a,int b);
typedef int(*pGet)(int a);
HINSTANCE hMode =LoadLibrary("MyDll.dll");if(hMode==nullptr)
return;

PMax Max = (PMax)GetProcAddress(hDLL,"Max");
if(Max==nullptr)
return;

int ret =Max(5,8); //8

PMin Get = (PMin)GetProcAddress(hDLL,"Get");
if(Get==nullptr)
return;

int ret =Get(5); //5

FreeLibrary(hDLL);
}

  這段代碼看起來很繁瑣,因為我沒每用一個函數就需要先定義一個函數指針,然后再根據名稱獲取函數地址,最后調用。如果一個dll中有上百個函數的話,這種重復而繁瑣的定義會讓人吐的。其實獲取函數地址和調用函數的過程是重復邏輯,應該消除,我不想每次都定義一個函數指針和調用GetProcAddress,我覺得可以用一種簡潔通用的方式去調用dll中的函數。我希望調用dll中的函數就像調用普通的函數一樣,即傳入一個函數名稱和函數的參數就可以實現函數的調用了。就類似於:

Ret CallDllFunc(const string&funName, T arg)

如果以這種方式調用的話,我就能避免繁瑣的函數指針定義以及反復的調用GetProcAddress了。

一種可行的解決方案

  如果要按照

Ret CallDllFunc(const string& funName, T arg)

這種方式調用的話,首先我要把函數指針轉換成一種函數對象或者泛型函數,這里我們可以用std::function去做這個事情,即通過一個函數封裝GetProcAddress,這樣通過函數名稱我就能獲取一個泛型函數std::function,我希望這個function是通用的,不論dll中是什么函數都可以轉換成這個function, 最后調用這個通用
的function就可以了。但是調用這個通用的function還有兩個問題需要解決:

  1. 不同函數的不同類型返回值怎么處理,因為函數的返回值可能是某些類型,如何以一種通用的返回值來消除這種不同返回值導致的差異呢?
  2. 函數的入參數目可能任意個數,且類型也不盡相同,如何來消除入參個數和類型的差異呢?

  我們一個個解決問題吧,首先看看如何封裝GetProcAddress,將函數指針轉換成std::function。通過如下代碼就可以了。

template <typename T>
std::function<T> GetFunction(const string&funcName)
{
FARPROC funAddress = GetProcAddress(m_hMod, funcName.c_str());
return std::function<T>((T*)(funAddress));
}

  其中T是std::function的模板參數,即函數類型的簽名。如果我們要獲取上面例子中,Max和Get函數,則可以這樣獲取:

auto fmax = GetFunction<int(int, int)>("Max");
auto fget = GetFunction<int(int)>("Get");

  這種方式比之之前先定義函數指針再調用GetProcAddress的方式更簡潔通用。

  再看看如何解決函數返回值和入參不統一的問題,關於這個問題,其實在前面的博文中就講到了,不知道的童鞋看這里: 

(原創)C++11改進我們的程序之簡化我們的程序(一)

  是的,還是通過result_of和可變參數模板來搞定。最終的調用函數是這樣的:

template <typename T, typename... Args>
typename std::result_of<std::function<T>(Args...)>::type ExcecuteFunc(const string& funcName,Args&&... args)
{
return GetFunction<T>(funcName)(args...);
}

  上面的例子中要調用Max和Get函數,這樣就行了:

auto max = ExcecuteFunc<int(int, int)>("Max", 5, 8);
auto ret = ExcecuteFunc<int(int)>("Get", 5);

  怎么樣,比之之前的調用方式是不是簡潔直觀多了,沒有了繁瑣的函數指針的定義,沒有了反復的調用GetProcAddress及其轉換和調用。

如果要限定調用方式就在參數前面加,如

ExcecuteFunc<int __stdcall(int, int)>


  最后看看完整的代碼吧。

#include <Windows.h>
#include <string>
#include <map>
#include <functional>
using namespace std;


class DllParser
{
public:

    DllParser()
    {
    }

    ~DllParser()
    {
        UnLoad();
    }

    bool Load(const string& dllPath)
    {
        m_hMod = LoadLibrary(dllPath.data());
        if (nullptr == m_hMod)
        {
            printf("LoadLibrary failed\n");
            return false;
        }

        return true;
    }

    bool UnLoad()
    {
        if (m_hMod == nullptr)
            return true;

        auto b = FreeLibrary(m_hMod);
        if (!b)
            return false;

        m_hMod = nullptr;
        return true;
    }

    template <typename T>
    T* GetFunction(const string& funcName)
    {
        auto addr = GetProcAddress(m_hMod, funcName.c_str());
        return (T*) (addr);
    }

    template <typename T, typename... Args>
    typename std::result_of<std::function<T>(Args...)>::type ExcecuteFunc(const string& funcName, Args&&... args)
    {
        auto f = GetFunction<T>(funcName);
        if (f == nullptr)
        {
            string s = "can not find this function " + funcName;
            throw std::exception(s.c_str());
        }            

        return f(std::forward<Args>(args)...);
    }


private:
    HMODULE m_hMod;
    std::map<string, FARPROC> m_map;
};

 

c++11 boost技術交流群:296561497,歡迎大家來交流技術。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM