经常有人用CDHtmlDialog开发纯网页的对话框。这里首先要解决的是“程序”与“网页”之间的通信问题。“程序”访问“网页”可以使用IWebBrowser2接口。“网页”访问“程序”可以在脚本中调用window.external的方法。对于“网页”而言,暂且将前者称为“被动模式”,后者称为“主动模式”。于是,“程序”在建立一个“网页”对话框时,向“网页”提供一个自定义的external对象,就解决了二者之间的通信问题了。
在简单的应用中,上述方案是有效的。但有一个约束性条件:就是“网页”中的代码都是可控的。如果“网页”允许通过iframe元素加载其他来源的子页面时,就会出现安全隐患了。因为当前网页中所有iframe中的子页面也都能访问window.external的方法。那可不可以为每一个“网页”建立一个单独的external对象呢?经测试是不行的。也就是说,“程序”内嵌的“WebBrowser控件”一次只能设置一个external对象,为当前“网页”和其下所有的“子页面”服务。而且,在external对象内部也无法判断此次调用的来源。
方案二:直接提供类似window的全局对象
因为“程序”可以使用IWebBrowser2接口访问“网页”,那么我们可以直接创建一个自定义对象,并将其设为“网页”的全局对象,就像window一样。下面演示如何为“网页”提供一个page对象。
- //IWebBrowser2接口是已知的
- CComPtr<IWebBrowser2> m_pWebBrowser2;
- //得到document
- CComPtr<IDispatch> pDocDisp;
- m_pWebBrowser2->get_Document(&pDocDisp);
- CComQIPtr<IHTMLDocument2> pDoc=pDocDisp;
- //得到window
- CComPtr<IHTMLWindow> pWindow;
- pDoc->get_parentWindow(&pWindow);
- //取得window的IDispatchEx接口 (IDispatchEx是动态脚本语言的基础,请参阅:http://blog.csdn.net/pimshell/archive/2008/08/04/2768182.aspx)
- CComQIPtr<IDispatchEx> pObject=pWindow;
- //创建page对象
- CComObject<CPage>* pPage;
- CComObject<CPage>::CreateInstance(&pPage);
- //为pObject生成 page变量 的 dispid
- DISPID dispid;
- CComBSTR bstrName=L"page";
- pObject->GetDispID(bstrName,fdexNameEnsure,&dispid);
- //为page变量赋值
- VARIANT var;
- DISPID putid;
- DISPPARAMS dispparams, dispparamsNoArgs = {NULL, NULL, 0, 0};
- putid = DISPID_PROPERTYPUT;
- var.vt = VT_DISPATCH;
- var.pdispVal = (IDispatch*)pPage;
- dispparams.rgvarg = &var;
- dispparams.rgdispidNamedArgs = &putid;
- dispparams.cArgs = 1;
- dispparams.cNamedArgs = 1;
- pObject->InvokeEx(dispid, LOCALE_USER_DEFAULT,
- DISPATCH_PROPERTYPUT | DISPATCH_PROPERTYPUTREF,
- &dispparams,
- NULL, NULL, NULL);
- //这样在脚本中就可以直接使用page了。如,page.dosomething(params);
这种方案的好处是可以为每个网页及其下的子页面设置单独的自定义对象,从而细致的控制安全性问题。
弊端:设置“全局对象”的时机不好把握。如果是在OnDocumentComplete事件中执行,当用户刷新页面时,这个事件不会被再次调用。而且,也没有资料表明可以截获“刷新”事件。这样的话,“网页”也就无法通过“全局对象”来访问“程序”了。
方案三:主动模式--控件(ActiveX)和行为(Behavior)
一开始我们说了,对于“网页”而言,“程序”访问“网页”称为“被动模式”,反之称为“主动模式”。前面的两个方案都是“被动模式”,由“程序”提供路径,以便可以被“网页”访问。
而“主动模式”是由“网页”在内部创建“程序”的自定义对象。自定义对象可以针对网页单独创建,而且也可以设置“全局对象”,这样就自然避免了“被动模式”的弊端。“主动模式”主要有两种方式:控件(ActiveX)和行为(Behavior)。Adobe AIR和MS Silverlight采用的是ActiveX。这种方式就是为网页提供了一个功能丰富的DOM元素。优点是性能高,缺点是ActiveX的界面与html的界面并不一致,体验也不同,需要额外的开发技术。PIMShell采用的是Behavior。这种方式就是为网页现有的DOM元素提供额外的属性、方法和事件。比如,为DIV元素附加一个HTMLEditor行为,就可以通过DIV元素实现网页编辑器的功能了。优点是,继续沿用现有的html/javascript技术,缺点是性能略逊于ActiveX。