前文已經(jīng)提到,WTL不是框架。因此,它并不隱藏winmain函數。而且,WTL向導確實(shí)也在主程序中生成一段代碼,整理如下:
CAppModule _Module;
int Run(LPTSTR /*lpstrCmdLine*/ = NULL, int nCmdShow = SW_SHOWDEFAULT){
CMessageLoop theLoop;
_Module.AddMessageLoop(&theLoop);
CMainFrame wndMain;
if(wndMain.CreateEx() == NULL){
ATLTRACE(_T("Main window creation failed!\n"));
return 0;
}
wndMain.ShowWindow(nCmdShow);
int nRet = theLoop.Run();
_Module.RemoveMessageLoop();
return nRet;
}
int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE /*hPrevInstance*/, LPTSTR lpstrCmdLine, int nCmdShow)
{
HRESULT hRes = ::CoInitialize(NULL);
ATLASSERT(SUCCEEDED(hRes));
hRes = _Module.Init(NULL, hInstance);
ATLASSERT(SUCCEEDED(hRes));
AtlAxWinInit();
// XXX 額外的初始化代碼可以插到這里
int nRet = Run(lpstrCmdLine, nCmdShow);
_Module.Term();
::CoUninitialize();
return nRet;
}
上述代碼進(jìn)行了一些修改,使其不再適應Windows 9x環(huán)境。很容易知道,這段程序的基本流程是:
初始化COM環(huán)境(CoInitialize)
初始化ATL應用程序
注冊ATL窗口類(lèi)
(運行應用程序:顯示窗口,進(jìn)入消息循環(huán),結束)
終止應用程序線(xiàn)程
撤銷(xiāo)COM環(huán)境
對于我們來(lái)說(shuō),最關(guān)心的是運行應用程序這一步。有些時(shí)候,可能會(huì )希望在真正顯示窗口之前做點(diǎn)什么(例如,檢查注冊表中的相關(guān)配置信息,等等)。這些代碼通常應寫(xiě)在_tWinMain函數中調用Run之前。
具體在什么地方執行呢?這取決于代碼本身的性質(zhì)。通常的經(jīng)驗,在XXX標記的地方插入代碼是比較合適的。這是因為,此時(shí)COM環(huán)境已經(jīng)被初始化,換言之,顯示對話(huà)框、調用注冊表接口,甚至通過(guò)ADO訪(fǎng)問(wèn)數據庫都不成問(wèn)題。
如果您仔細閱讀相關(guān)的WTL代碼,會(huì )發(fā)現它使用了“deprecated”的那些ATL 3.1代碼,包括CComModule。這是因為WTL7需要兼容VC6,而VC6中并不包含ATL 7。
自然,這樣做照顧了以前版本的VC用戶(hù),然而,筆者并不贊成如此編寫(xiě)代碼。ATL 7的設計中,將不同的Module模板分開(kāi)的設計顯然要比ATL 3.1更為科學(xué),因為縮減模板規模則有助于使代碼的最終編譯結果更小。
我個(gè)人很想把WTL 7中的這些代碼修改為與ATL 7規范相適應。然而,一方面由于許可協(xié)議的限制,我不能夠公開(kāi)發(fā)布修正過(guò)的版本;另一方面,我本人現在也沒(méi)有充裕的時(shí)間來(lái)做這件事情,因此,這一工作只能期待由Microsoft的開(kāi)發(fā)人員來(lái)完成了。但需要強調的是,對于VC7的用戶(hù)來(lái)說(shuō),使用ATL 7的相關(guān)使用規范要更好一些。
第4章 窗口中的消息處理
Windows系統中,消息機制是非常重要的一環(huán)。Windows程序的正確執行離不開(kāi)消息處理。在WTL/ATL中,在atlwin.h中定義的一組宏能夠簡(jiǎn)化這部分的編寫(xiě)過(guò)程。
限于篇幅,這里只介紹少數我們需要自己手工修改,或經(jīng)常用到的宏。
4.1 COMMAND_ID_HANDLER
這個(gè)宏能夠定義消息映射項(消息映射項由BEGIN_MSG_MAP和END_MSG_MAP包圍)。此消息映射能夠處理發(fā)到id的菜單、控件或加速鍵的WM_COMMAND消息,其格式如下:
COMMAND_ID_HANDLER( id, func )
其中,func必須是一個(gè)按照如下規格定義的函數:
LRESULT CommandHandler(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled);
這是一個(gè)非常一般的消息處理函數,如本文開(kāi)始時(shí)所說(shuō)的那樣,我假定讀者了解如何撰寫(xiě)這樣的函數,并了解各個(gè)參數的意義(在后文我會(huì )通過(guò)實(shí)例展示一部分用法,但不打算詳細介紹);當然,WTL向導會(huì )生成一部分消息處理函數,例如菜單的處理函數,等等。
4.2 CHAIN_MSG_MAP
這個(gè)宏用于在消息映射中引用其他類(lèi)(包括由模板實(shí)例化而來(lái)的那些)的消息映射。多數編程人員可能會(huì )希望這類(lèi)引用出現在消息映射的最后——這意味著(zhù)你的代碼能夠有機會(huì )在基類(lèi)(模板)的消息映射之前處理消息。
典型的WTL程序中的框架窗口會(huì )至少包括如下的消息映射引用:
CHAIN_MSG_MAP(CUpdateUI<用戶(hù)窗口類(lèi)名>)
CHAIN_MSG_MAP(CFrameWindowImpl<用戶(hù)窗口類(lèi)名>)
這兩個(gè)模板(CUpdateUI和CFrameWindowImpl)分別給出了對于菜單、窗口顯示所必需的一些消息處理的支持。這部分代碼可以在WTL的atlframe.h中找到。
4.3 通知的處理
通知(Notify)在其他Windows應用程序里面是比較重要的一類(lèi)消息。由于我們打算使用HTML界面,因此這部分就不那么重要了(我寧可用網(wǎng)頁(yè)來(lái)完成這些任務(wù))。為了便于閱讀,在此我把同志處理函數的一般格式列出:
LRESULT NotifyHandler(int /*idCtrl*/, LPNMHDR /*pnmh*/, BOOL& /*bHandled*/)
4.4 MESSAGE_HANDLER
盡管這個(gè)宏我們可能在絕大多數時(shí)候都用不著(zhù),但我認為還是有必要說(shuō)說(shuō)它。
默認的使用這個(gè)宏來(lái)進(jìn)行的消息映射只有1個(gè),那就是對WM_CREATE的處理(當然,在WTL內部還有一些其他的映射,但在這里我并不打算介紹):
MESSAGE_HANDLER(WM_CREATE, OnCreate)
消息處理函數的一般格式
LRESULT MessageHandler(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
這事兒并不難理解。唯一需要注意的是,MESSAGE_HANDLER是一個(gè)“什么都管”的東西,只要是發(fā)來(lái)這個(gè)消息,它就能截獲,而不管消息所作用的對象的ID。這在某些時(shí)候(例如,希望自己處理多個(gè)對象的WM_COMMAND消息)會(huì )比較方便。
嗯……現在你可以讓自己的程序說(shuō)點(diǎn)什么了。
4.5 嘗試一下使用消息映射
在資源文件中找到菜單,創(chuàng )建一個(gè)資源符號ID_FILE_HELLO,并創(chuàng )建一個(gè)菜單項,使用這個(gè)符。
在消息映射中的某個(gè)位置(當然不是在CHAIN_MSG_MAP之后:)添加下面一行:
COMMAND_ID_HANDLER(ID_FILE_HELLO, OnHello)
隨后,在這個(gè)類(lèi)中合適的位置建立一個(gè)函數(考慮到Hello world確實(shí)很俗,咱們換一個(gè)):
LRESULT OnHello(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
{
::MessageBox(NULL, \
_T("You born an excellent WTL programmer!"), \
_T("Congratulations!"), \
0);
return 0;
}
ok,現在您已經(jīng)了解了如何在一個(gè)WTL窗口中加入菜單資源,以及為它添加消息處理函數。值得一提的是,Visual C++ 7.0能夠識別WTL工程,并使用向導來(lái)幫您完成部分工作。具體操作和MFC類(lèi)似,這里我就不作介紹了。
4.6 使用HTML界面時(shí)的OnCreate函數
本文的標題是,使用WTL來(lái)編寫(xiě)HTML用戶(hù)界面的應用程序。不太嚴謹地說(shuō),只要能夠正確地完成消息映射的處理函數,就已經(jīng)足夠使用WTL來(lái)寫(xiě)程序了。
在我們最初創(chuàng )建的那個(gè)WTL工程中,自動(dòng)生成了一個(gè)叫做CWebUIView的類(lèi)。它的一個(gè)實(shí)例出現在我們的Frame Window中:
CWebUIView m_view;
在WTL向導生成的代碼中,這是一個(gè)public成員。我個(gè)人傾向于把它改為protected甚至private成員,當然,通常默認的設定也不會(huì )造成太大的問(wèn)題。
默認生成的代碼將在程序開(kāi)始執行是把你帶到
www.microsoft.com 。這一點(diǎn)是由OnCreate函數決定的。
LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
{
m_hWndClient = m_view.Create(m_hWnd,
rcDefault,
_T("http://www.microsoft.com"),
WS_CHILD | WS_VISIBLE |
WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_HSCROLL |
WS_VSCROLL, WS_EX_CLIENTEDGE);
// 注冊用于消息過(guò)濾和空閑時(shí)窗口刷新的對象
CMessageLoop* pLoop = _Module.GetMessageLoop();
ATLASSERT(pLoop != NULL);
pLoop->AddMessageFilter(this);
pLoop->AddIdleHandler(this);
return 0;
}
通常情況下不需要修改這段代碼——除了用紅色標記出的那個(gè)網(wǎng)址。當然,如果您希望對于每一個(gè)窗口實(shí)例(而不是應用程序實(shí)例)進(jìn)行某種特別的初始化的話(huà)另當別論……
?2001-2005 版權歸放飛技術(shù)小組所有。放飛技術(shù)小組保留對此的一切權利。
?2001-2005 Frontfree Workgroup. All rights reserved.
全部文章