从Javascript调用BHO方法?

Fav*_*ius 10 internet-explorer activex bho atl ieaddon

我试图从javascript调用我的BHO方法.问题与以下帖子中说明的相同:

  1. 从Javascript函数调用BHO
  2. http://social.msdn.microsoft.com/Forums/en-US/ieextensiondevelopment/thread/91d4076e-4795-4d9e-9b07-5b9c9eca62fb/
  3. 从Web浏览器控件中运行的JavaScript脚本调用C++函数

第三个链接是另一个SO帖子谈论它,但我不理解需求和代码.此外,共享工作样本使用ie 8和windows vista(即7)保持崩溃.

如果它有助于我的BHO使用ATL用C++编写.

我尝试过的:

我写了一个非常基本的BHO,并试图为所提到的方法在这里伊戈尔Tandetnik.没有生成异常但是当我在IE中打开以下html文件时它会说对象未定义.

<html>
    <head>
        <script language='javascript'>
            function call_external(){
                try{
                alert(window.external.TestScript);
                //JQueryTest.HelloJquery('a');
                }catch(err){
                    alert(err.description );
                }
            }
        </script>
    </head>
    <body id='bodyid' onload="call_external();">
        <center><div><span>Hello jQuery!!</span></div></center>
    </boay>
</html>
Run Code Online (Sandbox Code Playgroud)

题:

  1. 请澄清是否有可能暴露和从JavaScript调用BHO方法还是必须使用它的ActiveX以暴露(通过如回答jeffdav[2] )?如果是,那么该怎么做.
  2. 基本上我想扩展,window.external但上面链接[2]使用的方式var x = new ActiveXObject("MySampleATL.MyClass");; 调用约定是相同还是不同?

注意:

  1. 在SO上有一个相关的帖子,它提示可以通过[id(1), helpstring("method DoSomething")] HRESULT DoSomething();在BHO IDL文件中插入它来实现.我不确定它是如何完成的,并且无法通过谷歌找到任何支持资源.
  2. 我知道这个帖子调用你的bho-from-a-client-script,但没有尝试过,因为它正在解决使用ActiveX的问题.
  3. 我避免使用ActiveX的原因主要是由于安全限制.

编辑1


似乎有一种方法可以扩展window.external.检查一下.特别是标题为IDocHostUIHandler::GetExternal: Extending the DOM.Now的假设我们的IDispatch接口位于实现IDocHostUIHandler的同一对象上.然后我们可以这样做:

HRESULT CBrowserHost::GetExternal(IDispatch **ppDispatch) 
{
    *ppDispatch = this;
    return S_OK;
}
Run Code Online (Sandbox Code Playgroud)

这种方法的问题在于它不会附加到现有的Windows方法,而是替换它们.请告诉我是不是错了.

编辑2


The BHO Class:

class ATL_NO_VTABLE CTestScript :
    public CComObjectRootEx<CComSingleThreadModel>,
    public CComCoClass<CTestScript, &CLSID_TestScript>,
    public IObjectWithSiteImpl<CTestScript>,
    public IDispatchImpl<ITestScript, &IID_ITestScript, &LIBID_TestBHOLib, /*wMajor =*/ 1, /*wMinor =*/ 0>,
    public IDispEventImpl<1, CTestScript, &DIID_DWebBrowserEvents2, &LIBID_SHDocVw, 1, 1>
{
public:
    CTestScript()
    {
    }

DECLARE_REGISTRY_RESOURCEID(IDR_TESTSCRIPT)

DECLARE_NOT_AGGREGATABLE(CTestScript)

BEGIN_COM_MAP(CTestScript)
    COM_INTERFACE_ENTRY(ITestScript)
    COM_INTERFACE_ENTRY(IDispatch)
    COM_INTERFACE_ENTRY(IObjectWithSite)
END_COM_MAP()



    DECLARE_PROTECT_FINAL_CONSTRUCT()

    HRESULT FinalConstruct()
    {
        return S_OK;
    }

    void FinalRelease()
    {
    }

public:
    BEGIN_SINK_MAP(CTestScript)
        SINK_ENTRY_EX(1, DIID_DWebBrowserEvents2, DISPID_DOCUMENTCOMPLETE, OnDocumentComplete)
        //SINK_ENTRY_EX(1, DIID_DWebBrowserEvents2, DISPID_NAVIGATECOMPLETE2, OnNavigationComplete)
    END_SINK_MAP()

    void STDMETHODCALLTYPE OnDocumentComplete(IDispatch *pDisp, VARIANT *pvarURL);
    //void STDMETHODCALLTYPE OnNavigationComplete(IDispatch *pDisp, VARIANT *pvarURL);

    STDMETHOD(SetSite)(IUnknown *pUnkSite);

    HRESULT STDMETHODCALLTYPE DoSomething(){
        ::MessageBox(NULL, L"Hello", L"World", MB_OK);
        return S_OK;
    }
public:

//private:
    // InstallBHOMethod();

private:
    CComPtr<IWebBrowser2>  m_spWebBrowser;
    BOOL m_fAdvised;
};
Run Code Online (Sandbox Code Playgroud)

