C++和JavaScript脚本的相互调用

脚本调用C++相对比较容易,使用ATL组件只需要抛双接口即可,但在exe里如何做到呢?本文实现了在exe里脚本和C++的相互调用.在EXE里也需要对外抛送一个继承自IDispatch的接口.并需要重载它的所有接口。由于水平有限,所以难免有错。
 // 头文件
 static const GUID IID_CExternal =
 { 0x52fee9af, 0xb3b3, 0x4756, { 0x80, 0x10, 0xfe, 0xa8, 0xf9, 0xfd, 0xd3, 0x3f } };
 
 
 class CExternal:public IDispatch
 {
 public:
     CExternal(HWND h);
     virtual ~CExternal();
     ULONG __stdcall AddRef() { return 1; }
     ULONG __stdcall Release() {   return 1; }
 
       HRESULT __stdcall QueryInterface(REFIID riid, void FAR* FAR* ppv)
       {
          if (ppv == NULL)
             return E_POINTER;
          *ppv = NULL;
          if (InlineIsEqualGUID(riid, IID_IUnknown))
          {
             *ppv = static_cast<IUnknown *>(this);
             return S_OK;
          }
          if(InlineIsEqualGUID(riid, IID_IDispatch) )
          {
             *ppv = static_cast<IDispatch FAR *>(this);
             return S_OK;
          }
          if(InlineIsEqualGUID(riid, IID_CExternal) )
          {
             *ppv = static_cast<CExternal *>(this);
             return S_OK;
          }
          return E_NOINTERFACE;
       }
 
     HRESULT __stdcall GetTypeInfoCount(UINT FAR* pctinfo)
     {
       if (pctinfo == NULL)
       {
         return E_INVALIDARG;
        }
       // there is only one function
        *pctinfo = 1;
        return NOERROR;
       }
 
       HRESULT __stdcall GetTypeInfo(UINT iTInfo,LCID lcid,ITypeInfo FAR* FAR* ppTInfo)
       {
          if (ppTInfo == NULL)
             return E_INVALIDARG;
          *ppTInfo = NULL;
          if (iTInfo != 0)
             return DISP_E_BADINDEX;
          *ppTInfo = m_typeinfo;
          if (m_typeinfo!=NULL)
             m_typeinfo->AddRef();
          return NOERROR;
       }
 
       HRESULT __stdcall GetIDsOfNames(REFIID  riid,OLECHAR FAR* FAR* rgszNames, unsigned int  cNames,  LCID   lcid,DISPID FAR*rgdispid)
       {
          if(lstrcmpiW(*rgszNames,L"exec")==0)
          {
             *rgdispid=0;
             return S_OK;
          }
          return E_FAIL;
       }
 
       HRESULT __stdcall Invoke(DISPID dispidMember,REFIID riid,LCID lcid,WORD wFlags,DISPPARAMS FAR* pdispparams,VARIANT FAR* pvarResult,EXCEPINFO FAR* pexcepinfo,UINT FAR* puArgErr)
       {
          if((DISPATCH_PROPERTYGET&wFlags || DISPATCH_METHOD&wFlags)
             && dispidMember==0)
          {
             CComBSTR sRet;
             exec(pdispparams, &sRet);
             if (DISPATCH_PROPERTYGET&wFlags)
             {
                pvarResult->vt=VT_BSTR;
                pvarResult->bstrVal=sRet.Detach();
             }
             return S_OK;
          }
          return E_FAIL;
       }
        HRESULT  __stdcall exec(DISPPARAMS FAR* pdispparams, BSTR  *pbstrValue);
 
 private:
  HWND m_hWnd;
  LPTYPEINFO  m_typeinfo;
  CString GetVariantStr(VARIANT vVal);
 };
 
 // 实现的文件
 CExternal::CExternal(HWND h)
 {
  csDisplayStr = "";
  m_typeinfo =NULL;
  m_hWnd = h;
  // Create an exec function
  static PARAMDATA  PARAM_VALUE[]={{OLESTR("fnName"),VT_BSTR},{OLESTR("p1"),VT_BSTR}};
  static METHODDATA  rgmdataCCalc={OLESTR("exec"),PARAM_VALUE,0,0,CC_CDECL,2,DISPATCH_METHOD|DISPATCH_PROPERTYGET,VT_BSTR};
  static INTERFACEDATA ifdata={&rgmdataCCalc, 1};
  HRESULT hres=CreateDispTypeInfo(&ifdata, LOCALE_SYSTEM_DEFAULT, &m_typeinfo);
 
 }
 CString CExternal::GetVariantStr(VARIANT vVal)
 {
    CString csReVal;
 
 
    switch (vVal.vt)
    {
       case VT_BOOL:
       {
          if (vVal.boolVal == VARIANT_TRUE)
          {
             return("1");
          }
          else
          {
             return("0");
          }
          break;
 
       }
       case VT_I2:
       {
          csReVal.Format("%d",vVal.iVal);
          return(csReVal);
       }
 
       case VT_I4:
       {
          csReVal.Format("%d",vVal.lVal);
          return(csReVal);
       }
 
 //      case VT_R8:
 //      {
 //         //csReVal.Format("%f",vVal.dblVal);
 //         csReVal=vVal.dblVal;
 //         return(csReVal);
 //      }
 
 
       case VT_BSTR:
       {
 
          return(CString(vVal.bstrVal));
       }
    }
       return "";
 }
 
 HRESULT  __stdcall CExternal::exec(DISPPARAMS FAR* pdispparams, BSTR  *pbstrValue) // 脚本的入口点
 {
    //  no argument return
    if (pdispparams->cArgs < 1)
    {
       *pbstrValue = bstrRet.Detach();
       return S_OK;
    }
    int args = pdispparams->cArgs;
 
    // C calling convention order of parameters is in reversed
    CString action = pdispparams->rgvarg[args-1].bstrVal;
    debugIt(" exec***action:%s:%d ",action,args);
    if (action == "alert")
    {
         CString csMessage;
         CString csTitle;
 
         if (args > 1)
         {
             csMessage=  GetVariantStr(pdispparams->rgvarg[args-2]);
         }
         if (args > 2)
         {
             csTitle = GetVariantStr(pdispparams->rgvarg[args-3]);
         }
       MessageBox(m_hWnd, LPCSTR(csMessage), LPCSTR(csTitle), MB_OK);
    }
 //else if(action == "什么")
 //{
 //}
      *pbstrValue=bstrRet.Detach(); // 返回值
    return S_OK;
 }
 
 
 CExternal::~CExternal()
 {
  if(m_typeinfo) m_typeinfo->Release();
 }
 
