dll模組資源切換

來源:互聯網
上載者:User
大部分開發過程中我們都直接使用C或者C++開發基於WindowsSDK的Win32 Dll,但是有些場合,我們可能需要用到MFC的一些類庫來簡化我們的開發過程和周期,為此,我們需要開發MFC擴充dll。
使用MFC擴充Dll可能縮短我們的開發週期。MFC裡面提供了很多的封裝好的類庫,使用起來非常方便。但是有些東西有其優點就有其弊端。這裡將要談到的就是MFC擴充dll可能遇到的一個問題:資源控制代碼的切換。
在程式中,我們可能包括資源,代碼中我們可能要載入資源。載入資源有一系列API函數,例如LoadBitmap LoadICon等,這些函數都有一個共同點:需要知道資源所在模組的控制代碼。只有將資源所在的模組載入之後,擷取到資源所在模組控制代碼,才能調用這些函數擷取指定資源。例如LoadBitmap函數原型:
HBITMAP LoadBitmap(
HINSTANCE hInstance, // handle to application instance
LPCTSTR lpBitmapName // name of bitmap resource
);
LoadIcon函數原型如下:
HICON LoadIcon(
HINSTANCE hInstance,
LPCTSTR lpIconName
);
這類函數的第一個參數都是包含指定資源的模組的控制代碼。
MFC擴充Dll和基於MFC程式一樣,大部分都被MFC封裝了。例如CBitmap封裝了Bitmap位元影像相關的操作,CMenu封裝了菜單的相關操作。CBitmap提供了成員函數LoadBitmap來擷取指定資源。原型如下:
BOOL LoadBitmap(
LPCTSTR lpszResourceName
);
BOOL LoadBitmap(
UINT nIDResource
);
我們看到,這些成員函數都沒有資源所在模組的控制代碼參數了,那麼調用此函數架構怎麼知道從哪個模組來擷取資源呢?
一般的MFC對話方塊程式或者SDI MDI程式中由於只有一個應用程式,擷取資源,架構自動從exe模組中找。但是如果程式中存在多個模組,我們的目的不是從exe中找,而是從指定模組中找,那怎麼辦?經常LoadBitmap載入指定位元影像資源失敗,但是明明這個資源我們添加到資源檔中了。用GetLastError()擷取錯誤碼得知是指定資源不存在的錯誤;有些時候,指定的是這個資源,結果擷取的是另外一個資源。種種都讓人很迷惑,下面就分析一下本質原因以及解決辦法。
舉個例子:寫個dll,dll中有個對話方塊,假設對話方塊id為IDD_TIPS;dll中提供一個匯出函數ShowDlg如下:
void WINAPI _declspec(dllexport) ShowDlg(UINT nId)
{
CDialog dlg(nId);
dlg.DoModal();
}
現在dll被一個exe程式調用,正好這個exe檔案的資源中也存在這麼一個id為IDD_TIPS的對話方塊,那麼在exe中調用dll的匯出函數想彈出 dll中的tips對話方塊時,結果就大出我們意料之外,呈現在我們面前的不是dll中的IDD_TIPS對話方塊,而是exe中的IDD_TIPS對話方塊。下面給出一個簡單Sample,dll是MFC規則dll而不是MFC擴充dll,先簡單講述一下MFC規則dll,然後轉入正題。
首先我們看看MFC規則dll和MFC擴充dll的不同。
(1)MFC規則dll和MFC擴充dll都可以使用MFC封裝類
(2)MFC規則dll和普通dll一樣,不能以MFC類為介面,但是MFC擴充dll不同,他的介面可以是MFC類對象。

