自動導出動態連接庫接口在C++編程中絕對是一件煩人的事情,由於你不得不大量的重複如下幾個步驟:
1.加載動態連接庫
2.定義導出函數指針定義
3.定義導出函數指針變量
4.從動態連接庫中導出函數
5.編寫導出函數的封裝函數
6.卸載動態連接庫
有不少人試圖將這個過程自動化,然而所有折戟沉沙。我認爲終極緣由在於動態連接庫在導出函數的時候沒有包含參數信息,而在使用的時候不得不將導出函數進行強制轉型。
在C#中導出動態連接庫的函數相比C++來講要簡單的多:
1. 使用DllImportAttribute屬性規定導出函數的文件路徑、導出點、堆棧調用方式等
2.定義導出函數的C#函數原型
說簡單是代碼簡單,不是實現過程簡單。從表面上看,C#的這兩部實現了C++的那6步,從原理上來看,C#仍然須要C++的那6步。C#的編譯器把二、三、四、5的步驟封裝了,在C#代碼編譯成中間語言的時候進行了展開。C#在定義導出函數的函數原型的時候須要作一件事,就是判斷C#參數類型和C++參數類型的對應關係,爲何要特別注意這一點呢?就是由於C#在展開的時候須要準確的C++函數定義,不然調用過程當中會出現錯誤。
Java中有一個可以調用本地動態連接庫的組件——JNative,雖然我沒有看過它具體是怎麼實現的,可是從我瞭解到的隻字片語中推斷JNative參考了C#的實現過程並原模原樣的復刻了一份——由於JNative在實現的時候仍然須要判斷Java參數類型和C++參數類型的對應關係。
C#的實現方式和Java的實現方式在本質上和C++沒有區別,可是在編碼過程當中要簡單許多,由於C#和Java的編譯器在重複代碼的處理上比C++作了更多的工做。C++固然也能處理重複代碼,只不過不是經過編譯器,而是經過宏。
我按照C#的思路實做了C++自動導出函數的簡單版本。 編程
#pragma once #include <WTypes.h> #include <list> using namespace std; class DllImportAttribute { public: // 加載動態連接庫 virtual BOOL Init() = 0; // 卸載動態連接庫 virtual BOOL Uninit() = 0; }; // 加載全部庫 BOOL DllImportInit(); // 卸載全部庫 BOOL DllImportUninit(); // 動態庫列表 extern list<DllImportAttribute*> gDllImportList; #define DLLIMPORTCLASSBEGIN(CLASS, DLLPATH) class CLASS; \ extern CLASS g##CLASS##Dll; \ class CLASS : public DllImportAttribute \ { \ protected: \ HMODULE m_hModule; \ public: \ CLASS() : m_hModule(NULL) { \ gDllImportList.push_back(this); \ } \ virtual BOOL Init() { \ m_hModule = LoadLibraryA(DLLPATH); \ return (m_hModule != NULL); \ } \ virtual BOOL Uninit() { \ return FreeLibrary(m_hModule); \ } \ #define FUNCTIONENTRY(ENTRYTYPE, ENTRYPOINT) protected: \ typedef ENTRYTYPE; \ ENTRYPOINT ENTRYPOINT##Ptr; \ public: \ ENTRYPOINT ENTRYPOINT##Func() { \ ENTRYPOINT##Ptr = (ENTRYPOINT)GetProcAddress(m_hModule, "##ENTRYPOINT##"); \ return ENTRYPOINT##Ptr; \ } \ #define DLLIMPORTCLASSEND() }; #define DLLIMPORTCLASSIMPLEMENT(CLASS) CLASS g##CLASS##Dll; #define DLLIMPORTCALL(CLASS, ENTRYPOINT) g##CLASS##Dll.ENTRYPOINT##Func()
#include "StdAfx.h" #include "DllImportAttribute.h" list<DllImportAttribute*> gDllImportList; // 加載全部庫 BOOL DllImportInit() { BOOL bSuccess = TRUE; for (auto iter = gDllImportList.begin(); iter != gDllImportList.end(); iter++) { bSuccess &= (*iter)->Init(); } return bSuccess; } // 卸載全部庫 BOOL DllImportUninit() { BOOL bSuccess = TRUE; for (auto iter = gDllImportList.begin(); iter != gDllImportList.end(); iter++) { bSuccess &= (*iter)->Uninit(); } return bSuccess; }
#pragma once #include "DllImportAttribute.h" DLLIMPORTCLASSBEGIN(User32, "User32.dll") FUNCTIONENTRY(int (WINAPI *MessageBoxA) (HWND, LPCSTR, LPCSTR, UINT), MessageBoxA) DLLIMPORTCLASSEND()
#include "stdafx.h" #include "User32.h" DLLIMPORTCLASSIMPLEMENT(User32)
DllImportInit(); DLLIMPORTCALL(User32, MessageBoxA)(NULL, "ad", "ad", MB_OK); DllImportUninit();