CDHtmlDialog提供了C++与网页的双向交互,通此一系统简单的宏调用可以把网页中各元素的事件直接映射到C++程序中,而在网页中调用C++功能代码就显的不那么直观了。归根结底交互的基理就是实现相应COM接口。实现方式如下:
1、在窗体初始化时调用EnableAutomation函数。
通常情况下是放在窗体的构造函数中,
EnableAutomation();
当然也可以放在OnInitDialog中,不过要注意顺序,其调用不应该晚于对SetExternalDispatch的调用。此函数在底层的实现就是引用到正确的IDispatch接口(实际就是定位了相应的vtable指针),IDispatch是OLE自动化程序实现的根基。
2、在窗体初始化时调用SetExternalDispatch函数。
通常情况下放在OnInitDialog事件中。
SetExternalDispatch(GetIDispatch(TRUE));
在这个地方就用到了IDispatch接口。所以必须保证EnableAutomation是在SetExternalDispatch之前调用。调用此函数是对网页公布其窗口容器的接口从而在网页中可以通过window.external来调用窗口容器公布的函数、事件、属性。
3、在头文件中添加DECLARE_DISPATCH_MAP()
这个宏定义了对外公布信息需要使用的一些内部数据结构和操作。
4、在实现文件中(*.cpp)添加具体的信息映射
BEGIN_DISPATCH_MAP(CBrowserDlg, CDHtmlDialog)
DISP_FUNCTION(CBrowserDlg,"testfun",TestFunction,VT_EMPTY,VTS_VARIANT VTS_VARIANT)
END_DISPATCH_MAP()
testfun是对网页公布的函数名称,TestFunction是在CBrowserDlg的成员函数,VT_EMPTY表示此函数没有返回值,VTS_VARIANT表示函数参数,多个参数之间使用空格。需要注意的一点是js或vbs这类的脚本语言的数据类型都对应于COM中的变体类型,假如说testfun函数传递两个参数分别是整型和字符串类型(示例中的两个参数都当做字符串来处理),在定义的时候可以使用VTS_I4和VTS_VARIANT来表示这两个参数的类型,虽然VTS_PBSTR是用于表示字符串的但却不对应脚本语言的字符串类型所以应该使用VTS_VARIANT,使用VTS_VARIANT来代替VTS_I4也是正确的。其它复杂数据类型比如说对象等都应用使用VTS_VARIANT数据类型表示。
5、实现对外公布的函数(在此示例中是TestFunction)
view sourceprint?
01 void CBrowserDlg::TestFunction(VARIANT& vStr1,VARIANT& vStr2)
02 {
03 CComVariant varStr1(vStr1),varStr2(vStr2);
04 varStr1.ChangeType(VT_BSTR);
05 varStr2.ChangeType(VT_BSTR);
06 USES_CONVERSION;
07 CString strMsg;
08 strMsg.Format(_T("varStr1:%s,varStr2:%s"),OLE2T(varStr1.bstrVal),OLE2T(varStr2.bstrVal));
09 AfxMessageBox(strMsg);
10 }
6、在网页中调用窗体容器公布的函数
view sourceprint?
1 <script language="javascript" type="text/javascript"><br>// <!CDATA[<br> window.external.testfun("one","two");<br>// ]]><br> </script><br>
7、重写IsExternalDispatchSafe虚函数
重写此函数可以屏蔽掉网页弹出的ActiveX安全警告对话框。重写CanAccessExternal函数也可以达到同样的目的,但是不推荐这样做,CanAccessExternal中调用IsExternalDispatchSafe并在其值为FALSE时默认做了安全检查,如果重写了此函数那么就破坏了CDHtmlDialog的封装,并且想要再重新获得安全性时就显的不太方便了。
view sourceprint?
1 virtual BOOL IsExternalDispatchSafe(){ return TRUE; }
CDHtmlDialog可以方便的将网页嵌入对话框,使得在程序设计中人机界面(DHTML网页)与控制逻辑(CDialog)可以很好的分离,下面是一些实用技术与技巧。
1.将数据验证任务完全交给JavaScript,Dialog只做有意义的事。
<input type="button" id="button1" onclick="if(validate()); window.event.cancelBubble=true;" />
这样,事件由IE处理之后,就不会将该事件传给其他事件句柄处理了,因为IE先于CDHtmlDialog处理该事件。
2.从CDHtmlDialog调用网页中JavaScript函数的方法。
其中pDoc指针参数可通过CDHtmlDialog::GetDHtmlDocument(&pDoc)函数获得;
strFunctionName指示函数名;
dispParams为传给函数的参数列表,其使用方法请查阅MSDN相关文档;
varResult为函数返回值;
exceptInfo为JavaScript函数执行时抛出的异常;
nArgErr返回第一个出错的参数的下标,由于参数列表中参数的逻辑顺序为JavaScript函数定义的参数的顺序的逆序,所以应特别注意该返回值所指示的具体位置。
HRESULT CallJSFunction(IHTMLDocument2* pDoc2,
CString strFunctionName,
DISPPARAMS dispParams,
VARIANT* varResult,
EXCEPINFO* exceptInfo,
UINT* nArgErr )
{
IDispatch *pDispScript = NULL;
HRESULT hResult;
hResult = pDoc2->get_Script(&pDispScript);
if(FAILED(hResult))
{
return S_FALSE;
}
DISPID dispid;
CComBSTR objbstrValue = strFunctionName;
BSTR bstrValue = objbstrValue.Copy();
OLECHAR *pszFunct = bstrValue ;
hResult = pDispScript->GetIDsOfNames(IID_NULL,
&pszFunct,
1,
LOCALE_SYSTEM_DEFAULT,
&dispid);
if (S_OK != hResult)
{
pDispScript->Release();
return hResult;
}
varResult->vt = VT_VARIANT;
hResult = pDispScript->Invoke(dispid,
IID_NULL, LOCALE_USER_DEFAULT,
DISPATCH_METHOD,
&dispParams,
varResult,
exceptInfo,
nArgErr);
pDispScript->Release();
return hResult;
}
3.CDHtmlDialog中JavaScript通过external调用C++方法。
<1>让CDHtmlDialog对象自身支持自动化
EnableAutomation(); //只要是从CCmdTarget派生下来的类都可以支持
//可以放在CMyDHTMLDialog::CMyDHTMLDialog()中调用
<2>将自身暴露给Script引擎:
SetExternalDispatch(GetIDispatch(TRUE)); //将浏览器控件的扩展接口设置为对话框自身的IDispatch
//放在CMyDHTMLDialog::OnInitDialog中调用
<3>声明DISPATCH_MAP(MyDHTMLDialog.h)
DECLARE_DISPATCH_MAP()
<4>定义DISPATCH映射(MyDHTMLDialog.cpp)
BEGIN_DISPATCH_MAP(CMyDHtmlDialog, CDHtmlDialog)
DISP_FUNCTION(CMyDHTMLDialog, "Func1", Func1, VT_EMPTY, VTS_NONE)
// example:
// DISP_FUNCTION(CMyDHTMLDialog,"Func2",TestFunc,VT_BOOL,VTS_BSTR VTS_I4 VTS_I4)
// ^return, ^parameters type list
//每个方法都需要在这里添加映射
END_DISPATCH_MAP()
<5>函数实现
void CMyDHTMLDialog::Func1()
{
AfxMessageBox(_T("Func1 called!"));
}
<6>调用示例
<INPUT id="Button1" type="button" value="Button1" name="Button1" onclick="external.Func1();">