从Web浏览器控件中运行的JavaScript脚本调用C ++函数

时间:2010-09-19 20:47:25

标签: javascript c++ windows com iwebbrowser2

我在我的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. 将自定义对象添加到窗口对象
  4. 我想使用第三个选项,但我没有找到任何有关如何做到这一点的工作示例。有人可以告诉我该怎么做,或链接到某个网上的工作示例。

    最接近我发现的一个例子是Igor Tandetnik在a thread in the webbrowser_ctl news group中的第一个回复。但我担心我需要更多的帮助。

    我正在嵌入一个IWebBrowser2控件而我没有使用MFC,ATL或WTL。

    修改

    使用Igor在我之前链接的线程中提供的伪代码,以及代码项目文章“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, &params, NULL, NULL, NULL); 
    
        if (FAILED(hr)) {
            return;
        }
    }
    

    上面的代码一直在运行,所以一切看起来都不错。

    当我收到DISPID_NAVIGATECOMPLETE2 DWebBrowserEvents2事件并将其传递为*custObj时,我调用AddCustomObject:

    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”对象时,我从未得到“调用”消息框。

    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:

    已移至a separate question

    编辑6:

    从“返回字符串”编辑中取出a separate question。这样我可以接受Georg's回复,因为它回答了原始问题。

    编辑7:

    我已经收到了一些完全工作,自包含的示例实现的请求。这是:https://github.com/Tobbe/CppIEEmbed。如果可以,请分叉并改进:)

1 个答案:

答案 0 :(得分:5)

您需要实施GetIDsOfNames()来做一些合理的事情,因为Invoke()之前客户端代码会调用该函数。
如果您在类型库中有接口,请参阅here以获取示例。如果您想使用后期绑定,则可以使用DISPIDs更大DISPID_VALUE且小于0x80010000(所有值<= 00x80010000范围内的{{3}}保留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或常量值。

相关问题