// TestScript.cpp : Implementation of CTestScript

#include "stdafx.h"
#include "TestScript.h"


// CTestScript

STDMETHODIMP CTestScript::SetSite(IUnknown* pUnkSite)
{
    if (pUnkSite != NULL)
    {
        HRESULT hr = pUnkSite->QueryInterface(IID_IWebBrowser2, (void **)&m_spWebBrowser);
        if (SUCCEEDED(hr))
        {
            hr = DispEventAdvise(m_spWebBrowser);
            if (SUCCEEDED(hr))
            {
                m_fAdvised = TRUE;              
            }
        }
    }else
    {
        if (m_fAdvised)
        {
            DispEventUnadvise(m_spWebBrowser);
            m_fAdvised = FALSE;
        }
        m_spWebBrowser.Release();
    }
    return IObjectWithSiteImpl<CTestScript>::SetSite(pUnkSite);
}

void STDMETHODCALLTYPE CTestScript::OnDocumentComplete(IDispatch *pDisp, VARIANT *pvarURL)
{
        CComPtr<IDispatch> dispDoc;
        CComPtr<IHTMLDocument2> ifDoc;
        CComPtr<IHTMLWindow2> ifWnd;
        CComPtr<IDispatchEx> dispxWnd;

        HRESULT hr = m_spWebBrowser->get_Document( &dispDoc );
        hr = dispDoc.QueryInterface( &ifDoc );      
        hr = ifDoc->get_parentWindow( &ifWnd );
        hr = ifWnd.QueryInterface( &dispxWnd );

        // now ... be careful. Do exactly as described here. Very easy to make mistakes
        CComBSTR propName( L"myBho" );
        DISPID dispid;
        hr = dispxWnd->GetDispID( propName, fdexNameEnsure, &dispid );

        CComVariant varMyBho( (IDispatch*)this );
        DISPPARAMS params;
        params.cArgs = 1;
        params.cNamedArgs = 0;
        params.rgvarg = &varMyBho;            
        params.rgdispidNamedArgs = NULL;
        hr = dispxWnd->Invoke( dispid, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_PROPERTYPUT,
            &params, NULL, NULL, NULL );

}
Run Code Online (Sandbox Code Playgroud)

The Javascript:

<script language='javascript'>
            function call_external(){
                try{
                alert(window.ITestScript);
                }catch(err){
                    alert(err.description );
                }
            }
        </script>
Run Code Online (Sandbox Code Playgroud)

编辑3


花了三天后,我想我应该采取ActiveX路径.编写一个基本的activex只是简单易用的方法,并且在所有主要的IE版本上进行了测试.我将这个问题保持开放,请参阅Uri的回答中的评论(非常感谢他).我已经尝试了他的大部分建议(除了4和5).I will also suggest you to see the MSDN IDispatcEx sample.如果您找到解决方案,请发帖,如果我找到解决方案,那么我肯定会在这里更新.

编辑4


See my last comment in URI's post. Issue Resolved.

Uri*_*Uri 7

Igor Tandetnik的方法是正确的方法.该帖子的问题是示例代码(至少在我发现它的几页上)是不完整的.我有很多试验和错误,直到我开始工作.这是我的代码的很大一部分,可以解决这个问题:

假设您有一个类CMyBho,并且您希望为Java脚本公开IMyBho自动化对象

类定义:
您从标准CComObjectRootEx和CComCoClass派生,使其"共同创建".您有IObjectWithSiteImpl(重用此基类实现的m_spUnkSite).IDispatchImpl实现您的自动化对象,IDispatchEventImpl是从浏览器获取通知的接收器:

