我正在尝试从 javascript 调用我的 BHO 方法。问题与以下帖子中所述相同:
- 从 Javascript 函数调用 BHO
- http://social.msdn.microsoft.com/Forums/en-US/ieextensiondevelopment/thread/91d4076e-4795-4d9e-9b07-5b9c9eca62fb/
- 从运行在 Web 浏览器控件中的 JavaScript 脚本调用 C++ 函数
第三个链接是另一篇谈论它的SO帖子,但我不明白需求和代码。此外,共享的工作示例在带有 ie 8 的 windows 7 和带有 ie 7 的 windows vista 上不断崩溃。
如果有帮助,我的 BHO 是使用 ATL 用 C++ 编写的。
我试过的:
我写了一个非常基本的 BHO 并尝试了Igor Tandetnik提到的方法。没有产生异常,但是当我在 IE 中打开以下 html 文件时,它会显示object undefined。
<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>
问题:
- 请澄清是否可以从 javascript 公开和调用 BHO 方法,或者我是否必须使用 activex 公开它(如[2]中的jeffdav回答)?如果是,那么该怎么做。
- 基本上我想扩展
window.external
上面链接[2]中显示的方式使用var x = new ActiveXObject("MySampleATL.MyClass");
; 两个调用约定是相同还是不同?
笔记:
- SO上有一个相关的帖子,暗示可以通过将其插入
[id(1), helpstring("method DoSomething")] HRESULT DoSomething();
BHO IDL文件中。我不确定它是如何完成的,也无法通过谷歌找到任何支持资源。 - 我知道这篇文章call-into-your-bho-from-a-client-script,但没有尝试过,因为它正在使用 ActiveX 解决问题。
- 我避免使用 ActiveX 的原因主要是由于安全限制。
编辑 1
似乎有一种方法可以扩展
window.external
. 检查这个。特别是标题为IDocHostUIHandler::GetExternal: Extending the DOM.
现在假设我们的 IDispatch 接口位于实现 IDocHostUIHandler 的同一对象上的部分。然后我们可以这样做:
HRESULT CBrowserHost::GetExternal(IDispatch **ppDispatch)
{
*ppDispatch = this;
return S_OK;
}
这种方法的问题在于它不会附加到现有的 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;
};
// 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,
¶ms, NULL, NULL, NULL );
}
The Javascript:
<script language='javascript'>
function call_external(){
try{
alert(window.ITestScript);
}catch(err){
alert(err.description );
}
}
</script>
编辑 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.