上篇介绍了用C++调用JavaScript,这篇反过来说说JS调C++,这样JS和C++沟通的途径就完整了。
首先,实现一个IDispatch接口,当JS调用C++函数时,系统会调用GetIDsOfNames来用函数名取得函数的DISPID,然后调用Invoke完成真正的调用,需要注意的是Invoke的pDispParams中的参数是倒序的。代码如下:
class CExtenalDispatch : public IDispatch { public: CExtenalDispatch() { m_dwRef = 0; } ~CExtenalDispatch() { } STDMETHOD_(ULONG, AddRef)() { return InterlockedIncrement(&m_dwRef); } STDMETHOD_(ULONG, Release)() { unsigned long l = InterlockedDecrement(&m_dwRef); if(l == 0) delete this; return l; } STDMETHOD(QueryInterface)(REFIID iid, LPVOID far* ppvObject) { HRESULT hrRet = S_OK; *ppvObject = NULL; if(IsEqualIID(iid, IID_IDispatch)) *ppvObject = (IDispatch*)this; else hrRet = E_NOINTERFACE; if (S_OK == hrRet) ((IUnknown*)*ppvObject)->AddRef(); return hrRet; } STDMETHOD(GetTypeInfoCount)(UINT* pctinfo) { return E_NOTIMPL; } STDMETHOD(GetTypeInfo)(/* [in] */ UINT iTInfo, /* [in] */ LCID lcid, /* [out] */ ITypeInfo** ppTInfo) { return E_NOTIMPL; } STDMETHOD(GetIDsOfNames)( /* [in] */ REFIID riid, /* [size_is][in] */ LPOLESTR *rgszNames, /* [in] */ UINT cNames, /* [in] */ LCID lcid, /* [size_is][out] */ DISPID *rgDispId) { HRESULT hr = S_OK; for (int i = 0; i < (int)cNames; i++) { CString cszName = rgszNames[i]; if(cszName == L"myfunction") { bFind = TRUE; rgDispId[i] = 1; break; } else { // One or more are unknown so set the return code accordingly hr = ResultFromScode(DISP_E_UNKNOWNNAME); rgDispId[i] = DISPID_UNKNOWN; } } return hr; } STDMETHOD(Invoke)( /* [in] */ DISPID dispIdMember, /* [in] */ REFIID riid, /* [in] */ LCID lcid, /* [in] */ WORD wFlags, /* [out][in] */ DISPPARAMS *pDispParams, /* [out] */ VARIANT *pVarResult, /* [out] */ EXCEPINFO *pExcepInfo, /* [out] */ UINT *puArgErr) { if (!pDispParams) return E_INVALIDARG; if(wFlags & DISPATCH_METHOD) { int memberCount = sizeof(mapMember) / sizeof(MemberInfo); switch(dispIdMember) { case 1: { MessageBox(L"Hello JS!", L""); return S_OK; } break; default: return DISP_E_MEMBERNOTFOUND; break; } } return DISP_E_MEMBERNOTFOUND; } private: long m_dwRef; };
其次,实现一个IDocHostUIHandlerDispatch接口。其中最重要的是GetExternal,它返回一个CExtenalDispatch指针。
class CBrowserHostUIHandlerImpl : public IDispatchImpl { public: CBrowserHostUIHandlerImpl() { m_dwRef = 0; m_pDispatch = NULL; } ~CBrowserHostUIHandlerImpl() { if(m_pDispatch) { m_pDispatch->Release(); m_pDispatch = NULL; } } STDMETHOD_(ULONG, AddRef)(){ return InterlockedIncrement(&m_dwRef); } STDMETHOD_(ULONG, Release)() { unsigned long l = InterlockedDecrement(&m_dwRef); if(l == 0) delete this; return l; } STDMETHOD(QueryInterface)(REFIID iid, LPVOID far* ppvObject) { HRESULT hrRet = S_OK; *ppvObject = NULL; if (IsEqualIID(iid, IID_IUnknown)) *ppvObject = (IUnknown*)this; else if(IsEqualIID(iid, IID_IDispatch)) *ppvObject = (IDispatch*)this; else if (IsEqualIID(iid, __uuidof(IDocHostUIHandler))) *ppvObject = (IDocHostUIHandler*)this; else hrRet = E_NOINTERFACE; if (S_OK == hrRet) ((IUnknown*)*ppvObject)->AddRef(); return hrRet; } STDMETHOD(ShowContextMenu)(/* [in] */ DWORD dwID, /* [in] */ DWORD x, /* [in] */ DWORD y, /* [in] */ IUnknown *pcmdtReserved, /* [in] */ IDispatch *pdispReserved, /* [retval][out] */ HRESULT *dwRetVal) { *dwRetVal = S_OK; return S_OK; } STDMETHOD(GetHostInfo)(/* [out][in] */ DWORD *pdwFlags, /* [out][in] */ DWORD *pdwDoubleClick) { *pdwFlags = DOCHOSTUIFLAG_DIV_BLOCKDEFAULT | DOCHOSTUIFLAG_CODEPAGELINKEDFONTS | DOCHOSTUIFLAG_THEME | DOCHOSTUIFLAG_SCROLL_NO | DOCHOSTUIFLAG_NO3DBORDER; *pdwDoubleClick = DOCHOSTUIDBLCLK_DEFAULT; return S_OK; } STDMETHOD(ShowUI)(/* [in] */ DWORD dwID, /* [in] */ IUnknown *pActiveObject, /* [in] */ IUnknown *pCommandTarget, /* [in] */ IUnknown *pFrame, /* [in] */ IUnknown *pDoc, /* [retval][out] */ HRESULT *dwRetVal) { return S_OK; } STDMETHOD(HideUI)(){ return S_OK; } STDMETHOD(UpdateUI)(){ return S_OK; } STDMETHOD(EnableModeless)(/* [in] */ VARIANT_BOOL fEnable){ return S_OK; } STDMETHOD(OnDocWindowActivate)(/* [in] */ VARIANT_BOOL fActivate){ return S_OK; } STDMETHOD(OnFrameWindowActivate)(/* [in] */ VARIANT_BOOL fActivate){ return S_OK; } STDMETHOD(ResizeBorder)(/* [in] */ long left, /* [in] */ long top, /* [in] */ long right, /* [in] */ long bottom, /* [in] */ IUnknown *pUIWindow, /* [in] */ VARIANT_BOOL fFrameWindow) { return S_OK; } STDMETHOD(TranslateAccelerator)(/* [in] */ DWORD_PTR hWnd, /* [in] */ DWORD nMessage, /* [in] */ DWORD_PTR wParam, /* [in] */ DWORD_PTR lParam, /* [in] */ BSTR bstrGuidCmdGroup, /* [in] */ DWORD nCmdID, /* [retval][out] */ HRESULT *dwRetVal) { return S_FALSE; } STDMETHOD(GetOptionKeyPath)(/* [out] */ BSTR *pbstrKey, /* [in] */ DWORD dw){ return S_FALSE;} STDMETHOD(GetDropTarget)(/* [in] */ IUnknown *pDropTarget,/* [out] */ IUnknown **ppDropTarget){ return S_FALSE; } STDMETHOD(GetExternal)(/* [out] */ IDispatch **ppDispatch) { if(!m_pDispatch) { m_pDispatch = new CExtenalDispatch; } m_pDispatch->AddRef(); *ppDispatch = m_pDispatch; return S_OK; } STDMETHOD(TranslateUrl)(/* [in] */ DWORD dwTranslate, /* [in] */ BSTR bstrURLIn, /* [out] */ BSTR *pbstrURLOut){ return S_FALSE; } STDMETHOD(FilterDataObject)(/* [in] */ IUnknown *pDO, /* [out] */ IUnknown **ppDORet){ return S_FALSE; } private: long m_dwRef; CExtenalDispatch * m_pDispatch; };
最后,在WebBrowser控制创建好后,调用SetExternalUIHandler。如下:
CBrowserHostUIHandlerImpl * pHost = new CBrowserHostUIHandlerImpl; pHost->AddRef(); SetExternalUIHandler(pHost);