从 Javascript 调用 BHO 方法?
我正在尝试从 javascript 调用我的 BHO 方法。问题与以下帖子中所述相同:
- 从 Javascript 函数调用 BHO
- <一个href="http://social.msdn.microsoft.com/Forums/en-US/ieextensiondevelopment/thread/91d4076e-4795-4d9e-9b07-5b9c9eca62fb/" rel="nofollow noreferrer">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 来公开它(如中 jeffdav 的回答) href="http://social.msdn.microsoft.com/Forums/en-US/ieextensiondevelopment/thread/91d4076e-4795-4d9e-9b07-5b9c9eca62fb/" rel="nofollow noreferrer">[2] )?如果是的话该怎么做。
- 基本上我想扩展
window.external
但上面链接中显示的方式 [2] 使用var x = new ActiveXObject("MySampleATL.MyClass");
;两个调用约定是相同还是不同?
注意:
- SO 上有一篇相关文章暗示可以通过插入此
[id(1), helpstring("method DoSomething")] HRESULT DoSomething();。我不确定它是如何完成的,也无法通过谷歌找到任何支持资源。
- 我知道这篇文章calling-into-your-bho-from-a-client-script,但还没有尝试过,因为它是使用 ActiveX 解决问题的。
- 我避免使用 ActiveX 的原因主要是由于安全限制。
编辑1
It seems there is a way to extend the
window.external
. Check this. Specially the section titled IDocHostUIHandler::GetExternal: Extending the DOM.
Now assuming our IDispatch interface is on the same object that implements IDocHostUIHandler. Then we can do something like this: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: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 );
}
Javascript:
<script language='javascript'>
function call_external(){
try{
alert(window.ITestScript);
}catch(err){
alert(err.description );
}
}
</script>
编辑 3
After spending three days on this, I think I should take the ActiveX path. Writing a basic activex is just way to easy, written one and tested on all major IE releases. I am leaving this question as open, please see comments in Uri's answer (many thanks to him). I have tried most of his suggestions (except for 4 and 5).
I will also suggest you to see the MSDN IDispatcEx sample
. If you find a solution then please post, if I find a solution then I will definitely update here.编辑4
See my last comment in URI's post. Issue Resolved.
I am trying to call my BHO method from the javascript. The problem is same as stated in the the following posts:
- Call BHO from Javascript function
- http://social.msdn.microsoft.com/Forums/en-US/ieextensiondevelopment/thread/91d4076e-4795-4d9e-9b07-5b9c9eca62fb/
- Calling C++ function from JavaScript script running in a web browser control
Third link is another SO post talking about it, but I did not understand the need and code. Also the shared working sample keeps crashing on windows 7 with ie 8 and windows vista with ie 7.
If it helps my BHO is written in C++ using ATL.
What I have tried:
I have written a very basic BHO and tried the approach as mentioned here by Igor Tandetnik. There is no exception generated but when I open the following html file in IE then it says 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>
Question:
- Please clarify whether is it possible to expose and call BHO method from javascript or do i have to expose it using an activex (as answered by jeffdav in [2] )? If yes then how to do it.
- Basically i want to extend the
window.external
but the way shown in the above link [2] usesvar x = new ActiveXObject("MySampleATL.MyClass");
; Is both the calling conventions are same or different?
Note:
- There is a related post on SO which gives hint that it is possible through inserting this
[id(1), helpstring("method DoSomething")] HRESULT DoSomething();
in the BHO IDL file. I am not sure how it was done and couldn't find any supporting resource through google. - I am aware of this post calling-into-your-bho-from-a-client-script, but haven't tried it as it is solving the problem using ActiveX.
- My reason to avoid ActiveX is primarily due to the security restrictions.
Edit 1
It seems there is a way to extend the
window.external
. Check this. Specially the section titled IDocHostUIHandler::GetExternal: Extending the DOM.
Now assuming our IDispatch interface is on the same object that implements IDocHostUIHandler. Then we can do something like this:HRESULT CBrowserHost::GetExternal(IDispatch **ppDispatch)
{
*ppDispatch = this;
return S_OK;
}
The problem with this approach is that it won't append to the existing windows methods, but rather replace them. Please tell if I am wrong.
Edit 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>
Edit 3
After spending three days on this, I think I should take the ActiveX path. Writing a basic activex is just way to easy, written one and tested on all major IE releases. I am leaving this question as open, please see comments in Uri's answer (many thanks to him). I have tried most of his suggestions (except for 4 and 5).
I will also suggest you to see the MSDN IDispatcEx sample
. If you find a solution then please post, if I find a solution then I will definitely update here.Edit 4
See my last comment in URI's post. Issue Resolved.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
Igor Tandetnik 的方法是正确的方法。这篇文章的问题是示例代码(至少在我发现它的几页上)是不完整的。我经历了很多次尝试和错误,直到我让它发挥作用。
下面是我的代码的一个很好的部分,它可以实现这个技巧:
假设您有一个 CMyBho 类,并且您想要为 Java 脚本公开 IMyBho 自动化对象
类定义:
您可以从标准 CComObjectRootEx 和 CComCoClass 派生,使其“可共同创建”。您有 IObjectWithSiteImpl(重用此基类实现的 m_spUnkSite)。 IDispatchImpl 实现您的自动化对象,IDispatchEventImpl 是从浏览器获取通知的接收器:
接下来是 SetSite 方法,您可以在其中注册以获取通知。不要忘记调用基类。
当文档加载完成时,将调用此函数:
至于您的其他问题:
显然,我的回答意味着您可以使自动化对象可用于从 BHO 编写脚本。您的对象也可能会使用新的 ActiveXObject 进行实例化。在这种情况下,请不要忘记告诉 IE 您的对象对于脚本编写是安全的(旁注:使您的 BHO 对于脚本编写是安全的。确保恶意网站无法利用您的 BHO)。
我认为 window.myBho 比 window.external.myBho 更好。从语义上讲,“外部”是指 mshtml
浏览器控件托管在另一个应用程序中。
希望这有帮助。
Igor Tandetnik's method is the correct approach. The problem with the post is that the sample code (at least on the few pages I spotted it) is that it wasn't complete. I had many trials and errors until I got it working.
Here is a good chunk of my code that does the trick:
Say you have a class CMyBho, and you want to expose IMyBho automation object for Java scripts
Class definition:
You derive from the standard CComObjectRootEx, and CComCoClass to make it 'co creatble'. You have IObjectWithSiteImpl (reuse the m_spUnkSite implemented by this base class). IDispatchImpl implements your automation object, and IDispatchEventImpl is the sink to get notifications from the browser:
Next, the SetSite method, where you register to get the notification. Don't forget to call the base class.
When document load is complete, this function will be called:
As for your other questions:
Evidently, my answer implies that you can make an automation object available for scripting from your BHO. It is also possible your object will be instantiated with the new ActiveXObject. In that case don't forget to tell IE your object is safe for scripting (side note: make your BHO safe for scripting. make sure malicious web site won't be able to exploit your BHO).
I think that window.myBho is a better place than window.external.myBho. Semantically, 'external' is when the mshtml
browser control is hosted within another application.
Hope this helped.