Вызов функции C++ из сценария JavaScript, запущенного в элементе управления веб-браузера
я встроил элемент управления веб-браузером в свое приложение c++. Я хочу, чтобы javascript, запущенный в веб-браузере, мог вызывать функцию/метод c++.
я нашел упоминания о трех способах сделать это:
- реализовать компонент ActiveX, который действует как посредник. (Подробности реализации здесь: http://blogs.msdn.com/b/nicd/archive/2007/04/18/calling-into-your-bho-from-a-client-script.aspx)
- использовать окно.внешний. (Также обсуждается в ссылке выше, но реализация не предусмотрена)
- добавить пользовательский объект в объект окна
Я хочу пойти с третьим вариантом, но я не нашел каких-либо примеры, как это сделать. Может кто-то пожалуйста, покажите мне, как это сделать, или ссылку на рабочий пример в сети где-то.
ближайший пример, который я нашел-это первый ответ Игорь Tandetnik в поток в webbrowser_ctl news group. Но, боюсь,мне нужна помощь.
я встраиваю элемент управления IWebBrowser2, и я не использую MFC, ATL или WTL.
EDIT:
идет псевдо-код, данный Игорем в потоке, который я связал ранее, и код, найденный в статье codeproject"создание массивов JavaScript и других объектов из 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;
}
}
код выше запускает все проход, так что пока все выглядит нормально.
Я вызываю AddCustomObject при получении события DISPID_NAVIGATECOMPLETE2 DWebBrowserEvents2, передающего это как *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;
}
к сожалению, я никогда не получаю окно сообщения "Invoke" при попытке использовать объект "JSObject" из кода javascript.
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:
переехал в отдельный вопрос
EDIT 6:
составила отдельный вопрос из редактирования" возврат строки". Такой образ Я могу принять Георга ответить, как это отвечает на первоначальный вопрос.
EDIT 7:
я получил несколько запросов на полностью рабочую, автономную реализацию примера. Вот оно:https://github.com/Tobbe/CppIEEmbed. Пожалуйста, вилка и улучшить, если вы можете:)
1 ответов
вам нужно реализовать GetIDsOfNames()
сделать что-то разумное, так как эта функция будет вызываться клиентским кодом до Invoke()
.
Если у вас есть интерфейсы в библиотеке типов, см. здесь для примера. Если вы хотите использовать late-binding вместо этого, вы можете использовать DISPID
s больше 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
s не допускаются чтобы изменить внезапно, так, например, Статический map
или должны использоваться постоянные значения.