CHtmlView的通信机制

IWebBrowser2是microsoft提供的web浏览器控件, 它也是IE使用的内核,功能非常强大。在http客户端程序中,使用IWebBrowser2可以简化对http通信过程的处理,并将html文档解析成一个对象结构。IWebBrowser2是一个ActiveX控件和COM接口,直接使用比较复杂。CHtmlView是MFC对IWebBrowser2的封装,提供了更为简化的编程模型。尽管如此,程序员必须对底层的通信机制有基本的理解,才能正确地使用CHtmlView。
我们可以把http客户端与服务端的通信过程看成一个发送接收序列:

Send(1), Receive(1), Send(2),Receive(2) ….

客户端在每次发送和接收的时候都必须清楚当前的状态,才能逐步完成一个功能,比如为了上传一个文件,应该先登录一个账号,再获取上传页面,然后提交要上传的文件,整个过程是有序的。当客户端响应用户请求时,也必须清楚当前的通信状态,比如账号已经登录时,就不再响应登录请求。

在CHtmlView中有两个主要的方法:Navigate2()用来打开一个URL,OnDocumentComplete()在文档接收完时调用。Navigate2()执行以后程序不必等待,而是可以做其它事情,文档接收完成时OnDocumentComplete()自动被调用。感觉上这样的模型是很合理的,为了编写程序,还必须对它有确切的理解。主要的一个问题是,存不存在多线程,特别是,Navigate2()和OnDocumentComplete()是否在一个线程中执行? 因为对单线程的情况,有一种简单的方法来跟踪通信状态:用一个全局变量,在发送接收序列的第一步将其初始化,以后每一步将其加1,它的值就反映了状态。而多线程时,事情会变复杂。此外,这个模型涉及到一些基本概念和重要的机制,也非常值得了解。

追究IwebBrowser2的通信进制涉及到若干技术主题: Berkley socket通信模型,异步通信模型, COM连接点机制,ActiveX控件和包容器的通信,Windows程序模型。虽然没有源代码和官方的文档,无法确定IwebBrowser2的具体实现方式。但通过查阅有关的资料,可以整理出大体的概念,初步结果如下:

1.       Berkley socket通信模型,异步通信模型

传统的Berkley socket使用的是同步模型,用recv()接收数据时,如果没有数据,调用会阻塞,可以用select()检查socket状态,避免阻塞,但又必须忙等待,这都不符合CHtmlView的通信模型。recv()和select()的说明可以参考MSDN文档。

一种可能的实现方式是使用Windows提供的扩展socket API,WSAAsyncSelect()将socket和调用后立即返回,当数据到达时,以Windows消息的方式通知线程。可以参考MSDN中关于WSAAsyncSelect()的文档。

