在應用程式中,常常需要設計一種架構來適應需求的不斷變化。經常地,在軟體發布之後,使用者需要增加新的功能,或者不同的使用者需要根據各自特定的需求定製功能。為了達到這個目的而無需重寫代碼或者重做“開發——編譯——測試——發布”等一系列任務,我們可以實現一種在不破壞現有代碼的條件下可擴充模組的架構。使用外掛程式(plug-in)的架構可以滿足這一需要。
那麼什麼是使用外掛程式的架構呢?簡單地說,這種架構能允許軟體在啟動時尋找附加的功能模組並將其與軟體整合。許多應用程式,例如Microsoft Office,就使用了類似的技術來允許第三方開發人員來對已有的應用程式進行擴充。
怎麼樣來開發使用外掛程式的架構呢?一種非常簡單的方法就是使用DLL(動態連結程式庫)來實現外掛程式擴充。當應用程式啟動的時候,在預設的目錄中尋找符合一定規範的DLL檔案。尋找完成後,應用程式使用約定好的介面調用DLL模組。
架構的生命週期如下所示:
1, 應用程式初始化。
2, On_Init()函數在指定的目錄中尋找DLL檔案,例如,plug-in目錄。
3, 應用程式對每個尋找到的DLL調用load()函數。
4, load()調用之後,應用程式儲存每個模組的名字,並對每個模組建立引用,這樣,模組中的函數就可以在隨後被調用。
5, 在應用程式啟動並執行過程中,當使用者選擇功能表項目時,相應的所定義的功能就會被執行。
6, 關閉應用程式時,調用unload()函數,用來釋放load()函數中所申請的資源。
下面給出一個C++中使用DLL作為外掛程式的例子。
為了將問題簡化,這裡使用Visual Studio DLL嚮導建立DLL檔案。建立並匯出一個名為 fnPlug1的函數,參數的空,傳回型別為int,如下所示。
#define PLUG1_API __declspec(dllexport)extern "C" PLUG1_API int fnPlug1(void);
現在,我們來給DLL加入一個功能以便於觀察到其正常工作。向你的DLL函數中加入如下代碼。
PLUG1_API int fnPlug1(void){ return 1234;}
當然,在實際應用中,你需要加入特定的的功能而不是簡單地返回一個數字。
為了使這個DLL檔案作為外掛程式整合進應用程式中,我們需要建立程式來進行驅動。我們的目的是找到所有的DLL檔案,對其調用LoadLibrary()函數,儲存HMODULE以供以後引用。
下面給出一個例子。(注意我們使用.PLX副檔名代替了.DLL)
void CPluginDriverDlg::OnLoad(){ char filepath[MAX_PATH]; //who are we really? Get the Exe Path GetModuleFileName(AfxGetApp()->m_hInstance,filepath,MAX_PATH-1); SetCurrentDirectory(ExtractFilePath(filepath)); CFileFind finder; CString strWildCard = _T("*.plx"); //look for the plugin files //call this to set up the finder to iterate through all //the plugins BOOL bWorking = finder.FindFile(strWildCard); while (bWorking) { //have to call //FindNextFile() before GetFileName() or GetFilePath() //because FindFile just sets the object up and returns //true if _ANY_ files were found bWorking = finder.FindNextFile(); HMODULE hm = LoadLibrary(finder.GetFilePath()); if ( !hm ) { MessageBox("couldn't load"); } else { //loaded OK, so add each library's HMODULE to an array. //m_dwa is an MFC CDWordArray m_dwa.Add((DWORD)hm); } }}
接下來,當你想要反覆調用你的外掛程式時,使用對每個外掛程式儲存的HMODULE來得到函數的地址,接著利用函數地址調用函數。
void CPluginDriverDlg::OnRunPlugins(){ for(int i=0; i<m_dwa.GetSize() ; i++) { //Find a function and use it PFUNC pFunc = (PFUNC)GetProcAddress( (HINSTANCE)m_dwa.GetAt(i), _T("fnPlug1")); if (pFunc != NULL) { int n = pFunc(); CString answer ; answer.Format("The answer is %d", n); MessageBox(answer); } }}
說明一下,PFUNC的聲明形式是:
typedef int (*PFUNC)(void);
最後,一定要確保做好清理工作,從記憶體中釋放所有的DLL檔案,方法是使用FreeLibrary()函數。我在測試代碼的DestroyWindow()中完成了這個功能。
BOOL CPluginDriverDlg::DestroyWindow(){ for (int i=0; i<m_dwa.GetSize() ; i++) { // Free all the libs we used FreeLibrary((HMODULE)m_dwa[i]); } return CDialog::DestroyWindow();}
實際上就是這麼簡單。你可以把任意多的外掛程式放到預設的檔案夾中;當你準備好後,執行外掛程式中的函數。記住這是一個很簡單的例子,它可以很簡單地被擴充成健壯的模型,輕鬆載入你所需要的外掛程式。