dll中資源切換

來源:互聯網
上載者:User
 在討論關於動態連結到MFC的DLL的模組狀態問題之前,先來看一個例子。本例可以通過如下步驟來完成:

1)在VC菜單中File->New建立一個命名為DLLShared的MFC AppWizard的工程,下一步選擇Regular DLL using shared MFC DLL。

2)在工程中添加一個對話方塊資源,其ID為:IDD_ABOUTBOX。並在resource.h之中將IDD_ABOUTBOX 的數值改為100。

3)在DLLShared.cpp中定義如下函數:

void ShowDlg()
{

    CDialog dlg(IDD_ABOUTBOX);

    dlg.DoModal();

}

4)在DLLShared.def檔案中的EXPORTS語句中添加一行:ShowDlg,以匯出ShowDlg函數。

5)編譯產生DLLShared.dll和DLLShared.lib。

繼續使用上面的Use工程,將前面產生的DLLShared.dll和DLLShared.lib兩個檔案複製到工程的Debug目錄內,並將

extern "C" __declspec(dllexport) void ShowDlg();

#pragma comment(lib,"debug/DLLStatic")

這兩行改為:

void ShowDlg();

#pragma comment(lib,"debug/DLLShared")

編譯並運行Use.exe。點擊按鈕,這次你看到了什嗎?對,沒錯,這次彈出的是Use.exe的關於對話方塊。將上述例子的DLL類型換成MFC Extension DLL(using shared MFC DLL)也會出現相同的問題。
為什麼會出現上面的問題?這是因為在使用了MFC共用庫的時候,預設情況下,MFC使用主應用程式的資源控制代碼來載入資源模板。雖然我們調用的是DLL中的函數來顯示DLL中的對話方塊,並且對應的對話方塊模板是儲存在DLL中的,但MFC仍舊在主應用程式也就是Use.exe中尋找相應的對話方塊模板。由於在DLL中所定義的對話方塊資源ID與主應用程式中所定義的關於對話方塊的資源ID相同,所以MFC就把主應用程式中的關於對話方塊顯示了出來。如果二者不同,則MFC就認為DLL中所定義的對話方塊資源不存在,dlg.DoModal會返回0,也就是什麼都不會顯示。

那麼如何解決上述問題呢?解決辦法就是在適當的時候進行模組狀態切換,以保證具有目前狀態的模組是我們所需要的模組從而使用正確的資源。MFC提供了下列函數和宏來完成這些工作:

AfxGetStaticModuleState:這是一個函數,其函數原型為:

AFX_MODULE_STATE* AFXAPI AfxGetStaticModuleState( );

此函數在堆棧上構造AFX_MODULE_STATE類的執行個體pModuleState並對其賦值後將其返回。在AFX_MODULE_STATE類的建構函式中,該類擷取指向當前模組狀態的指標並將其儲存在成員變數中,然後將pModuleState設定為新的有效模組狀態。在它的解構函式中,該類將儲存在其成員變數中的指標還原為存貯的前一個模組狀態。

AFX_MANAGE_STATE:這是一個宏,其原型為:

AFX_MANAGE_STATE( AFX_MODULE_STATE* pModuleState )

該宏用於將pModuleState(指向包含模組全域資料也就是模組狀態的AFX_MODULE_STATE結構的指標)設定為當前的即時作用空間中(the remainder of the immediate containing scope)的有效模組狀態。在離開包含該宏的作用空間時,前一個有效模組狀態自動還原。

AfxGetResourceHandle:這個函數的原型為: 

    HINSTANCE AfxGetResourceHandle( ); 

    該函數返回了一個儲存了HINSTANCE類型的、應用程式預設所載入資源的模組的控制代碼。

AfxSetResourceHandle:這個函數的原型為:

void AfxSetResourceHandle( HINSTANCE hInstResource );

該函數將hInstResource所代表的模組設定為具有目前狀態的模組。

