• ActiveX异步回调JavaScript


    ActiveX异步回调JavaScript

          开发环境:VC6.0

          背景知识:COM/ActiveX/JavaScript/MFC/Thread

     

          想必用过Ajax的童鞋们都知道xmlhttp这个东西吧,通过设定onreadystatechange属性,我们就可以指定他状态改变的回调函数,当状态改变时,ActiveX控件就会调用我们通过onreadystatechange属性制定的回调函数。从而就出现了Ajax给我们带来的精彩。关于Ajax的技术我们这里不做讨论,我们的目的就是实现像xmlhttp这样具有异步回调JavaScript功能的ocx控件来。

     

          Let’s go!

     

    1.      建立MFC ActiveX Control(方法略)

    2.      ClassWizard中添加属性callbackfunction属性,并为该属性生成getset方法。我们将在ActiveX控件中开启线程,线程执行完后将调用通过该属性执行的JavaScript函数。在该实例中,通过callbackfunction属性指定的JavaScript函数必须是返回值是void的,并且含有一个short类型的参数的函数。

    3.      我们需要一个方法来触发回调函数,添加方法Invoke包含一个short类型的参数param。在这个函数里将开启一个线程进行运算,然后返回计算结果。并把结果以回调函数的形式调用JavaScript的函数。

    4.      Invoke方法中开启线程。进行计算。线程同步的方法采用PostMessage自定义消息。这个很重要,否则的话,我们在线程中操作界面控件是不正确的。(我就是忘记了进行线程同步才多走了好多弯路)

    #define WM_THREADFIREEVENT WM_USER+101

    void f(void * r)

    {

          CThirdCtrl* p = (CThirdCtrl*)r;

          Sleep(5000);

          p->m_param +=10;

           PostMessage(p->m_hWnd,WM_THREADFIREEVENT,(WPARAM)NULL,(LPARAM)NULL);

          return;

    }

     

    void CThirdCtrl::invoke(short param)

    {

          m_param = param;

          _beginthread(f, 0, (void*)(this));

    }

     

    5.      添加THREADFIREEVENT消息的消息映射函数:

    ON_MESSAGE(WM_THREADFIREEVENT,OnFireEventForThread)

     

          6.   实现函数OnFireEventForThread

    LRESULT CThirdCtrl::OnFireEventForThread(WPARAM wParam, LPARAM lParam)

    {

       //FireLengthyProcessDone();

          InvokeScript ();

          return TRUE;

    }

    7.  在实现InvokeScript前,先说一个重要的东西,就是OnSetClientSite这是一个CThirdCtrl的父类ColeControl的一个虚方法。我们需要重写他来获得IWebBrowser2指针,有了IWebBrowser2我们就可以为所欲为了。比方说获得document对象,获得html中的elements,设定他们的属性,调用方法。也可以执行页面中的JavaScript函数。

         为获得顶层IWebBrowser2引用,从客户站点获取IServiceProvider接口并且执行一个QueryService 操作获取IID_IServiceProvider服务:SID_STopLevelBrowser (这在Shlguid.h中定义);对第二个IServiceProvider,执行一个QueryService获取IID_IWebBrowser2服务:SID_SWebBrowserApp.

         上代码:

    void CThirdCtrl::OnSetClientSite()

    {

         

         IOleClientSite*  pClientSite  =  GetClientSite(); 

         

         HRESULT  hr  =  S_OK; 

         IServiceProvider  *isp,  *isp2  =  NULL;//用于导航DHTML对象层次,作用就是提供服务 

         

         if  (!pClientSite) 

         

          if(browser!=NULL)

          {

                 browser->Release();

                 browser = NULL;

          }

          return;//  !S_OK; 

         

         else 

         

          hr  =  pClientSite->QueryInterface(IID_IServiceProvider,  reinterpret_cast<void  **>(&isp)); 

          if  (FAILED(hr))   

          

                 hr  =  S_OK; 

                 goto  cleanup; 

          

          

          hr  =  isp->QueryService(SID_STopLevelBrowser,  IID_IServiceProvider,  reinterpret_cast<void  **>(&isp2)); 

          if  (FAILED(hr)) 

          

                 hr  =  S_OK; 

                 goto  cleanup; 

          

          

          //获得浏览器 

          hr  =  isp->QueryService(SID_SWebBrowserApp,  IID_IWebBrowser2,  reinterpret_cast<void  **>(&browser));

          if  (FAILED(hr))   

          

                 hr  =  S_OK; 

                 goto  cleanup; 

          

          

    cleanup: 

          //  Free  resources. 

          if(isp!=NULL)

          {

                 isp->Release();

                 isp = NULL;

          }

          if(isp2!=NULL)

          {

                 isp2->Release();

                 isp2 = NULL;

          }

          return;//  hr; 

         

         

         return;//  hr;

         

    }

                 同样的道理,如果我们是ATL做的ActiveX,则需要重写

    STDMETHODIMP  CThirdCtrl::SetClientSite()

    这个方法。

    8.   下面就是最关键的InvokeScript函数的实现,我们在这里使用上面获取到的IWebBrowser2指针来获取document对象,然后获取Idispatch接口的script对象,然后调用Idispatch接口的Invoke方法。就可以调用JavaScript了。Idispatch接口真是强大啊。

    废话少说,上代码:

    void  CThirdCtrl::InvokeScript() 

    if(!browser) 

           if(browser!=NULL)

           {

                  browser->Release();

                  browser = NULL;

           }

           return; 

    CComPtr<IHTMLDocument2> m_spDoc; 

    HRESULT hr = browser->get_Document((IDispatch**)&m_spDoc);   

    if(FAILED(hr)) 

           throw(""); 

    CComPtr<IDispatch> pScript; 

    hr = m_spDoc->get_Script(&pScript); 

    if(FAILED(hr)) 

           throw(""); 

    CComBSTR  bstrMember(m_callbackfunction);   

    DISPID  dispid; 

    hr=pScript->GetIDsOfNames(IID_NULL,&bstrMember,1,LOCALE_SYSTEM_DEFAULT,&dispid); 

    //  设置函数参数 

    DISPPARAMS  dispparams; 

    memset(&dispparams,0,sizeof(dispparams)); 

    dispparams.cArgs = 1;//表示参数的计数。 

    dispparams.rgvarg = new VARIANT[dispparams.cArgs];//表示对参数数组的引用。 

    for(int i = 0; i < 1; i++) 

           //CComBSTR bstr = "111";  //  back  reading 

           //bstr.CopyTo(&dispparams.rgvarg[i].bstrVal); 

           dispparams.rgvarg[i].iVal = m_param;

           dispparams.rgvarg[i].vt = VT_I2; 

    dispparams.cNamedArgs =0;//表示命名参数的计数。 

    EXCEPINFO excepInfo; 

    memset(&excepInfo,0,sizeof(excepInfo)); 

    CComVariant vaResult; 

    UINT nArgErr = (UINT)-1;  //  initialize  to  invalid  arg 

    hr = pScript->Invoke(dispid, IID_NULL,0,DISPATCH_METHOD,&dispparams,&vaResult,&excepInfo,&nArgErr); 

    这样,ActiveX控件就完成了。

    9.      编写html页面代码。打开Microsoft ActiveX Control Pad,插入控件。然后编写JavaScript代码。

    <HTML>

    <HEAD>

    <TITLE>New Page</TITLE>

    </HEAD>

    <BODY>

       <SCRIPT LANGUAGE="JavaScript" >

    function invoke()

    {

           Third1.callbackfunction = "callback";

           Third1.invoke(2);

           alert("begin invoke");

    }

    function callback(param)

    {

           alert(param);

    }

     

       </SCRIPT>

    <OBJECT ID="Third1" WIDTH=100 HEIGHT=51

     CLASSID="CLSID:E9D38528-0F4E-468B-858D-69905F16942F">

       <PARAM NAME="_Version" VALUE="65536">

       <PARAM NAME="_ExtentX" VALUE="2646">

       <PARAM NAME="_ExtentY" VALUE="1323">

       <PARAM NAME="_StockProps" VALUE="0">

    </OBJECT>

    <input type="button" value="test" onclick="invoke();" />

    </BODY>

    </HTML>

    10.  测试:打开浏览器,打开test.html页面。点击“test“按钮,将会先显示对话框begin invoke,然后过5秒钟再显示对话框12

    11.  调试方法:我们可以直接调试浏览器。浏览器加载了控件,然后我们调用控件的方法,这时会自动触发我们在工程中设置的断点。在

    project---settings---debug---executable for debug sessions设置浏览器的exe文件的路径。我用的世界之窗浏览器。所以值设置为:C:Program FilesTheWorldTheWorld.exe

    如果你用IE浏览器,可设置为:C:Program FilesInternet Exploreriexplore.exe

     

    说明:

    1.   上述控件与xmlhttp不同的地方是callbackfunction我传的是一个字符串,而xmlhttp传的是一个JavaScript的函数指针。

    2.   COM中的线程模型不在本文讨论范围之内。还有浏览器安全问题和打包CAB的问题也不在本文讨论范围之内。

    参考:

    http://vcfaq.mvps.org/com/1.htm

    http://vcfaq.mvps.org/com/11.htm

    http://support.microsoft.com/kb/q157437/

  • 相关阅读:
    Oracle日期周具体解释以及周開始结束时间计算
    getActivity nullPointerException
    dTree无限级文件夹树和JQuery同步Ajax请求
    按键精灵微信群内加好友
    QPS、RT、PV、UV之间的关系
    [简谈]绕过HR破门而入的求职智慧
    Mina、Netty、Twisted一起学(十):线程模型
    ubuntu查看硬件信息
    openstack API debug OpenstackEveryProject_CLI,curl_based
    openstack configure
  • 原文地址:https://www.cnblogs.com/lidabo/p/3263036.html
Copyright © 2020-2023  润新知