C++调用脚本可以使用下面的代码,此代码是我在网上下载的,具体的来源我已经不记得了,但在网上应该可以找到类似的.原理是用到了WebBrowser2,但是C++和脚本的相互调用都用到了HTML页面,使用HTML页面成了包袱,能否丢掉它,我不知道如何实现,望高手指点.......
 // 头文件
 #pragma once
 #include <atlbase.h>
 #include <Mshtml.h>
 
 class CCallScript
 {
 public:
     CCallScript();
     virtual ~CCallScript();
     BOOL DocumentSet(){return(m_bDocumentSet);}
     BOOL SetDocument(IDispatch* pDisp);
     LPDISPATCH GetHtmlDocument() const;
     const CComBSTR GetLastError() const;
     BOOL GetScript(CComPtr<IDispatch>& spDisp);
     BOOL GetScripts(CComPtr<IHTMLElementCollection>& spColl);
 
     BOOL Run(const CComBSTR strFunc,CComVariant* pVarResult = NULL);
     BOOL Run(const CComBSTR strFunc,const CComBSTR strArg1,CComVariant* pVarResult = NULL);
     BOOL Run(const CComBSTR strFunc,const CComBSTR strArg1,const CComBSTR strArg2,CComVariant* pVarResult = NULL);
     BOOL Run(const CComBSTR strFunc,const CComBSTR strArg1,const CComBSTR strArg2,const CComBSTR strArg3,CComVariant* pVarResult = NULL);
     BOOL Run(const CComBSTR strFunc,const CSimpleArray<CComBSTR> & paramArray,CComVariant* pVarResult = NULL);
 private:
     BOOL m_bDocumentSet;
 protected:
 
 
     void ShowError(CComBSTR lpszText);
 
 protected:
 
     CComPtr<IHTMLDocument2>    m_spDoc;
     CComBSTR    m_strError;
 };
 
 inline void CCallScript::ShowError(CComBSTR lpszText)
 {
     m_strError = "Error: ";
     m_strError.Append(lpszText);
 }
 inline const CComBSTR CCallScript::GetLastError() const
 {
     return m_strError;
 }
 inline LPDISPATCH CCallScript::GetHtmlDocument() const
 {
     return m_spDoc;
 }
 // CPP文件
 #include "stdafx.h"
 #include "CallScript.h"
 
 #define CHECK_POINTER(p)
     ATLASSERT(p != NULL);
     if(p == NULL)
     {
         ShowError("NULL pointer");
         return FALSE;
     }
 
 const CComBSTR GetSystemErrorMessage(DWORD dwError)
 {
     CComBSTR strError;
     LPTSTR lpBuffer;
 
     if(!FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
             NULL,  dwError,
             MAKELANGID(LANG_NEUTRAL, SUBLANG_SYS_DEFAULT),
             (LPTSTR) &lpBuffer, 0, NULL))
 
     {
         strError = "FormatMessage Netive Error" ;
     }
     else
     {
         strError = lpBuffer;
         LocalFree(lpBuffer);
     }
     return strError;
 }
 
 CCallScript::CCallScript()
 {
     m_bDocumentSet = FALSE;
 
 }
 
 CCallScript::~CCallScript()
 {
 
 }
 
 BOOL CCallScript::SetDocument(IDispatch* pDisp)
 {
     CHECK_POINTER(pDisp);
 
     m_spDoc = NULL;
 
     CComPtr<IDispatch> spDisp = pDisp;
 
     HRESULT hr = spDisp->QueryInterface(IID_IHTMLDocument2,(void**)&m_spDoc);
     if(FAILED(hr))
     {
         ShowError("Failed to get HTML document COM object");
         return FALSE;
     }
     m_bDocumentSet = TRUE;
     return TRUE;
 }
 
 BOOL CCallScript::GetScript(CComPtr<IDispatch>& spDisp)
 {
     CHECK_POINTER(m_spDoc);
     HRESULT hr = m_spDoc->get_Script(&spDisp);
     ATLASSERT(SUCCEEDED(hr));
     return SUCCEEDED(hr);
 }
 
 BOOL CCallScript::GetScripts(CComPtr<IHTMLElementCollection>& spColl)
 {
     CHECK_POINTER(m_spDoc);
     HRESULT hr = m_spDoc->get_scripts(&spColl);
     ATLASSERT(SUCCEEDED(hr));
     return SUCCEEDED(hr);
 }
 
 BOOL CCallScript::Run(const CComBSTR strFunc,CComVariant* pVarResult)
 {
     CSimpleArray<CComBSTR>  paramArray;
     return Run(strFunc,paramArray,pVarResult);
 }
 
 BOOL CCallScript::Run(const CComBSTR strFunc,const CComBSTR strArg1,CComVariant* pVarResult)
 {
     CSimpleArray<CComBSTR>  paramArray;
     paramArray.Add((CComBSTR &)strArg1);
     return Run(strFunc,paramArray,pVarResult);
 }
 
 BOOL CCallScript::Run(const CComBSTR strFunc,const CComBSTR strArg1,const CComBSTR strArg2,CComVariant* pVarResult)
 {
     CSimpleArray<CComBSTR>  paramArray;
     paramArray.Add((CComBSTR &)strArg1);
     paramArray.Add((CComBSTR &)strArg2);
     return Run(strFunc,paramArray,pVarResult);
 }
 
 BOOL CCallScript::Run(const CComBSTR strFunc,const CComBSTR strArg1,const CComBSTR strArg2,const CComBSTR strArg3,CComVariant* pVarResult)
 {
     CSimpleArray<CComBSTR>  paramArray;
     paramArray.Add((CComBSTR &)strArg1);
     paramArray.Add((CComBSTR &)strArg2);
     paramArray.Add((CComBSTR &)strArg3);
     return Run(strFunc,paramArray,pVarResult);
 }
 
 BOOL CCallScript::Run(const CComBSTR strFunc, const CSimpleArray<CComBSTR>& paramArray,CComVariant* pVarResult)
 {
     CComPtr<IDispatch> spScript;
     if(!GetScript(spScript))
     {
         ShowError("Cannot GetScript");
         return FALSE;
     }
     CComBSTR bstrMember(strFunc);
     DISPID dispid = NULL;
     HRESULT hr = spScript->GetIDsOfNames(IID_NULL,&bstrMember,1,
                                             LOCALE_SYSTEM_DEFAULT,&dispid);
     if(FAILED(hr))
     {
         ShowError(GetSystemErrorMessage(hr));
         return FALSE;
     }
 
     //const int arraySize = paramArray.GetCount();
     const int arraySize = paramArray.GetSize();
 
     DISPPARAMS dispparams;
     memset(&dispparams, 0, sizeof dispparams);
     dispparams.cArgs = arraySize;
     dispparams.rgvarg = new VARIANT[dispparams.cArgs];
     //__asm {int 3}
     CComBSTR bstr;
     for( int i = 0; i < arraySize; i++)
     {
         bstr.Empty();
         //CComBSTR bstr = paramArray.GetAt(arraySize - 1 - i); // back reading
         bstr = paramArray[arraySize - 1 - i]; // back reading
         //bstr.CopyTo(&dispparams.rgvarg[i].bstrVal); //memory leak
         dispparams.rgvarg[i].bstrVal = bstr.m_str; //also cause problem when paras are more than 1
         dispparams.rgvarg[i].vt = VT_BSTR;
     }
     dispparams.cNamedArgs = 0;
 
     EXCEPINFO excepInfo;
     memset(&excepInfo, 0, sizeof excepInfo);
        CComVariant vaResult;
     UINT nArgErr = (UINT)-1;  // initialize to invalid arg
 
     hr = spScript->Invoke(dispid,IID_NULL,0,
                             DISPATCH_METHOD,&dispparams,&vaResult,&excepInfo,&nArgErr);
 
 /////////////// bug fix memory leak code start ///////////////
 //    for( int j = 0; j < arraySize; j++)
 //        ::SysFreeString(dispparams.rgvarg[j].bstrVal);
 /////////////// bug fix memory leak code end ///////////////
 
     delete [] dispparams.rgvarg;
     if(FAILED(hr))
     {
         ShowError(GetSystemErrorMessage(hr));
         return FALSE;
     }
 
     if(pVarResult)
     {
         *pVarResult = vaResult;
     }
     return FALSE;
 }
 
