Вызов функции C ++ из сценария JavaScript, запущенного в элементе управления веб-браузером

Я встроил элемент управления веб-браузера в свое приложение на C ++. Я хочу, чтобы 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. Добавить настраиваемый объект к объекту окна

Я хочу использовать третий вариант, но я не нашел никаких рабочих примеров того, как это сделать . Может кто-нибудь, пожалуйста, покажите мне, как это сделать, или дайте ссылку на рабочий пример где-нибудь в сети.

Самый близкий к примеру, который я нашел, - это первый ответ Игоря Тандетника в ветке новостей webbrowser_ctl группа . Но боюсь, мне нужна дополнительная помощь.

Я встраиваю элемент управления IWebBrowser2 и не использую MFC, ATL или WTL.

РЕДАКТИРОВАТЬ:

Исходя из псевдокода, данного Игорем в потоке, который я связал ранее, и коде, найденном в статье 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);
};

Примечательные реализации могут be

HRESULT STDMETHODCALLTYPE JSObject::QueryInterface(REFIID riid, void **ppv)
{
    *ppv = NULL;

    if (riid == IID_IUnknown || riid == IID_IDispatch) {
        *ppv = static_cast(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::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;

    // ...
};

EDIT 3, 4 и 5:

Перемещено в отдельный вопрос

EDIT 6:

Сделал отдельным вопросом из правок "возврат строки". Так я могу принять Георга ' s ответит, что это ответ на исходный вопрос.

РЕДАКТИРОВАТЬ 7:

Я получил несколько запросов на полностью рабочий, автономный пример реализации. Вот он: https://github.com/Tobbe/CppIEEmbed . Пожалуйста, разветвите и улучшите, если сможете:)

12
задан Community 23 May 2017 в 12:13
поделиться