wtl中显示html,用WTL构建HTML界面的应用程序(2)

前文已经提到,WTL不是框架。因此,它并不隐藏winmain函数。而且,WTL向导确实也在主程序中生成一段代码,整理如下:

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;

}

上述代码进行了一些修改,使其不再适应Windows 9x环境。很容易知道,这段程序的基本流程是:

初始化COM环境(CoInitialize)

初始化ATL应用程序

注册ATL窗口类

(运行应用程序:显示窗口,进入消息循环,结束)

终止应用程序线程

撤销COM环境

对于我们来说,最关心的是运行应用程序这一步。有些时候,可能会希望在真正显示窗口之前做点什么(例如,检查注册表中的相关配置信息,等等)。这些代码通常应写在_tWinMain函数中调用Run之前。

具体在什么地方执行呢?这取决于代码本身的性质。通常的经验,在XXX标记的地方插入代码是比较合适的。这是因为,此时COM环境已经被初始化,换言之,显示对话框、调用注册表接口,甚至通过ADO访问数据库都不成问题。

如果您仔细阅读相关的WTL代码,会发现它使用了“deprecated”的那些ATL 3.1代码,包括CComModule。这是因为WTL7需要兼容VC6,而VC6中并不包含ATL 7。

自然,这样做照顾了以前版本的VC用户,然而,笔者并不赞成如此编写代码。ATL 7的设计中,将不同的Module模板分开的设计显然要比ATL 3.1更为科学,因为缩减模板规模则有助于使代码的最终编译结果更小。

我个人很想把WTL 7中的这些代码修改为与ATL 7规范相适应。然而,一方面由于许可协议的限制,我不能够公开发布修正过的版本;另一方面,我本人现在也没有充裕的时间来做这件事情,因此,这一工作只能期待由Microsoft的开发人员来完成了。但需要强调的是,对于VC7的用户来说,使用ATL 7的相关使用规范要更好一些。

第4章 窗口中的消息处理

Windows系统中,消息机制是非常重要的一环。Windows程序的正确执行离不开消息处理。在WTL/ATL中,在atlwin.h中定义的一组宏能够简化这部分的编写过程。

限于篇幅,这里只介绍少数我们需要自己手工修改,或经常用到的宏。

4.1 COMMAND_ID_HANDLER

这个宏能够定义消息映射项(消息映射项由BEGIN_MSG_MAP和END_MSG_MAP包围)。此消息映射能够处理发到id的菜单、控件或加速键的WM_COMMAND消息,其格式如下:

COMMAND_ID_HANDLER( id, func )

其中,func必须是一个按照如下规格定义的函数:

LRESULT CommandHandler(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled);

这是一个非常一般的消息处理函数,如本文开始时所说的那样,我假定读者了解如何撰写这样的函数,并了解各个参数的意义(在后文我会通过实例展示一部分用法,但不打算详细介绍);当然,WTL向导会生成一部分消息处理函数,例如菜单的处理函数,等等。

4.2 CHAIN_MSG_MAP

这个宏用于在消息映射中引用其他类(包括由模板实例化而来的那些)的消息映射。多数编程人员可能会希望这类引用出现在消息映射的最后——这意味着你的代码能够有机会在基类(模板)的消息映射之前处理消息。

典型的WTL程序中的框架窗口会至少包括如下的消息映射引用:

CHAIN_MSG_MAP(CUpdateUI)

CHAIN_MSG_MAP(CFrameWindowImpl)

这两个模板(CUpdateUI和CFrameWindowImpl)分别给出了对于菜单、窗口显示所必需的一些消息处理的支持。这部分代码可以在WTL的atlframe.h中找到。

4.3 通知的处理

通知(Notify)在其他Windows应用程序里面是比较重要的一类消息。由于我们打算使用HTML界面,因此这部分就不那么重要了(我宁可用网页来完成这些任务)。为了便于阅读,在此我把同志处理函数的一般格式列出:

LRESULT NotifyHandler(int /*idCtrl*/, LPNMHDR /*pnmh*/, BOOL& /*bHandled*/)

4.4 MESSAGE_HANDLER

尽管这个宏我们可能在绝大多数时候都用不着,但我认为还是有必要说说它。

默认的使用这个宏来进行的消息映射只有1个,那就是对WM_CREATE的处理(当然,在WTL内部还有一些其他的映射,但在这里我并不打算介绍):

MESSAGE_HANDLER(WM_CREATE, OnCreate)

消息处理函数的一般格式

LRESULT MessageHandler(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)

这事儿并不难理解。唯一需要注意的是,MESSAGE_HANDLER是一个“什么都管”的东西,只要是发来这个消息,它就能截获,而不管消息所作用的对象的ID。这在某些时候(例如,希望自己处理多个对象的WM_COMMAND消息)会比较方便。

嗯……现在你可以让自己的程序说点什么了。

4.5 尝试一下使用消息映射

在资源文件中找到菜单,创建一个资源符号ID_FILE_HELLO,并创建一个菜单项,使用这个符。

在消息映射中的某个位置(当然不是在CHAIN_MSG_MAP之后:)添加下面一行:

COMMAND_ID_HANDLER(ID_FILE_HELLO, OnHello)

随后,在这个类中合适的位置建立一个函数(考虑到Hello world确实很俗,咱们换一个):

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,现在您已经了解了如何在一个WTL窗口中加入菜单资源,以及为它添加消息处理函数。值得一提的是,Visual C++ 7.0能够识别WTL工程,并使用向导来帮您完成部分工作。具体操作和MFC类似,这里我就不作介绍了。

4.6 使用HTML界面时的OnCreate函数

本文的标题是,使用WTL来编写HTML用户界面的应用程序。不太严谨地说,只要能够正确地完成消息映射的处理函数,就已经足够使用WTL来写程序了。

在我们最初创建的那个WTL工程中,自动生成了一个叫做CWebUIView的类。它的一个实例出现在我们的Frame Window中:

CWebUIView m_view;

在WTL向导生成的代码中,这是一个public成员。我个人倾向于把它改为protected甚至private成员,当然,通常默认的设定也不会造成太大的问题。

默认生成的代码将在程序开始执行是把你带到 www.microsoft.com 。这一点是由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);

// 注册用于消息过滤和空闲时窗口刷新的对象  CMessageLoop* pLoop = _Module.GetMessageLoop();

ATLASSERT(pLoop != NULL);

pLoop->AddMessageFilter(this);

pLoop->AddIdleHandler(this);

return 0;

}

通常情况下不需要修改这段代码——除了用红色标记出的那个网址。当然,如果您希望对于每一个窗口实例(而不是应用程序实例)进行某种特别的初始化的话另当别论……

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值