我在我的C++应用程序中嵌入了一个Web浏览器控件。我希望在Web浏览器控件中运行的JavaScript能够调用C++函数/方法。
我发现有三种方法可以实现这一点:
1. 实现一个作为中间人的ActiveX组件。(实现细节在此处:http://blogs.msdn.com/b/nicd/archive/2007/04/18/calling-into-your-bho-from-a-client-script.aspx)
2. 使用window.external。(也在上面的链接中讨论,但未提供实现)
3. 向window对象添加自定义对象。
我想选择第三个选项,但我没有找到任何可行的示例。请问是否有人可以向我展示如何做,或者在网络上链接到一个可行的示例。
我找到的最接近示例是Igor Tandetnik在a thread in the webbrowser_ctl news group中的第一个回复。但我需要更多的帮助。
我正在嵌入一个IWebBrowser2控件,并且没有使用MFC、ATL或WTL。
编辑:
上面的代码一直运行,所以到目前为止一切看起来都很好。
当我接收到DISPID_NAVIGATECOMPLETE2 DWebBrowserEvents2事件时,我调用AddCustomObject,并将*this作为* custObj参数传递:
值得注意的实现可能包括:
我发现有三种方法可以实现这一点:
1. 实现一个作为中间人的ActiveX组件。(实现细节在此处:http://blogs.msdn.com/b/nicd/archive/2007/04/18/calling-into-your-bho-from-a-client-script.aspx)
2. 使用window.external。(也在上面的链接中讨论,但未提供实现)
3. 向window对象添加自定义对象。
我想选择第三个选项,但我没有找到任何可行的示例。请问是否有人可以向我展示如何做,或者在网络上链接到一个可行的示例。
我找到的最接近示例是Igor Tandetnik在a thread in the webbrowser_ctl news group中的第一个回复。但我需要更多的帮助。
我正在嵌入一个IWebBrowser2控件,并且没有使用MFC、ATL或WTL。
编辑:
根据我之前链接的帖子中Igor提供的伪代码和codeproject文章"Creating JavaScript arrays and other objects from C ++"中找到的代码,我编写了一些代码。
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,并将*this作为* 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:
将“返回字符串”编辑制作成一个单独的问题。这样我就可以接受Georg的回答,因为它回答了原始问题。
编辑7:
我收到了一些关于完全可行的自包含示例实现的请求。在这里:https://github.com/Tobbe/CppIEEmbed。如果您能,请分叉并改进 :)