乡下人产国偷v产偷v自拍,国产午夜片在线观看,婷婷成人亚洲综合国产麻豆,久久综合给合久久狠狠狠9

  • <output id="e9wm2"></output>
    <s id="e9wm2"><nobr id="e9wm2"><ins id="e9wm2"></ins></nobr></s>

    • 分享

      WebBrowser介紹——Javascript與C 互操作

       看見就非常 2012-09-17

      WebBrowser控件是Microsoft提供的一個(gè)用于網(wǎng)頁瀏覽的客戶端控件,WebBrowser控件的使用相當(dāng)廣泛,例如很多郵件客戶端都是使用可編輯的WebBrowser控件作為寫郵件的工具,也有很多軟件用WebBrowser控件彈出網(wǎng)頁,如qq的個(gè)性首頁。關(guān)于WebBrowser的應(yīng)用,也可以參考筆者開發(fā)的開源WebIM,Lesktop開源WebIM提供的IM客戶端就是使用WebBrowser實(shí)現(xiàn)的:

      image

      微軟的MFC和.NET都有WebBrowser控件,這兩個(gè)控件雖然容易上手,不過由于包裝的太好,所以很難深入。因此本文介紹的WebBrowser將不使用MFC和.NET,而是使用C++實(shí)現(xiàn)SDK的WebBrowser。

      由于本文主要探討如何實(shí)現(xiàn)Javascript與C++的互操作,對于如何使用SDK實(shí)現(xiàn)WebBrowser,本文不做詳細(xì)介紹,讀者可以參考以下這篇文章:

      http://blog.csdn.net/norsd/archive/2008/09/13/2921389.aspx

      不過盡管文章中介紹了SDK實(shí)現(xiàn)WebBrowser的要點(diǎn),卻沒有提供一個(gè)可以運(yùn)行的示例,如果要看到實(shí)際的運(yùn)行效果,可以下載以下這份源代碼,源代碼中也包括了互操作的演示:

      1、C++調(diào)用WebBrowser中的全局函數(shù),變量等

      (1) 從C++的角度看WebBrowser中的對象

      WebBrowser中的對象大致可以分成兩類:DOM對象和使用Javascript創(chuàng)建的對象。但是無論是那種對象,從C++的角度來看,都是一些實(shí)現(xiàn)了IDispatch接口的對象,因此,如果用C++操作WebBrowser中的對象(全局函數(shù),變量,DOM)等,只需要通過IDispatch即可。

      (2) 3個(gè)常用的函數(shù)

      當(dāng)獲取了WebBrowser的對象的IDispatch接口后,就可以調(diào)用IDispatch的Invoke方法來調(diào)用對象的方法,獲取對象的屬性和設(shè)置對象的屬性。但是Invoke是通過ID判斷要調(diào)用指定對象的哪一個(gè)方法(或?qū)傩裕?,因此在通過方法(或?qū)傩裕┟Q調(diào)用對象的方法是,必須先調(diào)用IDispatch的GetIDsOfNames方法,將方法(或?qū)傩裕┟D(zhuǎn)換成ID,然后才能通過IDispatch的Invoke方法調(diào)用對象的方法。為了方便操作,封裝了三個(gè)函數(shù),分別用于調(diào)用WebBrowser的對象的方法,讀取對象的屬性,設(shè)置對象的屬性。

      DISPID CWebBrowserBase::FindId(IDispatch *pObj, LPOLESTR pName)
      {
          DISPID id = 0;
          if(FAILED(pObj->GetIDsOfNames(IID_NULL,&pName,1,LOCALE_SYSTEM_DEFAULT,&id))) id = -1;
          return id;
      }
      
      HRESULT CWebBrowserBase::InvokeMethod(IDispatch *pObj, LPOLESTR pName, VARIANT *pVarResult, VARIANT *p, int cArgs)
      {
          DISPID dispid = FindId(pObj, pName);
          if(dispid == -1) return E_FAIL;
      
          DISPPARAMS ps;
          ps.cArgs = cArgs;
          ps.rgvarg = p;
          ps.cNamedArgs = 0;
          ps.rgdispidNamedArgs = NULL;
      
          return pObj->Invoke(dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_METHOD, &ps, pVarResult, NULL, NULL);
      }
      
      HRESULT CWebBrowserBase::GetProperty(IDispatch *pObj, LPOLESTR pName, VARIANT *pValue)
      {
          DISPID dispid = FindId(pObj, pName);
          if(dispid == -1) return E_FAIL;
      
          DISPPARAMS ps;
          ps.cArgs = 0;
          ps.rgvarg = NULL;
          ps.cNamedArgs = 0;
          ps.rgdispidNamedArgs = NULL;
      
          return pObj->Invoke(dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_PROPERTYGET, &ps, pValue, NULL, NULL);
      }
      
      HRESULT CWebBrowserBase::SetProperty(IDispatch *pObj, LPOLESTR pName, VARIANT *pValue)
      {
          DISPID dispid = FindId(pObj, pName);
          if(dispid == -1) return E_FAIL;
      
          DISPPARAMS ps;
          ps.cArgs = 1;
          ps.rgvarg = pValue;
          ps.cNamedArgs = 0;
          ps.rgdispidNamedArgs = NULL;
      
          return pObj->Invoke(dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_PROPERTYPUT, &ps, NULL, NULL, NULL);
      }

      (3)調(diào)用頁面的全局函數(shù)

      在網(wǎng)頁中,所有的全局函數(shù)均是window的一個(gè)方法,因此,如果要調(diào)用全局函數(shù),首先要獲取到頁面的window對象,然后用InvokeMethod調(diào)用全局函數(shù),例如,假設(shè)頁面中有一個(gè)Test全局函數(shù):

      <script language="javascript" type="text/javascript">
      function Test()
      {
          alert("你調(diào)用了Test");
      }
      </script>

      那么,您可以在C++中用以下代碼調(diào)用Test函數(shù):

      VARIANT params[10];
      VARIANT ret;
      //獲取頁面window
      IDispatch *pHtmlWindow = pBrowser->GetHtmlWindow();
      //頁面全局函數(shù)Test實(shí)際上是window的Test方法,
      CWebBrowserBase::InvokeMethod(pHtmlWindow, L"Test", &ret, params, 0);

      (4)調(diào)用全局對象的方法

      在網(wǎng)頁中,所有的全局變量均是window的一個(gè)屬性,因此,如果要調(diào)用變量的方法(或?qū)傩裕?,首先要獲取到頁面的window對象,然后用GetProperty獲取到全局變量,然后就可以調(diào)用這個(gè)對象的方法,或讀寫其屬性。例如,假設(shè)頁面中有一個(gè)globalObject全局變量:

      <script language="javascript" type="text/javascript">
      function GlobalObject()
      {
          this.Test=function()
          {
              alert("你調(diào)用了GlobalObject.Test");
          }
      }
      
      var globalObject = new GlobalObject();
      </script>

      那么,您可以使用一下代碼調(diào)用globalObject的Test方法:

      VARIANT params[10];
      VARIANT ret;
      //獲取頁面window
      IDispatch *pHtmlWindow = pBrowser->GetHtmlWindow();
      //獲取globalObject
      CVariant globalObject;
      params[0].vt = VT_BSTR;
      params[0].bstrVal = L"globalObject";
      CWebBrowserBase::GetProperty(pHtmlWindow, L"globalObject", &globalObject);
      //調(diào)用globalObject.Test
      CWebBrowserBase::InvokeMethod(globalObject.pdispVal, L"Test", &ret, params, 0);

      2、在網(wǎng)頁中調(diào)用客戶端的方法

      上文我們已經(jīng)介紹了如何在C++中調(diào)用WebBrowser中的對象,接下來,將介紹如何在頁面中調(diào)用客戶端中的函數(shù)和對象:

      (1) 通過window.external調(diào)用

      下面將示例如何通過window.external調(diào)用客戶端中的函數(shù),假設(shè)在C++中定義了一個(gè)名為CppCall的函數(shù):

      void CppCall()
      {
          MessageBox(NULL, L"您調(diào)用了CppCall", L"提示(C++)", 0);
      }

      定義一個(gè)對象,并且實(shí)現(xiàn)IDispatch接口:

      class ClientCall:public IDispatch
      {
          long _refNum;
      public:
          ClientCall()
          {
              _refNum = 1;
          }
          ~ClientCall(void)
          {
          }
      public:
      
          // IUnknown Methods
      
          STDMETHODIMP QueryInterface(REFIID iid,void**ppvObject)
          {
              *ppvObject = NULL;
              if (iid == IID_IUnknown)    *ppvObject = this;
              else if (iid == IID_IDispatch)    *ppvObject = (IDispatch*)this;
              if(*ppvObject)
              {
                  AddRef();
                  return S_OK;
              }
              return E_NOINTERFACE;
          }
      
          STDMETHODIMP_(ULONG) AddRef()
          {
              return ::InterlockedIncrement(&_refNum);
          }
      
          STDMETHODIMP_(ULONG) Release()
          {
              ::InterlockedDecrement(&_refNum);
              if(_refNum == 0)
              {
                  delete this;
              }
              return _refNum;
          }
      
          // IDispatch Methods
      
          HRESULT _stdcall GetTypeInfoCount(
              unsigned int * pctinfo) 
          {
              return E_NOTIMPL;
          }
      
          HRESULT _stdcall GetTypeInfo(
              unsigned int iTInfo,
              LCID lcid,
              ITypeInfo FAR* FAR* ppTInfo) 
          {
              return E_NOTIMPL;
          }
      
          HRESULT _stdcall GetIDsOfNames(
              REFIID riid, 
              OLECHAR FAR* FAR* rgszNames, 
              unsigned int cNames, 
              LCID lcid, 
              DISPID FAR* rgDispId 
          )
          {
              if(lstrcmp(rgszNames[0], L"CppCall")==0)
              {
                  //網(wǎng)頁調(diào)用window.external.CppCall時(shí),會調(diào)用這個(gè)方法獲取CppCall的ID
                  *rgDispId = 100;
              }
              return S_OK;
          }
      
          HRESULT _stdcall Invoke(
              DISPID dispIdMember,
              REFIID riid,
              LCID lcid,
              WORD wFlags,
              DISPPARAMS* pDispParams,
              VARIANT* pVarResult,
              EXCEPINFO* pExcepInfo,
              unsigned int* puArgErr
          )
          {
              if(dispIdMember == 100)
              {
                  //網(wǎng)頁調(diào)用CppCall時(shí),或根據(jù)獲取到的ID調(diào)用Invoke方法
                  CppCall();
              }
              return S_OK;
          }
      };

      定義類ClientCall后,就可以創(chuàng)建一個(gè)ClientCall的對象,傳遞給WebBrowser,使得網(wǎng)頁中可以通過window.external調(diào)用CppCall,要實(shí)現(xiàn)這些功能,WebBrowser需要實(shí)現(xiàn)IDocHostUIHandler接口,并重寫GetExternal方法以返回一個(gè)ClientCall對象:

      ClientCall *pClientCall;
      pClientCall = new ClientCall();
      
      virtual HRESULT STDMETHODCALLTYPE GetExternal(IDispatch **ppDispatch)
      {
          //重寫GetExternal返回一個(gè)ClientCall對象
          *ppDispatch = pClientCall;
          return S_OK;
      }

      接下來,就可以在網(wǎng)頁中調(diào)用了:

      window.external.CppCall()
      (2)向網(wǎng)頁傳遞回調(diào)函數(shù)

      向網(wǎng)頁傳遞回調(diào)函數(shù)的一個(gè)典型應(yīng)用就是在客戶端中用C++處理DOM的事件(例如,處理按鈕的onclick事件),這里要注意的是,與C++不同的是,在網(wǎng)頁中,所謂的函數(shù),其實(shí)就是一個(gè)具有call方法的對象,因此,向網(wǎng)頁傳遞一個(gè)回調(diào)函數(shù),其實(shí)就是傳遞一個(gè)實(shí)現(xiàn)了call方法的對象,因此,我們必須定義一個(gè)C++類,并實(shí)現(xiàn)IDispatch接口:

      typedef void _stdcall JsFunction_Callback(LPVOID pParam);
      
      class JsFunction:public IDispatch
      {
          long _refNum;
          JsFunction_Callback *m_pCallback;
          LPVOID m_pParam;
      public:
          JsFunction(JsFunction_Callback *pCallback, LPVOID pParam)
          {
              _refNum = 1;
              m_pCallback = pCallback;
              m_pParam = pParam;
          }
          ~JsFunction(void)
          {
          }
      public:
      
          // IUnknown Methods
      
          STDMETHODIMP QueryInterface(REFIID iid,void**ppvObject)
          {
              *ppvObject = NULL;
      
              if (iid == IID_IOleClientSite)    *ppvObject = (IOleClientSite*)this;
              else if (iid == IID_IUnknown)    *ppvObject = this;
              if(*ppvObject)
              {
                  AddRef();
                  return S_OK;
              }
              return E_NOINTERFACE;
          }
      
          STDMETHODIMP_(ULONG) AddRef()
          {
              return ::InterlockedIncrement(&_refNum);
          }
      
          STDMETHODIMP_(ULONG) Release()
          {
              ::InterlockedDecrement(&_refNum);
              if(_refNum == 0)
              {
                  delete this;
              }
              return _refNum;
          }
      
          // IDispatch Methods
      
          HRESULT _stdcall GetTypeInfoCount(
              unsigned int * pctinfo) 
          {
              return E_NOTIMPL;
          }
      
          HRESULT _stdcall GetTypeInfo(
              unsigned int iTInfo,
              LCID lcid,
              ITypeInfo FAR* FAR* ppTInfo) 
          {
              return E_NOTIMPL;
          }
      
          HRESULT _stdcall GetIDsOfNames(
              REFIID riid, 
              OLECHAR FAR* FAR* rgszNames, 
              unsigned int cNames, 
              LCID lcid, 
              DISPID FAR* rgDispId 
          )
          {
              //令人費(fèi)解的是,網(wǎng)頁調(diào)用函數(shù)的call方法時(shí),沒有調(diào)用GetIDsOfNames獲取call的ID,而是直接調(diào)用Invoke
              return E_NOTIMPL;
          }
      
          HRESULT _stdcall Invoke(
              DISPID dispIdMember,
              REFIID riid,
              LCID lcid,
              WORD wFlags,
              DISPPARAMS* pDispParams,
              VARIANT* pVarResult,
              EXCEPINFO* pExcepInfo,
              unsigned int* puArgErr
          )
          {
              m_pCallback(m_pParam);
              return S_OK;
          }
      };

      接下來,我們就可以使用JsFunction向網(wǎng)頁傳遞回調(diào),以下代碼用于處理按鈕的onclick事件:

      static void _stdcall button1_onclick(LPVOID pParam)
      {
          MainForm *pMainForm = (MainForm*)pParam;
          MessageBox(pMainForm->hWnd, L"您點(diǎn)擊了button1", L"提示(C++)", 0);
      }
      
      VARIANT params[10];
      
      //獲取window
      IDispatch *pHtmlWindow = GetHtmlWindow();
      
      //獲取document
      CVariant document;
      params[0].vt = VT_BSTR;
      params[0].bstrVal = L"document";
      GetProperty(pHtmlWindow, L"document", &document);
      
      //獲取button1
      CVariant button1;
      params[0].vt = VT_BSTR;
      params[0].bstrVal = L"button1";
      InvokeMethod(document.pdispVal, L"getElementById", &button1, params, 1);
      
      //處理button1的onclick事件
      params[0].vt = VT_DISPATCH;
      params[0].pdispVal = new JsFunction(button1_onclick, this);
      SetProperty(button1.pdispVal, L"onclick", params);

      以上就是筆者開發(fā)Lesktop開源WebIM時(shí)使用WebBrowser的經(jīng)驗(yàn)總結(jié),如有紕漏,敬請指出。

        本站是提供個(gè)人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊一鍵舉報(bào)。
        轉(zhuǎn)藏 分享 獻(xiàn)花(0

        0條評論

        發(fā)表

        請遵守用戶 評論公約

        類似文章 更多