通過使用上述四個函數或宏就可以正確的在動態連結到MFC的DLL中切換模組狀態。接下來我們將通過修改上面出現問題的那個例子來介紹如何使用上述四個函數或宏。先來看看Regular DLL using shared MFC DLL類型:
在上述例子的第三步的ShowDlg函數的第一條語句前加上如下語句(要確保該語句在函數實現的第一行):

AFX_MANAGE_STATE(AfxGetStaticModuleState());

之後重新編譯產生DLLShared.dll和DLLShared.lib,並將這兩個檔案重新拷貝到Use工程的Debug目錄內。這次編譯產生Use.exe並運行,點擊按鈕,可以看到彈出的時我們在DLL中所加入的那個對話方塊,而不再是Use.exe的關於對話方塊了。

通過上面的講解,相信你已經知道該語句的作用了。在函數ShowDlg的第一行加上這麼一句後,每次調用DLL的應用程式使用該函數的時候,MFC庫都會自動切換當前模組狀態,這樣就保證了資源讀取的正確性。

AFX_MANAGE_STATE(AfxGetStaticModuleState());是自動切換當前模組狀態,也可以通過使用AfxGetResourceHandle和AfxSetResourceHandle來手動切換當前模組狀態。具體使用方法如下:

在上述例子的第三步的ShowDlg函數的第一條語句前加上如下語句(要確保該語句在函數實現的第一行):

HINSTANCE save_hInstance = AfxGetResourceHandle();

    AfxSetResourceHandle(theApp.m_hInstance);

在調用對話方塊成功之後,也就是dlg.DoModal();之後,添加:

AfxSetResourceHandle(save_hInstance); 

    這種方法在進入ShowDlg函數之後,通過AfxGetResourceHandle來獲得並儲存目前狀態模組的控制代碼。然後獲得DLL模組的控制代碼theApp.m_hInstance(當然,也可以使用GetModuleHandle函數來獲得DLL模組的控制代碼),並使用AfxSetResourceHandle函數來將其設定為目前狀態狀態。最後在調用對話方塊成功之後再用恢複AfxSetResourceHandle資源控制代碼,將當前模組狀態恢複。 

    這樣做有些麻煩,但是有一點好處是可以在完成使用資源的任務之後就可以立即恢複資源控制代碼。而AFX_MANAGE_STATE(AfxGetStaticModuleState());的方法只能等函數的作用空間結束之後才恢複資源控制代碼。由於可執行檔必須重畫工具條等原因,因此建議只要有可能就必須恢複資源控制代碼,否則可能會遇到許多問題。比如說,如果使用者移動DLL的對話方塊,而此時資源控制代碼仍然為DLL的資源,那麼程式就會崩潰。最好的恢複控制代碼的時機在對話方塊響應WM_INITDIALOG訊息的時候,因為這時對話方塊的模板等已經讀出了。 

    對於MFC Extension DLL(using shared MFC DLL)類型的MFC DLL,切換當前模組狀態的方法與Regular DLL using shared MFC DLL類型的MFC DLL所使用的方法很相似,這裡不再舉例實現。二者不同的地方如下: 

    在MFC擴充DLL中使用AFX_MANAGE_STATE(AfxGetStaticModuleState());時,會產生如下錯誤:

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擴充DLL中需要將AFX_MANAGE_STATE(AfxGetStaticModuleState());換成AFX_MANAGE_STATE(AfxGetAppModuleState());才能正確切換當前模組狀態。

在MFC擴充DLL中使用AfxGetResourceHandle和AfxSetResourceHandle的方法與在Regular DLL using shared MFC DLL類型的MFC DLL中所使用的方法相同。並且,DLL模組的控制代碼可以通過MFC提供的DlgextentDLL這個結構的hModule成員來獲得。即使用AfxSetResourceHandle(DlgextentDLL.hModule);語句。
當然,對於動態連結到MFC的DLL,也可以在調用該DLL的MFC應用程式中使用AfxGetResourceHandle和AfxSetResourceHandle兩個函數來切換目前狀態模組。該DLL模組的控制代碼可以用GetModuleHandle函數來獲得。在此不再贅述。 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.