Internet explorer 从Javascript调用BHO方法?
我试图从javascript调用我的BHO方法。问题与以下帖子中所述的相同: 第三个链接是另一篇关于它的帖子,但我不理解它的需要和代码。此外,共享工作示例在使用ie 8的windows 7和使用ie 7的windows vista上不断崩溃Internet explorer 从Javascript调用BHO方法?,internet-explorer,activex,atl,bho,ieaddon,Internet Explorer,Activex,Atl,Bho,Ieaddon,我试图从javascript调用我的BHO方法。问题与以下帖子中所述的相同: 第三个链接是另一篇关于它的帖子,但我不理解它的需要和代码。此外,共享工作示例在使用ie 8的windows 7和使用ie 7的windows vista上不断崩溃 如果帮助我的BHO是用C++写的,使用ATL. 我尝试过的: 我写了一个非常基本的BHO,并尝试了Igor Tandetnik所提到的方法。没有生成异常,但当我在IE中打开以下html文件时,它会显示对象未定义 <html> <
如果帮助我的BHO是用C++写的,使用ATL. 我尝试过的:
我写了一个非常基本的BHO,并尝试了Igor 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>
这种方法的问题是它不会附加到现有的windows方法中,而是替换它们。如果我错了,请告诉我
编辑2
BHO类:
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;
};
#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 );
}
<script language='javascript'>
function call_external(){
try{
alert(window.ITestScript);
}catch(err){
alert(err.description );
}
}
</script>
Javascript:
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;
};
#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 );
}
<script language='javascript'>
function call_external(){
try{
alert(window.ITestScript);
}catch(err){
alert(err.description );
}
}
</script>
函数调用_external(){
试一试{
警报(window.ITestScript);
}捕捉(错误){
警报(错误描述);
}
}
编辑3
在花了三天的时间之后,我想我应该选择ActiveX路径。编写一个基本的activex只是一种简单的方法,它可以编写并在所有主要的IE版本上测试。我将这个问题留待讨论,请参阅Uri回答中的评论(非常感谢他)。我试过他的大部分建议(除了第4和第5条)
我还建议您查看MSDN IDispatcEx示例
。如果你找到一个解决方案,然后请张贴,如果我找到一个解决方案,那么我一定会更新这里
编辑4
查看URI帖子中的最后一条评论。问题已解决。
Igor Tandetnik的方法是正确的方法。这篇文章的问题在于示例代码(至少在我发现的几页上)不完整。我经历了很多尝试和错误,直到我让它工作。
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;
};
#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 );
}
<script language='javascript'>
function call_external(){
try{
alert(window.ITestScript);
}catch(err){
alert(err.description );
}
}
</script>
下面是我的一段代码:
假设您有一个类CMyBho,并且希望为Java脚本公开IMyBho自动化对象
类定义:您从标准的CComObjectRootEx和CComCoClass派生,使其“可共同创建”。您拥有IObjectWithSiteImpl(重用此基类实现的m_spUnkSite)。IDispatchImpl实现您的自动化对象,IDispatchevenTempl是从浏览器获取通知的接收器:
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
}
class ATL\u NO\u VTABLE CMyBho
:public CComObjectRootEx
,公共CComCoClass
,公共IObjectWithSiteImpl
,公共图书馆
,IDispatchEventImpl
{
...
公众:
开始COM地图(CMyBho)
COM_接口_条目(IMyBho)
COM_接口_条目(IDispatch)
COM_接口_条目(IObjectWithSite)
END_COM_MAP()
...
开始\u接收器\u映射(CMyBho)
接收项(1,DIID\U DWebbrowserEvents 2,DISPID\u DOCUMENTCOMPLETE,OnDocComplete)
END_SINK_MAP()
...
私人:
CComPtr m_ifbrz;//指向宿主浏览器的指针
}
接下来是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)
...
}
...
}
STDMETHODIMP CMyBho::设置站点(IUnknown*未知站点)
{
...
hr=IObjectWithSiteImpl::SetSite(未确定位置);
如果(未确定现场){
...
//建议浏览事件。
CComPtr ifsp;
hr=m_spUnkSite.QueryInterface(&ifsp);
hr=ifsp->QueryService(SID_SwebBrowserApp、IID_IWebBrowser2和m_ifbrz);
hr=分散式台钳(m_ifbrz);
}
否则{
//释放各种资源(m_ifbrz将由其dtor自动释放)
...
}
...
}
文档加载完成后,将调用此函数:
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,
¶ms, NULL, NULL, NULL );
}
void STDMETHODCALLTYPE CMyBho::onDocComplete(IDispatch*dispBrz,VARIANT*pvarUrl)
{
CComPtr dispDoc;
CComPtr ifDoc;
CComPtr ifWnd;
CComPtr dispxWnd;
hr=m_ifbrz->get_Document(&dispDoc);
hr=dispDoc.QueryInterface(&ifDoc);
hr=ifDoc->get\u parentWindow(&ifWnd);
hr=ifWnd.QueryInterface(&dispxWnd);
//现在…小心点。完全按照这里描述的做。很容易出错
CComBSTR专有名称(L“myBho”);
DISPID DISPID;
hr=dispxWnd->GetDispID(propName、fdexnamesure和dispid);
c变异varMyBho((IDispatch*)本;
DISPPARAMS-params;
参数s.cArgs=1;
params.cNamedArgs=0;
params.rgvarg=&varMyBho;
params.rgdispidNamedArgs=NULL;
hr=dispxWnd->Invoke(dispid,IID\u NULL,LOCALE\u USER\u DEFAULT,DISPATCH\u PROPERTYPUTREF,
&参数,NULL,NULL,NULL);
}
至于你的其他问题:
- 显然,我的回答意味着您可以使自动化对象从BHO中用于脚本编写。您的对象也可能会被新的ActiveXObject实例化。在这种情况下,不要忘记告诉IE您的对象可以安全地进行脚本编写(旁注:确保您的BHO可以安全地进行脚本编写。确保恶意网站无法利用您的BHO)
- 我认为window.myBho比window.external.myBho更好。从语义上讲,“external”是当mshtml 浏览器控件托管在另一个应用程序中
希望这有帮助。谢谢您的回复。我尝试了你的建议,但当我在javascript中访问bho对象时,它给了我
未定义的对象
。请看我的更新答案,我已经包括了BHO代码和相应的javascript。我已经更改了这两条语句params.rgvarg=&varTanduBar;params.rgdispidNamedArgs=null代码>到这个params.rgvarg=&varMyBho;params.rgdispidNamedArgs=NULL代码>。否则,我使用了相同的代码。此外,我还尝试了DISPATCH\u PROPERTYPUTREF