在传统socket基础上实现异步通信模型需要使用多线程,大体说就是在主线程中启动发送过程,随即返回进行其它处理,另一个线程负责跟踪,当数据接收完成时通知主线程,跟踪线程是可以阻塞的。MSDN文档《using an asyncronous client socket》(http://msdn.microsoft.com/en-us/library/bbx2eya8.aspx)讲解了DotNet框架中的异步socket模型。

2.       COM连接点机制,ActiveX控件和包容器的通信

在IWebBrowser实现了异步通信的基础上,它还必须通知控件的调用者,这里使用了COM的连接点机制,我们知道COM组件提供接口给客户调用,但这是单向的,通过在调用方实现COM组件支持的出接口,调用方可以收到COM组件的通知,进一步内容可以参考潘爱明的《COM原理和应用》第6和12章。这里需要知道COM组件是在调用方线程中执行,除非它执行时又创建了新的线程。

3.       Windows消息模型

Windows程序是基于消息的,简单说,它有一个消息循环不断从消息队列中取消息,发给窗口过程来响应,执行响应过程中,某些系统调用可能会产生不进队消息,这些消息立即被处理,只要窗口过程是可重入的。不管怎么说,这是一个单线程模型,线程入口函数中包含了消息循环,所有的代码都必须通过消息执行。关于Windows程序模型,可以参考Petzold的《Windows程序设计》第1章。

现在我们考虑为了实现CHtmlView的通信模型,以上三种机制如何协作。

首先,需要确定Navigate2()和OnDocumentComplete()是否在同一线程中执行。如果在OoDocumentComplete()加入一个消息框,而在调用Navigate2()之后执行一个无限循环,则无论等多久,该消息框都不会弹出。这说明OnDocumentComplete()不是在另外一个线程中执行的。

然后,通过CHtmlView的源代码容易知道OnDocumentComplete()是通过COM连接点机制被调用的,从而IWebBrowser2中负责调用的代码也是在同一线程中执行的。我们可以想到,在IWebBrowser2中还有一个线程负责跟踪http通信,或者IWebBrowser2没有创建新线程,但是用WSAAsyncSelect()为socket注册了消息,Windows会以消息形式通知线程。如果存在跟踪线程,它怎样和主线程通信呢,若干线程通信手段,如事件,信号量,都需要等待一方阻塞,这不符合CHtmlView的使用模型,唯一的可能是跟踪线程向主线程发消息。

用CHtmlView编写一个浏览器程序,在OnDocumentComplete()中打断点,如下是调用栈。

 

CHtmlView_tView::OnDocumentComplete(const char * 0x003864cc) line 105

CHtmlView::DocumentComplete(IDispatch * 0x0015cee0, tagVARIANT * 0x0012f560 {"http://www.google.cn/" VT_BSTR}) line 680

_AfxDispatchCall(void (void)* 0x5f599c20, void (void)* 0x5f599c20, void (void)* 0x5f599c20) line 43

CCmdTarget::OnEvent(unsigned int 59648, AFX_EVENT * 0x0012f330, AFX_CMDHANDLERINFO * 0x00000000) line 95 + 58 bytes

COccManager::OnEvent(CCmdTarget * 0x003850f0 {CHtmlView_tView}, unsigned int 59648, AFX_EVENT * 0x0012f330, AFX_CMDHANDLERINFO * 0x00000000) line 138

CCmdTarget::OnCmdMsg(unsigned int 59648, int -2, void * 0x0012f330, AFX_CMDHANDLERINFO * 0x00000000) line 208 + 41 bytes

CView::OnCmdMsg(unsigned int 59648, int -2, void * 0x0012f330, AFX_CMDHANDLERINFO * 0x00000000) line 162 + 24 bytes

COleControlSite::OnEvent(AFX_EVENT * 0x0012f330) line 944

COleControlSite::XEventSink::Invoke(COleControlSite::XEventSink * const 0x00385428, long 259, const _GUID & {00000000-0000-0000-0000-000000000000}, const _GUID & {00000000-0000-0000-0000-000000000000}, unsigned short 1, tagDISPPARAMS * 0x0012f510, tagVARIANT * 0x00000000 {???}, tagEXCEPINFO * 0x00000000, unsigned int * 0x00000000) line 1952

SHLWAPI! 77f62559()

SHLWAPI! 77f55e62()

SHLWAPI! 77f55dd6()

SHLWAPI! 77f55fcc()

SHDOCVW! 7e56804b()

SHDOCVW! 7e575951()

SHDOCVW! 7e578cb3()

MSHTML! 7e2a229e()

MSHTML! 7e2a30cc()

MSHTML! 7e2a2f1a()

MSHTML! 7e27ea9c()

MSHTML! 7e27e7a5()

MSHTML! 7e27cb3b()

MSHTML! 7e278937()

USER32! 77d18734()

USER32! 77d18816()

USER32! 77d2a013()

USER32! 77d2a998()

_AfxActivationWndProc(HWND__ * 0x000202f4, unsigned int 32770, unsigned int 0, long 0) line 439 + 26 bytes

USER32! 77d18734()

USER32! 77d18816()

USER32! 77d189cd()

USER32! 77d196c7()

CWinThread::PumpMessage() line 853

CWinThread::Run() line 487 + 11 bytes

CWinApp::Run() line 400

AfxWinMain(HINSTANCE__ * 0x00400000, HINSTANCE__ * 0x00000000, char * 0x00141f09, int 1) line 49 + 11 bytes

WinMain(HINSTANCE__ * 0x00400000, HINSTANCE__ * 0x00000000, char * 0x00141f09, int 1) line 30

WinMainCRTStartup() line 330 + 54 bytes

KERNEL32! 7c817067()

 
 

 

SHDOCVW是IWebBrowser2所在的DLL,CWinThread::PumpMessage()是MFC封装的消息循环。从上面的调用栈可以看出,当OnDocumentComplete()被调用时,确实是主线程先收到消息,再由IWebBrowser2控件处理,由IWebBrowser2最后调用到CHtmlView::DocumentComplete()。

 

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/zliner/archive/2008/10/19/3100668.aspx

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值