對於我們來說,使用MFC規則dll和MFC擴充dll作為sample,效果差別不大。
首先我們建立一個MFC規則dll,注意,架構產生的MFC規則dll代碼和MFC exe程式差不多,一個由WinApp派生的類,並且為此類定義了一個全域的對象。另外,在程式中有Begin_message_map | End_message_map對。這些我們不用關注。在資源編輯器中我們為MFC規則dll添加一個對話方塊,對話方塊ID設定為IDD_TIPS;然後在主程式檔案檔案中(也就是定義了全域CWinApp衍生類別對象的檔案)添加一個匯出函數:
extern "C"
void __declspec(dllexport) ShowDlg(UINT nID)
{
CDialog dlg(nID);
dlg.DoModal();s
}

建立一個測試程式,這裡我們簡單起見就直接建立一個基於dialog的程式。為對話方塊添加一個對話方塊,對話方塊ID也設定為IDD_TIPS,和前面dll中的對話方塊id相同。另外,在dialog中添加一個按鈕,為按鈕添加點擊處理函數,當使用者點擊的時候我們想讓其調用dll中的匯出函數ShowDlg,然後彈出dll中的Tips對話方塊,按鈕點擊處理函數如下:
void CVC6PrjDlg::OnShowDlg()
{
ShowDlg(IDD_TIPS);
}
當然,我們需要包含前面dll的匯入庫檔案,並且加入匯入函數ShowDlg函數的聲明
#pragma comment(lib, ".\MFCExtDll\Debug\MFCExtDll.lib")
extern "C"
void __declspec(dllimport)
ShowDlg(UINT nID);
編譯運行,點擊exe的按鈕,彈出一個對話方塊。仔細看下,發現此對話方塊並不是dll中的對話方塊,而是exe中的對話方塊,怎麼回事呢?我們調用的是dll中的匯出函數,傳入的id是dll中的對應對話方塊的id,怎麼把exe中的對應此id的對話方塊給彈出來了?
這裡涉及到資源控制代碼的切換。
前面說過,任何一個資源都有該資源模組的載入,根據資源模組的控制代碼找到對應的資源。模組的控制代碼是模組載入到進程地址空間的起始地址。MFC架構幫我們做了很多封裝,對我們來說,調用本exe的資源載入函數,都沒有攜帶模組參數。那麼MFC怎麼知道在哪個模組中尋找資源呢。特別上面的例子:dll中包含一個 IDD_TIPS的對話方塊資源,同時,exe檔案中也包含一個IDD-TIPS的對話方塊資源,此時架構會總是從exe的資源模組中尋找資源。所以導致上面出現的問題。
同樣的此類問題還可能發生在:在基於MFC支援的ATL項目中,使用LoadBitmap等尋找資源函數載入資源總是失敗,不是對應的資源不存在,而是尋找的模組不對。在ATL中調用CBitmap的LoadBitmap函數,沒有指定資源控制代碼,架構使用的資源模組總是指的exe本身的資源模組。而我們知道,atl寫的activex控制項大部分嵌入在ie裡頭,這樣的話,LoadBitmap相當於在ie的資源模組中尋找資源,而不是在activex控制項自身模組中尋找,這樣肯定會尋找失敗。
解決此類的問題根本在於修正當前的資源模組控制代碼。
MFC提供了一個宏AFX_MANAGE_STATE(GetStaticModuleState())可以幫我們解決問題。在函數的入口,調用此行語句,用於將當前的模組控制代碼切換到該代碼被所在模組的控制代碼。
修改一下dll的ShowDlg()函數:
extern "C" __declspec(dllexport) void ShowDlg(UINT nID)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
CDialog dlg(nID);
dlg.DoModal();
return;
}
rebuild dll,再次運行exe,點擊按鈕,發現這次彈出來的是dll中的對話方塊。

另外,你還可以直接手動設定模組控制代碼,記得在修改之後,函數退出部分,需要將其設定回來。最好的辦法還是用MFc提供的宏AFX_MANAGE_STATE(AfxGetStaticModuleState())來進行模組控制代碼的切換。
最後,如果你純粹的使用SDK API來實現,那麼不存在問題,因為在api中會指定資源所在的模組控制代碼。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.