Próbuję wywołać moją metodę BHO z javascript. Problem jest taki sam jak podano w następujących stanowisk:Wywołanie metody BHO z Javascript?
- 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
trzecie ogniwo to kolejny SO pisać o tym mówić, ale ja nie rozumiem potrzeba i kod. Również udostępniona robocza próbka ulega awarii w systemie Windows 7, np. 8 i Windows Vista z np. 7.
Jeśli to pomaga, mój BHO jest napisany w C++ przy użyciu ATL.
Co próbowałem:
Napisałem bardzo prosty BHO i próbował podejście jak wspomniano here przez Igor Tandetnik. Nie ma generowanego wyjątku, ale gdy otworzę następujący plik html w IE, oznacza to, że obiekt jest niezdefiniowany 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>
Pytanie:
- Proszę wyjaśnić, czy to możliwe, aby odsłonić i wywołać metodę BHO z javascript czy muszę wystawiać go za pomocą ActiveX (jak odpowiedzieć jeffdav w [2])? Jeśli tak, to jak to zrobić.
- Zasadniczo chcę przedłużyć
window.external
, ale sposób pokazany w powyższym łączu [2] używavar x = new ActiveXObject("MySampleATL.MyClass");
; Czy obie konwencje są takie same czy różne?
Uwaga:
- Jest związane post tak, to które daje wskazówkę, że jest możliwe poprzez wstawienie tej
[id(1), helpstring("method DoSomething")] HRESULT DoSomething();
w pliku BHO IDL. Nie jestem pewien, jak to się stało i nie mogłem znaleźć żadnych zasobów pomocniczych za pośrednictwem google. - Jestem świadomy tego posta calling-into-your-bho-from-a-client-script, ale nie próbowałem go, ponieważ rozwiązuje problem za pomocą ActiveX.
- Mój powód, aby unikać ActiveX wynika przede wszystkim z ograniczeń bezpieczeństwa.
Edycja 1
Wydaje się, że jest to sposób na przedłużenie
window.external
. Sprawdź
this. Zwłaszcza sekcja zatytułowana
IDocHostUIHandler::GetExternal: Extending the DOM.
Teraz zakładając, że nasz interfejs IDispatch znajduje się na tym samym obiekcie, który implementuje IDocHostUIHandler.Wtedy możemy zrobić coś takiego:
HRESULT CBrowserHost::GetExternal(IDispatch **ppDispatch)
{
*ppDispatch = this;
return S_OK;
}
problemu z tego podejścia jest to, że nie dołączy do istniejących metod Windows, ale raczej je zastąpić. Powiedz, czy się mylę.
Edycja 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>
Edycja 3
Po spędzeniu trzech dni na to, myślę, że powinienem wziąć ścieżkę ActiveX. Napisanie podstawowego ActiveX jest po prostu proste, napisane i przetestowane na wszystkich głównych wydaniach IE. Pozostawiam to pytanie jako otwarte, proszę zobaczyć komentarze w odpowiedzi Uri (wielkie dzięki dla niego). Próbowałem większość jego sugestii (z wyjątkiem 4 i 5).
I will also suggest you to see the MSDN IDispatcEx sample
. Jeśli znajdziesz rozwiązanie, proszę napisz, jeśli znajdę rozwiązanie, to na pewno zaktualizuję tutaj.
Edycja 4
See my last comment in URI's post. Issue Resolved.
Dziękuję za odpowiedź. Próbowałem twojej sugestii, ale kiedy mam dostęp do obiektu bho w moim javascript, to daje mi 'object undefined'. Zobacz moją zaktualizowaną odpowiedź, umieściłem kod BHO i odpowiedni javascript. – Favonius
Zmieniłem te dwie instrukcje 'params.rgvarg = & varTanduBar; params.rgdispidNamedArgs = null; 'to this' params.rgvarg = & varMyBho; params.rgdispidNamedArgs = NULL; '. W przeciwnym razie użyłem tego samego kodu. – Favonius
Również próbowałem 'DISPATCH_PROPERTYPUTREF' i' DISPATCH_PROPERTYPUT'. Ale w tym przypadku oba nie działają. – Favonius