2、動(dòng)態(tài)鏈接到MFC的DLL
在討論關(guān)于動(dòng)態(tài)鏈接到MFC的DLL的模塊狀態(tài)問(wèn)題之前,先來(lái)看一個(gè)例子。本例可以通過(guò)如下步驟來(lái)完成:
1)在VC菜單中File->New新建一個(gè)命名為DLLShared的MFC AppWizard的工程,下一步選擇Regular DLL using shared MFC DLL。
2)在工程中添加一個(gè)對(duì)話框資源,其ID為:IDD_ABOUTBOX。并在resource.h之中將IDD_ABOUTBOX 的數(shù)值改為100。
3)在DLLShared.cpp中定義如下函數(shù):
void ShowDlg()
{
CDialog dlg(IDD_ABOUTBOX);
dlg.DoModal();
}
4)在DLLShared.def文件中的EXPORTS語(yǔ)句中添加一行:ShowDlg,以導(dǎo)出ShowDlg函數(shù)。
5)編譯生成DLLShared.dll和DLLShared.lib。
繼續(xù)使用上面的Use工程,將前面生成的DLLShared.dll和DLLShared.lib兩個(gè)文件復(fù)制到工程的Debug目錄內(nèi),并將
extern "C" __declspec(dllexport) void ShowDlg();
#pragma comment(lib,"debug/DLLStatic")
這兩行改為:
void ShowDlg();
#pragma comment(lib,"debug/DLLShared")
編譯并運(yùn)行Use.exe。點(diǎn)擊按鈕,這次你看到了什么?對(duì),沒(méi)錯(cuò),這次彈出的是Use.exe的關(guān)于對(duì)話框。將上述例子的DLL類(lèi)型換成MFC Extension DLL(using shared MFC DLL)也會(huì)出現(xiàn)相同的問(wèn)題。
為什么會(huì)出現(xiàn)上面的問(wèn)題?這是因?yàn)樵谑褂昧?/span>MFC共享庫(kù)的時(shí)候,默認(rèn)情況下,MFC使用主應(yīng)用程序的資源句柄來(lái)加載資源模板。雖然我們調(diào)用的是DLL中的函數(shù)來(lái)顯示DLL中的對(duì)話框,并且對(duì)應(yīng)的對(duì)話框模板是存儲(chǔ)在DLL中的,但MFC仍舊在主應(yīng)用程序也就是Use.exe中尋找相應(yīng)的對(duì)話框模板。由于在DLL中所定義的對(duì)話框資源ID與主應(yīng)用程序中所定義的關(guān)于對(duì)話框的資源ID相同,所以MFC就把主應(yīng)用程序中的關(guān)于對(duì)話框顯示了出來(lái)。如果二者不同,則MFC就認(rèn)為DLL中所定義的對(duì)話框資源不存在,dlg.DoModal會(huì)返回0,也就是什么都不會(huì)顯示。
那么如何解決上述問(wèn)題呢?解決辦法就是在適當(dāng)?shù)臅r(shí)候進(jìn)行模塊狀態(tài)切換,以保證具有當(dāng)前狀態(tài)的模塊是我們所需要的模塊從而使用正確的資源。MFC提供了下列函數(shù)和宏來(lái)完成這些工作:
AfxGetStaticModuleState:這是一個(gè)函數(shù),其函數(shù)原型為:
AFX_MODULE_STATE* AFXAPI AfxGetStaticModuleState( );
此函數(shù)在堆棧上構(gòu)造AFX_MODULE_STATE類(lèi)的實(shí)例pModuleState并對(duì)其賦值后將其返回。在AFX_MODULE_STATE類(lèi)的構(gòu)造函數(shù)中,該類(lèi)獲取指向當(dāng)前模塊狀態(tài)的指針并將其存儲(chǔ)在成員變量中,然后將pModuleState設(shè)置為新的有效模塊狀態(tài)。在它的析構(gòu)函數(shù)中,該類(lèi)將存儲(chǔ)在其成員變量中的指針還原為存貯的前一個(gè)模塊狀態(tài)。
AFX_MANAGE_STATE:這是一個(gè)宏,其原型為:
AFX_MANAGE_STATE( AFX_MODULE_STATE* pModuleState )
該宏用于將pModuleState(指向包含模塊全局?jǐn)?shù)據(jù)也就是模塊狀態(tài)的AFX_MODULE_STATE結(jié)構(gòu)的指針)設(shè)置為當(dāng)前的即時(shí)作用空間中(the remainder of the immediate containing scope)的有效模塊狀態(tài)。在離開(kāi)包含該宏的作用空間時(shí),前一個(gè)有效的模塊狀態(tài)自動(dòng)還原。
AfxGetResourceHandle:這個(gè)函數(shù)的原型為:
HINSTANCE AfxGetResourceHandle( );
該函數(shù)返回了一個(gè)保存了HINSTANCE類(lèi)型的、應(yīng)用程序默認(rèn)所加載資源的模塊的句柄。
AfxSetResourceHandle:這個(gè)函數(shù)的原型為:
void AfxSetResourceHandle( HINSTANCE hInstResource );
該函數(shù)將hInstResource所代表的模塊設(shè)置為具有當(dāng)前狀態(tài)的模塊。
通過(guò)使用上述四個(gè)函數(shù)或宏就可以正確的在動(dòng)態(tài)鏈接到MFC的DLL中切換模塊狀態(tài)。接下來(lái)我們將通過(guò)修改上面出現(xiàn)問(wèn)題的那個(gè)例子來(lái)介紹如何使用上述四個(gè)函數(shù)或宏。先來(lái)看看Regular DLL using shared MFC DLL類(lèi)型:
在上述例子的第三步的ShowDlg函數(shù)的第一條語(yǔ)句前加上如下語(yǔ)句(要確保該語(yǔ)句在函數(shù)實(shí)現(xiàn)的第一行):
AFX_MANAGE_STATE(AfxGetStaticModuleState());
之后重新編譯生成DLLShared.dll和DLLShared.lib,并將這兩個(gè)文件重新拷貝到Use工程的Debug目錄內(nèi)。這次編譯生成Use.exe并運(yùn)行,點(diǎn)擊按鈕,可以看到彈出的時(shí)我們?cè)?/span>DLL中所加入的那個(gè)對(duì)話框,而不再是Use.exe的關(guān)于對(duì)話框了。
通過(guò)上面的講解,相信你已經(jīng)知道該語(yǔ)句的作用了。在函數(shù)ShowDlg的第一行加上這么一句后,每次調(diào)用DLL的應(yīng)用程序使用該函數(shù)的時(shí)候,MFC庫(kù)都會(huì)自動(dòng)切換當(dāng)前模塊狀態(tài),這樣就保證了資源讀取的正確性。
AFX_MANAGE_STATE(AfxGetStaticModuleState());是自動(dòng)切換當(dāng)前模塊狀態(tài),也可以通過(guò)使用AfxGetResourceHandle和AfxSetResourceHandle來(lái)手動(dòng)切換當(dāng)前模塊狀態(tài)。具體使用方法如下:
在上述例子的第三步的ShowDlg函數(shù)的第一條語(yǔ)句前加上如下語(yǔ)句(要確保該語(yǔ)句在函數(shù)實(shí)現(xiàn)的第一行):
HINSTANCE save_hInstance = AfxGetResourceHandle();
AfxSetResourceHandle(theApp.m_hInstance);
在調(diào)用對(duì)話框成功之后,也就是dlg.DoModal();之后,添加:
AfxSetResourceHandle(save_hInstance);
這種方法在進(jìn)入ShowDlg函數(shù)之后,通過(guò)AfxGetResourceHandle來(lái)獲得并保存當(dāng)前狀態(tài)模塊的句柄。然后獲得DLL模塊的句柄theApp.m_hInstance(當(dāng)然,也可以使用GetModuleHandle函數(shù)來(lái)獲得DLL模塊的句柄),并使用AfxSetResourceHandle函數(shù)來(lái)將其設(shè)置為當(dāng)前狀態(tài)狀態(tài)。最后在調(diào)用對(duì)話框成功之后再用恢復(fù)AfxSetResourceHandle資源句柄,將當(dāng)前模塊狀態(tài)恢復(fù)。
這樣做有些麻煩,但是有一點(diǎn)好處是可以在完成使用資源的任務(wù)之后就可以立即恢復(fù)資源句柄。而AFX_MANAGE_STATE(AfxGetStaticModuleState());的方法只能等函數(shù)的作用空間結(jié)束之后才恢復(fù)資源句柄。由于可執(zhí)行文件必須重畫(huà)工具條等原因,因此建議只要有可能就必須恢復(fù)資源句柄,否則可能會(huì)遇到許多問(wèn)題。比如說(shuō),如果用戶移動(dòng)DLL的對(duì)話框,而此時(shí)資源句柄仍然為DLL的資源,那么程序就會(huì)崩潰。最好的恢復(fù)句柄的時(shí)機(jī)在對(duì)話框響應(yīng)WM_INITDIALOG消息的時(shí)候,因?yàn)檫@時(shí)對(duì)話框的模板等已經(jīng)讀出了。
對(duì)于MFC Extension DLL(using shared MFC DLL)類(lèi)型的MFC DLL,切換當(dāng)前模塊狀態(tài)的方法與Regular DLL using shared MFC DLL類(lèi)型的MFC DLL所使用的方法很相似,這里不再舉例實(shí)現(xiàn)。二者不同的地方如下:
在MFC擴(kuò)展DLL中使用AFX_MANAGE_STATE(AfxGetStaticModuleState());時(shí),會(huì)產(chǎn)生如下錯(cuò)誤:
mfcs42d.lib(dllmodul.obj) : error LNK2005: __pRawDllMain already defined in dllextend.obj
mfcs42d.lib(dllmodul.obj) : error LNK2005: _DllMain@12 already defined in dllextend.obj
mfcs42d.lib(dllmodul.obj) : error LNK2005: __pRawDllMain already defined in dllextend.obj
因此在MFC擴(kuò)展DLL中需要將AFX_MANAGE_STATE(AfxGetStaticModuleState());換成AFX_MANAGE_STATE(AfxGetAppModuleState());才能正確切換當(dāng)前模塊狀態(tài)。
在MFC擴(kuò)展DLL中使用AfxGetResourceHandle和AfxSetResourceHandle的方法與在Regular DLL using shared MFC DLL類(lèi)型的MFC DLL中所使用的方法相同。并且,DLL模塊的句柄可以通過(guò)MFC提供的DlgextentDLL這個(gè)結(jié)構(gòu)的hModule成員來(lái)獲得。即使用AfxSetResourceHandle(DlgextentDLL.hModule);語(yǔ)句。
當(dāng)然,對(duì)于動(dòng)態(tài)鏈接到MFC的DLL,也可以在調(diào)用該DLL的MFC應(yīng)用程序中使用AfxGetResourceHandle和AfxSetResourceHandle兩個(gè)函數(shù)來(lái)切換當(dāng)前狀態(tài)模塊。該DLL模塊的句柄可以用GetModuleHandle函數(shù)來(lái)獲得。在此不再贅述。
連載:在DLL中使用資源(一)
聯(lián)系客服