0

 对于整个的交互过程不是很了解,在网上收罗了一大批资料,搞的云里雾里的。

最终还是把它做出来啦。献给和我一样奋斗在code前的人们。


    javascript 中调用VC 中的类或方法大概有两种(我只做了第二种):

    第一种是基于事件的模型,使用DHTML_EVENT_MAP()来实现,例如一个html界面的button被点击,可以使用 DHTML_EVENT_ONCLICK(BUTTONID,FuncHandle)的方式进行捕获。在FuncHandle的参数里面,可以获相应的控件句柄,便可以获取并操作控件的各种属性

    第二种是基于com接口。使用BEGIN_DISPATCH_MAP将相应的函数接口暴露的javascript,javascript调用的时候就可以使用window.external.函数名进行调用。


这里我讲一下第二种方式的实现:

     第一步:让CDHtmlDialog 对象支持自动化(这个具体什么叫自动化很复杂,这里就不说了,我也说不清)

                     在自己的CDHtmlDialog的派生类的构造函数中加入如下代码,以支持自动化。

              

  1. EnableAutomation();//让框架自身支持自动化  
               

  1. CHtmlTestDlg::CHtmlTestDlg(CWnd* pParent /*=NULL*/)  
  2.     : CDHtmlDialog(CHtmlTestDlg::IDD, CHtmlTestDlg::IDH, pParent)/*IDH 定义了开始时候的html资源号,并且让网页被加载,如果参数设为0,将不加载网页*/  
  3. {  
  4.     EnableAutomation();//让框架自身支持自动化  
  5.     m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);  
  6. }  
    第二步 :将自身暴露给Script 引擎:

                      将如下代码放入CHtmlTestDlg(CDhtmlDialog派生类)的OnInitDialog中

  1. // TODO: 在此添加额外的初始化代码     
  2. SetExternalDispatch(GetIDispatch(TRUE));  
    第三步:在CHtmlTestDialog头文件(HtmlTestDlg.h)中申明映射

    

  1. DECLARE_DISPATCH_MAP()  //声明Dispatch map  

   第四步: 在HtmlTestDlg.cpp中定义DISPATCH 映射

    

  1. //添加DISPATCH_MAP 映射关系  
  2. BEGIN_DISPATCH_MAP(CHtmlTestDlg,CDHtmlDialog)  
  3.     DISP_FUNCTION(CHtmlTestDlg,"Func1",Func1,VT_EMPTY,VTS_NONE)  
  4. END_DISPATCH_MAP()  
这里如果想把某一个方法暴露给外部文件(这里是.htm),就把它添加到上面的消息映射中去。

函数的四个DISP_FUNCTION的参数可以查到。第一个参数就是当前类名,第二,三个是方法名,后面是参数,和返回值

其中func1方法在类中定义如下(注意要在头文件中声明):

  1. void CHtmlTestDlg::Func1(){  
  2.   
  3.     AfxMessageBox((CString)"very good");  
  4. }  

  第五步:可以在js中调用此方法啦:

                我这里是如下代码,双击响应这个函数

[javascript] view plaincopy
  1. GEvent.addListener(marker,"dblclick",function(){window.external.Func1()});  

或者网页调用:

  1. <input id="Btn1" type="button" value="Btn1" name="Btn1" onclick="external.Func1();" /> 




CDHtmlDialog----Javascript


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  


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();">

关闭 返回顶部
联系我们
Copyright © 2011. 聚财吧. All rights reserved.