主要針對有一定Windows編程經(jīng)驗的讀者。閱讀本文需要對COM機制有一定的了解,最好掌握C++的STL,熟悉模板機制。本文假定您使用的是Visual C++(6.0以上版本),并正確安裝了WTL 7.0和完整的ATL(Visual C++組件),如果您沒(méi)有安裝Visual C++,請跳過(guò)本文;如果您沒(méi)有安裝WTL 7.0,請看這里。 第1章 WTL介紹 ATL想必大家都不陌生,用VC寫(xiě)COM或COM+組件,ATL能夠幫助我們完成不少工作。我個(gè)人比較喜歡ATL的運作方式?D?D實(shí)際上,除了初學(xué)COM時(shí)編寫(xiě)的幾個(gè)示例組件之外,我幾乎沒(méi)有完全手工地起草過(guò)COM的C++源代碼?D?D因為ATL已經(jīng)完成了這些工作。 如果您不熟悉ATL的話(huà),在這兒我先簡(jiǎn)單地介紹一下它。ATL是一組C++模板庫,最初設計它的目的為編程人員提供一個(gè)快捷的途徑來(lái)編寫(xiě)快而小的COM組件。早期,它特別為實(shí)現多層次企業(yè)解決方案中的商業(yè)邏輯和數據訪(fǎng)問(wèn)的自動(dòng)化組件而設計。最初的ATL版本中不包括對于界面的支持,但在2.0中,ATL開(kāi)始包括那些用來(lái)支持界面控件的機制。 與MFC相比,ATL最大的優(yōu)勢就在于它是使用模板,而不是類(lèi)繼承實(shí)現的。當然它也有不足之處,即它不像MFC那樣容易編寫(xiě)界面。通常的應用中,ATL被用來(lái)編寫(xiě)非界面的組件,例如,OLE DB、安全驗證組件,等等。 對于我本人來(lái)說(shuō),ATL的優(yōu)勢是非常明顯的。MFC需要一些額外的消耗,因為MFC需要支持界面的種種操作,需要支持許多我并不需要的特性(至少是所寫(xiě)的程序中不需要),而由于采用的是類(lèi)繼承,這些額外的設施將不得不被包括到最終的代碼中。靜態(tài)連接MFC庫需要增加數百KB,而動(dòng)態(tài)連接,盡管能夠有效地減小程序體積,但作為換湯不換藥的機制,仍然需要消耗比較多的內存。通常對于組件而言,希望額外的東西越少越好?D?D如果我不需要在屏幕上顯示窗口,那么窗口支持就是多余的;如果我不需要處理鼠標事件,那么這段代碼就是多余的;而多余的代碼最好干脆不要出現。典型的網(wǎng)站服務(wù)中,更少的內存占用無(wú)疑意味著(zhù)更大的承載能力。 對于那些希望有良好界面的應用程序開(kāi)發(fā)人員來(lái)說(shuō),ATL的上述優(yōu)勢的吸引力也許并不大。然而,ATL的許多特性,特別是允許編寫(xiě)更小、更快的代碼(與MFC相比),以及對于COM的與生俱來(lái)的支持,促使Microsoft在A(yíng)TL中加入了對編寫(xiě)普通Windows程序的支持。 按照Microsoft的說(shuō)法,WTL是使用了這些支持的對ATL的一組擴展。它包括了一組擴展ATL的類(lèi),使它能夠支持更為復雜的,包括應用程序和各種用戶(hù)界面組件,同時(shí)保持ATL最大的好處?D?D能夠編寫(xiě)小而快的代碼。WTL的目標是成為最好和最簡(jiǎn)單的實(shí)現基于Win32和ATL的應用程序、服務(wù)器和控件的方法。 盡管WTL現在還不能像MFC那樣方便地用來(lái)編寫(xiě)應用程序,但它已經(jīng)足以在許多地方替代MFC,當然,使用它的開(kāi)發(fā)者也就同時(shí)得到了它所帶來(lái)的好處?D?D緊湊而高效的代碼。隨著(zhù)基于HTML頁(yè)面制作程序界面這一新興潮流的興起,使用WTL將不再是一種“另類(lèi)”的做法。 在本系列的文章中,我將一步一步地帶領(lǐng)大家實(shí)現一個(gè)采用HTML頁(yè)面作為界面的應用程序。如果你是一個(gè)網(wǎng)頁(yè)設計高手的話(huà),那么你將很快地發(fā)現制作一個(gè)漂亮的Windows應用程序是如此的得心應手;即使您原來(lái)非常熟悉Windows應用程序的界面設計,您也會(huì )發(fā)現,使用HTML來(lái)設計程序界面是一個(gè)不錯的主意?D?D它將極大地縮短界面的設計時(shí)間,并且,與您處于同一個(gè)開(kāi)發(fā)組的其他設計人員將能夠更容易地幫助您做好界面設計工作,因為編寫(xiě)HTML界面不需要特別的程序設計經(jīng)驗。 第1章 WTL入門(mén) 不熟悉COM、ATL都沒(méi)有關(guān)系,因為WTL是一件容易學(xué)會(huì )的工具。下載之后,執行自解壓程序,它將會(huì )把WTL復制到你的硬盤(pán)上的某個(gè)位置。隨后,執行與VC版本對應的AppWizard安裝程序(一個(gè)js文件),并在VC環(huán)境中適當地配置Include文件的位置就可以開(kāi)始使用WTL了。 在開(kāi)始寫(xiě)程序之前,我還需要嘮叨一些關(guān)于WTL中宏定義。與MFC一樣,ATL/WTL也使用了宏。我并不打算介紹全部這些宏,但幾個(gè)常用的無(wú)疑能夠幫助你理解WTL向導生成的代碼:
以及幾個(gè)智能指針類(lèi),以及我們可能會(huì )用到的ATL類(lèi)。智能指針類(lèi)可以幫助你更好地撰寫(xiě)程序,并避免內存泄漏等問(wèn)題。
限于篇幅,我不打算介紹WTL的類(lèi)?D?D除非在后面的文字中提到它們。這里列出它們的名字,WTL的類(lèi)包括CAppModule,CServerAppModule,CMessageLoop,CMessageFilter,CIdleHandler,CFrameWindowImplBase,CFrameWindowImpl,COwnerDraw,CDialogResize,CMDIWindow,CMDIFrameWindowImpl,CMDIChildWindowImpl,CUpdateUIBase,CUpdateUI,CStatic,CButton,CListBox,CComboBox,CEdit,CEditCommands,CScrollBar,CImageList,CListViewCtrl,CTreeViewCtrl,CTreeItem,CTreeViewCtrlEx,CHeaderCtrl,CToolBarCtrl,CStatusBarCtrl,CTabCtrl,CToolTipCtrl,CToolInfo,CTrackBarCtrl,CUpDownCtrl,CProgressBarCtrl,CHotKeyCtrl,CAnimateCtrl,CRichEditCtrl,CRichEditCommands,CDragListBox,CDragListNotifyImpl,CReBarCtrl,CComboBoxEx,CDateTimePickerCtrl,CFlatScrollBarImpl,CFlatScrollBar,CIPAddressCtrl,CMonthCalendarCtrl,CCustomDraw,CPropertySheetWindow,CPropertySheetImpl,CPropertySheet,CPropertyPageWindow,CPropertyPageImpl,CPropertyPage,CAxPropertyPageImpl,CAxPropertyPage,CFileDialogImpl,CFileDialog,CFolderDialogImpl,CFolderDialog,CFontDialogImpl,CFontDialog,CRichEditFontDialogImpl,CRichEditFontDialog,CColorDialogImpl,CColorDialog,CPrintDialogImpl,CPrintDialog,CPrintDialogExImpl,CPrintDialogEx,CPageSetupDialogImpl,CPageSetupDialog,CFindReplaceDialogImpl,CFindReplaceDialog,CMenu,CDC,CPaintDC,CClientDC,CWindowDC,CPen,CBrush,CFont,CBitmap,CPalette,CRgn,CCommandBarCtrlImpl,CCommandBarCtrl,CBitmapButtonImpl,CBitmapButton,CCheckListViewCtrlImpl,CCheckListViewCtrl,CHyperLinkImpl,CHyperLink,CWaitCursor,CMultiPaneStatusBarCtrlImpl,CMultiPaneStatusBarCtrl,CPaneContainerImpl,CPaneContainer,CScrollImpl,CScrollWindowImpl,CMapScrollImpl,CMapScrollWindowImpl,CSplitterImpl,CSplitterWindowImpl,CSplitterWindow,CTheme,CThemeImpl,CPrinterInfo,CPrinter,CDevMode,CPrinterDC,CPrintJobInfo,CPrintJob,CPrintPreview,CPrintPreviewWindow,CSize,CPoint,CRect,CString,CWinDataExchange,CRecentDocumentList。許多類(lèi)對于MFC程序員來(lái)說(shuō)是非常熟悉的,僅從類(lèi)名我們就可以看出,WTL具有相當強大的界面支持。與MFC不同,WTL并不是一個(gè)框架?D?D它不強加任何應用程序模型,并能夠適應幾乎所有的應用程序模型。WTL基本上是模板庫,這不僅意味著(zhù)它包括了全部源代碼,并且,也意味著(zhù)只需要實(shí)例化使用到的那些數據結構和內聯(lián)函數,從而得到盡可能小的代碼。 第2章 使用WTL應用程序向導 Visual C++ 7和Visual C++ 6中的WTL應用程序向導的界面差異比較大(內容相同)。為了創(chuàng )建一個(gè)支持HTML界面的應用程序,需要作如下選擇:
首先是選擇WTL應用程序向導,然后
選擇SDI應用程序,最后,
將View窗口的類(lèi)型選定為HTML Page,并去掉Toolbar, Rebar, Command Bar, Status Bar這些特性。 假定我們在第一步中輸入的工程名稱(chēng)為webui,則我們將拿到一組文件:stdafx.cpp、stdafx.h(這個(gè)就不用說(shuō)了吧?),webui.cpp(程序實(shí)現),MainFrm.h(CMainFrame類(lèi)),webuiView.h(CWebuiView類(lèi)),AboutDlg.h(CAboutDlg類(lèi)),resource.h(資源定義),以及一組資源文件。 現在程序已經(jīng)可以運行了,其界面類(lèi)似下圖:
其中,白色部分將出現Microsoft的主頁(yè),如果連著(zhù)網(wǎng)的話(huà)。 | ||||||||||||||||||||||||||||||||||||||||||||||||||||
前文已經(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); hRes = _Module.Init(NULL, hInstance); AtlAxWinInit(); // XXX 額外的初始化代碼可以插到這里 int nRet = Run(lpstrCmdLine, nCmdShow); _Module.Term(); return nRet; |
上述代碼進(jìn)行了一些修改,使其不再適應Windows 9x環(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)使用規范要更好一些。
Windows系統中,消息機制是非常重要的一環(huán)。Windows程序的正確執行離不開(kāi)消息處理。在WTL/ATL中,在atlwin.h中定義的一組宏能夠簡(jiǎn)化這部分的編寫(xiě)過(guò)程。
限于篇幅,這里只介紹少數我們需要自己手工修改,或經(jīng)常用到的宏。
這個(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ì )生成一部分消息處理函數,例如菜單的處理函數,等等。
這個(gè)宏用于在消息映射中引用其他類(lèi)(包括由模板實(shí)例化而來(lái)的那些)的消息映射。多數編程人員可能會(huì )希望這類(lèi)引用出現在消息映射的最后?D?D這意味著(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中找到。
通知(Notify)在其他Windows應用程序里面是比較重要的一類(lèi)消息。由于我們打算使用HTML界面,因此這部分就不那么重要了(我寧可用網(wǎng)頁(yè)來(lái)完成這些任務(wù))。為了便于閱讀,在此我把同志處理函數的一般格式列出:
| LRESULT NotifyHandler(int /*idCtrl*/, LPNMHDR /*pnmh*/, BOOL& /*bHandled*/) |
盡管這個(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)什么了。
在資源文件中找到菜單,創(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)似,這里我就不作介紹了。
本文的標題是,使用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, |
通常情況下不需要修改這段代碼?D?D除了用紅色標記出的那個(gè)網(wǎng)址。當然,如果您希望對于每一個(gè)窗口實(shí)例(而不是應用程序實(shí)例)進(jìn)行某種特別的初始化的話(huà)另當別論……
- 作者: 微塵 2003年08月21日, 星期四 03:52
你可以使用這個(gè)鏈接引用該篇日志 http://publishblog.blogdriver.com/blog/tb.b?diaryID=806
聯(lián)系客服