我在我的c ++应用程序中embedded了一个Web浏览器控件。 我想要在Web浏览器控件中运行的JavaScript能够调用一个C ++函数/方法。
我发现提到三种方法来做到这一点:
我想用第三种方法,但是我还没有find有关如何做的实例。 有人可以告诉我怎么做,或者链接到网上的一个工作的例子。
最接近我find的例子是Igor Tandetnik在webbrowser_ctl新闻组的一个主题中的第一个回复。 但是恐怕我需要更多的帮助。
我正在embedded一个IWebBrowser2控件,我不使用MFC,ATL或WTL。
编辑:
在我之前链接的线程中,以及在codeproject文章“ 从C ++创buildJavaScript数组和其他对象 ”中find的Igor提供的伪代码,我已经生成了一些代码。
void WebForm::AddCustomObject(IDispatch *custObj, std::string name) { IHTMLDocument2 *doc = GetDoc(); IHTMLWindow2 *win = NULL; doc->get_parentWindow(&win); if (win == NULL) { return; } IDispatchEx *winEx; win->QueryInterface(&winEx); if (winEx == NULL) { return; } int lenW = MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, name.c_str(), -1, NULL, 0); BSTR objName = SysAllocStringLen(0, lenW); MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, name.c_str(), -1, objName, lenW); DISPID dispid; HRESULT hr = winEx->GetDispID(objName, fdexNameEnsure, &dispid); SysFreeString(objName); if (FAILED(hr)) { return; } DISPID namedArgs[] = {DISPID_PROPERTYPUT}; DISPPARAMS params; params.rgvarg = new VARIANT[1]; params.rgvarg[0].pdispVal = custObj; params.rgvarg[0].vt = VT_DISPATCH; params.rgdispidNamedArgs = namedArgs; params.cArgs = 1; params.cNamedArgs = 1; hr = winEx->InvokeEx(dispid, LOCALE_USER_DEFAULT, DISPATCH_PROPERTYPUT, ¶ms, NULL, NULL, NULL); if (FAILED(hr)) { return; } }
上面的代码一直运行,所以一切看起来都很好。
我在收到DISPID_NAVIGATECOMPLETE2 DWebBrowserEvents2事件时调用AddCustomObject,并将其传递为*custObj
:
class JSObject : public IDispatch { private: long ref; public: // IUnknown virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppv); virtual ULONG STDMETHODCALLTYPE AddRef(); virtual ULONG STDMETHODCALLTYPE Release(); // IDispatch virtual HRESULT STDMETHODCALLTYPE GetTypeInfoCount(UINT *pctinfo); virtual HRESULT STDMETHODCALLTYPE GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo); virtual HRESULT STDMETHODCALLTYPE GetIDsOfNames(REFIID riid, LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId); virtual HRESULT STDMETHODCALLTYPE Invoke(DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *Params, VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr); };
值得注意的实现可能是
HRESULT STDMETHODCALLTYPE JSObject::QueryInterface(REFIID riid, void **ppv) { *ppv = NULL; if (riid == IID_IUnknown || riid == IID_IDispatch) { *ppv = static_cast<IDispatch*>(this); } if (*ppv != NULL) { AddRef(); return S_OK; } return E_NOINTERFACE; }
和
HRESULT STDMETHODCALLTYPE JSObject::Invoke(DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *Params, VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr) { MessageBox(NULL, "Invoke", "JSObject", MB_OK); return DISP_E_MEMBERNOTFOUND; }
不幸的是,当我尝试使用javascript代码中的“JSObject”对象时,我从来没有得到“Invoke”消息框。
JSObject.randomFunctionName(); // This should give me the c++ "Invoke" message // box, but it doesn't
编辑2:
我这样实现了GetIDsOfNames
:
HRESULT STDMETHODCALLTYPE JSObject::GetIDsOfNames(REFIID riid, LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId) { HRESULT hr = S_OK; for (UINT i = 0; i < cNames; i++) { std::map<std::wstring, DISPID>::iterator iter = idMap.find(rgszNames[i]); if (iter != idMap.end()) { rgDispId[i] = iter->second; } else { rgDispId[i] = DISPID_UNKNOWN; hr = DISP_E_UNKNOWNNAME; } } return hr; }
这是我的构造函数
JSObject::JSObject() : ref(0) { idMap.insert(std::make_pair(L"execute", DISPID_USER_EXECUTE)); idMap.insert(std::make_pair(L"writefile", DISPID_USER_WRITEFILE)); idMap.insert(std::make_pair(L"readfile", DISPID_USER_READFILE)); }
将DISPID_USER_ *常量定义为私有类成员
class JSObject : public IDispatch { private: static const DISPID DISPID_USER_EXECUTE = DISPID_VALUE + 1; static const DISPID DISPID_USER_WRITEFILE = DISPID_VALUE + 2; static const DISPID DISPID_USER_READFILE = DISPID_VALUE + 3; // ... };
编辑3,4和5:
搬到一个单独的问题
编辑6:
从“返回string”编辑中单独提出问题 。 这样我可以接受格奥尔格的答复,因为这回答了原来的问题。
编辑7:
我已经收到了一些完整的,自成体系的示例实现的请求。 这里是: https : //github.com/Tobbe/CppIEEmbed 。 如果可以的话,请叉和改善:)
你需要实现GetIDsOfNames()
来做一些明智的事情,因为这个函数将在Invoke()
之前被客户端代码Invoke()
。
如果你有一个类型库中的接口,请参阅这里的例子。 如果要使用后期绑定,则可以使用DISPID
更大的DISPID_VALUE
且小于0x80010000
(所有值<= 0
并且保留范围为0x80010000
到0x8001FFFF
):
HRESULT GetIDsOfNames(REFIID riid, LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId) { HR hr = S_OK; for (UINT i=0; i<cNames; ++i) { if (validName(rgszNames)) { rgDispId[i] = dispIdForName(rgszNames); } else { rgDispId[i] = DISPID_UNKNOWN; hr = DISP_E_UNKNOWNNAME; } } return hr; } HRESULT Invoke(DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *Params, VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr) { if (wFlags & DISPATCH_METHOD) { // handle according to DISPID ... } // ...
请注意, DISPID
不应该突然改变,所以应该使用静态map
或常量值。