天天看點

使用DLL中的資源

使用DLL中的資源面臨的一個主要問題是,DLL和EXE中都有資源集,但是程式在運作态到底會去哪個資源集中找常常令我們疑惑。

考慮如下的經典情況:

在建立MFC DLL工程時選中Regular DLL using shared MFC DLL選項,建立一個與MFC自身DLL共享的DLL。在新DLL中建立一個ID名為IDD_DLLDIALOG的對話框資源。在這個DLL中導出一個ShowDialog()函數,内容如下:

<a href="http://www.thankcreate.com/c/275#viewSource">檢視源代碼</a>

<code>1</code>

<code>extern</code> <code>"C"</code> <code>void</code> <code>Show Dialog()</code>

<code>2</code>

<code>{</code>

<code>3</code>

<code>    </code><code>CDialog dlg(IDD_DLLDIALOG);</code>

<code>4</code>

<code>    </code><code>dlg.DoModal();</code>

<code>5</code>

<code>}</code>

建立一個MFC的EXE工程,導入剛剛建立的DLL工程的lib和dll,将OK按鈕的響應函數設定為:  ShowDialog();  編譯連結通過後,我們運作起程式來會發現點選OK按鈕後什麼反應都沒有!怎麼會這樣?我們不是明明在DLL裡設定好了IDD_DLLDIALOG嗎?請大家注意“在建立MFC DLL工程時選中Regular DLL using shared MFC DLL”這句話,如果我們當時選的是“Regular DLL with MFC statically linked”又會是什麼情況呢?又能正常顯示了!

使用靜态連接配接的方式通路MFC庫DLL,會把MFC庫DLL與工程本身一起打包,而共享的方式則會在運作時去系統路徑中動态加載MFC庫DLL。我們可以這麼了解以上的現象:如果建立的DLL使用共享的方式通路庫DLL,當EXE程式運作時,實際上有兩套上下文系統在運作,而靜态連接配接則隻有一套上下文系統了,是以在靜态連接配接時會需要進行子產品狀态的切換。

當我們建立的是共享方式通路 庫DLL的DLL時,要解決上述問題比較通用的做法是在ShowDialog函數裡面加上一句AFX_MANAGE_STATE(AfxGetStaticModuleState());

AfxGetStaticModuleState()的原型為:

AFX_MODULE_STATE* AFXAPI AfxGetStaticModuleState( );

即目前子產品的ModuleState指針。

AFX_MANAGE_STATE宏則會以傳入的指針為參數去在棧上建立一個類,此類會做哪些事相信聰明的你也能猜到了.沒錯,它會在構造函數中存下目前的ThreadState所指向的ModuleState指針,再将傳入的指針指定為目前ThreadState所指向的指針。等到這個類的執行個體被析構是,又将其改回去。

如果我們不想在EXE中通過間接的調用DLL導出的ShowDialog(),而是直接想在EXE的OnBnClickedOk()中生成一個使用DLL中對話框資源的對話框呢?這時AFX_MANAGE_STATE(AfxGetStaticModuleState())就派不上用場了(因這它是用在DLL工程中的)。最好的辦法是用AfxSetResourceHander(HINSTANCE)配合GetModuleHandle(LPCTSTR)。

GetModuleHandle(LPCTSTR str)将會根據傳入的字元串得到相應的子產品句柄。注意,EXE和DLL都可以被看作是子產品。當傳入為空(NULL)時,将得到目前程序(EXE)的HINSTANCE句柄。使用GetModuleHandle要保證相應的dll已經被加載,也許你會說這個條件不是廢話麼,但是這确實是很容易犯錯的地方。

如果是用靜态加載DLL的方式(即直接把生成的那個.lib檔案導入),而之前又從未調用過這個dll中的任何函數或變量的話,GetModuleHandle隻會得到一個空值。因為系統會直到有某個語句調用過dll裡面的東西時才會将dll真正加載。

例如:

<code>void</code> <code>CUseDllDlg::OnBnClickedOk()</code>

<code>    </code><code>HINSTANCE</code> <code>temp = AfxGetResourceHandle();</code>

<code>    </code><code>AfxSetResourceHandle(GetModuleHandle(_T(</code><code>"RegDll"</code><code>)));</code>

<code>    </code><code>CDialog dlg(4000);</code>

<code>6</code>

<code>7</code>

<code>    </code><code>AfxSetResourceHandle(temp);</code>

<code>8</code>

上面的這份代碼運作時會報錯,因為AfxSetResourceHandle在檢查入參時發現了個空值。而下面的代碼則不會有問題:

<code>    </code><code>ASSERT(TestOnly() == 1);</code>

<code>9</code>

其中TestOnly也是從RegDll中導出的函數,它什麼都不做,隻是傳回一個1。我們使用它隻是為了把RegDll加載進來。當然了,如果我們直接使用動态加載的方法,像下面這樣直接LoadLibrary那更是沒有問題了。

<code>    </code><code>HMODULE</code> <code>hm= LoadLibrary(_T(</code><code>"..//Debug//RegDll.dll"</code><code>));</code>

不過眼尖的讀者可能會立即發現其實在上面這種情況下使用GetModuleHandle不是最好的選擇。因為直接來個AfxSetResourceHandle(hm)就可解決問題了。