class ATL_NO_VTABLE CMyBho
    : public CComObjectRootEx<CComSingleThreadModel>
    , public CComCoClass<CMyBho, &CLSID_MyBho>
    , public IObjectWithSiteImpl<CMyBho>
    , public IDispatchImpl<IMyBho, &IID_IMyBho, &LIBID_MyBhoLib, 1, 0>
    , IDispatchEventImpl<1, CMyBho, &DIID_DWebBrowserEvents2, &LIBID_SHDocVw, 1, 1>
{
    ...

public:
    BEGIN_COM_MAP(CMyBho)
        COM_INTERFACE_ENTRY(IMyBho)
        COM_INTERFACE_ENTRY(IDispatch)
        COM_INTERFACE_ENTRY(IObjectWithSite)
    END_COM_MAP()

    ...

    BEGIN_SINK_MAP(CMyBho)
        SINK_ENTRY_EX( 1, DIID_DWebBrowserEvents2, DISPID_DOCUMENTCOMPLETE, OnDocComplete )
    END_SINK_MAP()

    ...

private:
    CComPtr<IWebBrowser2> m_ifbrz;          // pointer to the hosting browser

}
Run Code Online (Sandbox Code Playgroud)

接下来,在SetSite方法中,您注册以获取通知.不要忘记调用基类.

STDMETHODIMP CMyBho::SetSite( IUnknown* unkSite )
{
    ...
    hr = IObjectWithSiteImpl::SetSite( unkSite );
    if( unkSite ) {
        ...
        // advise to browser event.
        CComPtr<IServiceProvider> ifsp;
        hr = m_spUnkSite.QueryInterface( &ifsp );
        hr = ifsp->QueryService( SID_SwebBrowserApp, IID_IWebBrowser2, &m_ifbrz );
        hr = DispEventAdvise( m_ifbrz );
    }
    else {
        // release various resources (m_ifbrz will be released automatically by its dtor)
        ...
    }
...
}
Run Code Online (Sandbox Code Playgroud)

文档加载完成后,将调用此函数:

void STDMETHODCALLTYPE CMyBho::onDocComplete( IDispatch* dispBrz, VARIANT* pvarUrl )
{
    CComPtr<IDispatch> dispDoc;
    CComPtr<IHTMLDocument2> ifDoc;
    CComPtr<IHTMLWindow2> ifWnd;
    CComPtr<IDispatchEx> dispxWnd;

    hr = m_ifbrz->get_Document( &dispDoc );
    hr = dispDoc.QueryInterface( &ifDoc );      
    hr = ifDoc->get_parentWindow( &ifWnd );
    hr = ifWnd.QueryInterface( &dispxWnd );

    // now ... be careful. Do exactly as described here. Very easy to make mistakes
    CComBSTR propName( L"myBho" );
    DISPID dispid;
    hr = dispxWnd->GetDispID( propName, fdexNameEnsure, &dispid );

    CComVariant varMyBho( (IDispatch*)this );
    DISPPARAMS params;
    params.cArgs = 1;
    params.cNamedArgs = 0;
    params.rgvarg = &varMyBho;            
    params.rgdispidNamedArgs = NULL;
    hr = dispxWnd->Invoke( dispid, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_PROPERTYPUTREF,
                           &params, NULL, NULL, NULL );
}
Run Code Online (Sandbox Code Playgroud)

至于你的其他问题:

  • 显然,我的回答意味着您可以从BHO创建一个自动化对象来编写脚本.您的对象也可以使用新的ActiveXObject进行实例化.在这种情况下,不要忘记告诉IE你的对象是安全的脚本(旁注:让您的BHO安全的脚本.确保恶意网站将无法利用您的BHO).

  • 我认为window.myBho比window.external.myBho更好.从语义上讲,"外部"是指mshtml浏览器控件托管在另一个应用程序中.

希望这有帮助.

  • 我的应用范围非常有限。它在任何给定页面上插入 jquery 和 jqueryui 库。然后用于增强现有应用程序的外观。将 html 文本保存在固定位置并始终为文本需要与 js 和 bho 的交互。你说的问题非常真实,而且还有与activex相关的安全角度。这就是为什么我想使用 bho。我创建了简单的裸骨 bho 应用程序来测试这个。请在 http://code.google.com/p/simple-atl-bho/downloads/list 上找到它。谢谢。 (2认同)
  • @uri:谢谢。终于让它工作了,即从 JS 调用 BHO 而没有 ActiveX。至少适用于 IE8 和 IE7。虽然不确定ie9及以上。这只是我这边的一个愚蠢的错误!!忘记在 idl 中公开方法。尽管“DISPATCH_PROPERTYPUTREF”对我不起作用,但我尝试过“DISPATCH_PROPERTYPUT”并且它起作用了。谢谢你的时间。 (2认同)