这两个文件的使用的方法:
     // Get the browser control.
     CAxWindow wnd = GetDlgItem(IDC_EXPLORER); // WebBrowser
     wnd.QueryControl( &m_spBrowser );
     CComPtr<IAxWinAmbientDispatch> spAmbient;
     HRESULT hr = wnd.QueryHost(&spAmbient);
 
     // diable the context menu
     // disable the scrollbar
     if( SUCCEEDED(hr) )
     {
         spAmbient->put_AllowContextMenu(VARIANT_TRUE);
         spAmbient->put_DocHostFlags(docHostUIFlagFLAT_SCROLLBAR);
     }
 
     // navigate to the base html
     VARIANT flag = {0};
     VARIANT name = {0};
     VARIANT post = {0};
     VARIANT head = {0};
     // 
 //    m_spBrowser->Navigate(_bstr_t(GetFullName("WhizConsoleSlave.html")), &flag, &name, &post, &head);
     TCHAR szFileName[MAX_PATH];
     ::GetModuleFileName(_Module.GetModuleInstance(), szFileName, MAX_PATH);
     TCHAR szRes[MAX_PATH+10];
     ::wsprintf(szRes, _T("res://%s/%0d"), szFileName, IDR_HTML);
     CComVariant vURL(szRes);
     m_spBrowser->Navigate2(&vURL, &flag, &name, &post, &head); // 显示指定的页面
 
     // Create a wrapper about the external dispatch interface
     CComObject<CWrapperDispatch>*   spdispWrapper = 0;
     hr = CComObject<CWrapperDispatch>::CreateInstance(&spdispWrapper);
     if( FAILED(hr) ) return 0;
 
     // Dummy for refcount management
     CComPtr<IUnknown> spUnk = spdispWrapper;
 
     // Create the object that will handle the external interface for the
     // html file.
     pExternal = new CExternal(m_hWnd);
     m_oExternal = static_cast<IDispatch *>(pExternal);
 
     // Set the external dispatch interface
     spdispWrapper->SetDispatch(m_oExternal); // 对脚本抛送接口
     hr = wnd.SetExternalDispatch(spdispWrapper);
 //    wnd.SetFocus();
 
 
 // ******************************************************************
 // 调用脚本比较容易
     if ( m_CallScript.DocumentSet() == FALSE)
     {
         IDispatch* d = NULL;
         m_spBrowser->get_Document(&d);
         m_CallScript.SetDocument(d);
         d->Release();
     }
     m_CallScript.Run(L"DisplayStr"); // DisplayStr就是脚本的函数
     m_CallScript.Run(L"AddDir","a","b"); // AddDir也是脚本的函数,a和b是AddDir的参数.
 
 
原文地址:https://www.cnblogs.com/blogpro/p/11426776.html