2012-01-11 20 views
10

Sto provando a chiamare il mio metodo BHO dal javascript. Il problema è lo stesso come indicato nei seguenti posti:Chiamare il metodo BHO da Javascript?

  1. Call BHO from Javascript function
  2. http://social.msdn.microsoft.com/Forums/en-US/ieextensiondevelopment/thread/91d4076e-4795-4d9e-9b07-5b9c9eca62fb/
  3. Calling C++ function from JavaScript script running in a web browser control

terzo anello è un altro SO post parlando di esso, ma non ho capito la necessità e il codice. Anche l'esempio di lavoro condiviso continua a bloccarsi su Windows 7 con ie 8 e windows vista con ie 7.

Se aiuta il mio BHO è scritto in C++ usando ATL.

Quello che ho cercato:

Ho scritto un BHO molto semplice e provato l'approccio come detto here da Igor Tandetnik. Non è stata generata alcuna eccezione, ma quando apro il seguente file html in IE, viene indicato l'oggetto non definito.

<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> 

Domanda:

  1. Si prega di chiarire se è possibile esporre e chiamare il metodo BHO da JavaScript o devo esporlo tramite un ActiveX (come risposta da jeffdav in [2])? Se sì, allora come si fa?
  2. Fondamentalmente voglio estendere il window.external ma il modo mostrato nel link sopra [2] utilizza var x = new ActiveXObject("MySampleATL.MyClass");; Entrambe le convenzioni di chiamata sono uguali o diverse?

Nota:

  1. C'è un post correlati su SO che dà suggerimento che è possibile attraverso l'inserimento di questo [id(1), helpstring("method DoSomething")] HRESULT DoSomething(); nel file BHO IDL. Non sono sicuro di come è stato fatto e non sono riuscito a trovare alcuna risorsa di supporto tramite Google.
  2. Sono a conoscenza di questo post calling-into-your-bho-from-a-client-script, ma non l'ho provato in quanto risolve il problema utilizzando ActiveX.
  3. La ragione per cui si desidera evitare ActiveX è principalmente dovuta alle restrizioni di sicurezza.

Modifica 1


Sembra che ci sia un modo per estendere la window.external. Controllare this. Specialmente la sezione IDocHostUIHandler::GetExternal: Extending the DOM. Ora supponiamo che la nostra interfaccia IDispatch si trovi sullo stesso oggetto che implementa IDocHostUIHandler.Poi possiamo fare qualcosa di simile:

HRESULT CBrowserHost::GetExternal(IDispatch **ppDispatch) 
{ 
    *ppDispatch = this; 
    return S_OK; 
} 

Il problema con questo approccio è che non aggiungerà ai metodi esistenti di Windows, ma piuttosto sostituirli. Per favore, dimmi se ho torto.

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, 
      &params, NULL, NULL, NULL); 

} 

The Javascript:

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

Edit 3


Dopo aver trascorso tre giorni in questo, penso che dovrei prendere il sentiero ActiveX. Scrivere un activex di base è solo un modo semplice, scritto e testato su tutte le principali versioni di IE. Lascio questa domanda aperta, per favore vedi i commenti nella risposta di Uri (molte grazie a lui). Ho provato la maggior parte dei suoi suggerimenti (ad eccezione di 4 e 5). I will also suggest you to see the MSDN IDispatcEx sample. Se trovi una soluzione, per favore posta, se trovo una soluzione, la aggiornerò sicuramente qui.

Edit 4


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

risposta

7

metodo di Igor Tandetnik è l'approccio corretto. Il problema con il post è che il codice di esempio (almeno nelle poche pagine che ho individuato) è che non era completo. Ho avuto molte prove ed errori, fino a quando non ho funzionato. Ecco buona parte del mio codice che fa il trucco:

Diciamo che avete una classe CMyBho, e si desidera esporre oggetto di automazione IMyBho per gli script Java

definizione Classe:
si deriva dal CComObjectRootEx di serie e CComCoClass per renderlo "co creatble". Hai IObjectWithSiteImpl (riusare il m_spUnkSite implementato da questa classe base). IDispatchImpl implementa l'oggetto di automazione, e IDispatchEventImpl è il dissipatore per ottenere le notifiche dal browser:

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 

} 

Avanti, il metodo SetSite, dove si registra per ottenere la notifica. Non dimenticare di chiamare la classe base.

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) 
     ... 
    } 
... 
} 

Quando il carico documento è completo, questa funzione sarà chiamato:

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); 
} 

Per quanto riguarda le vostre altre domande:

  • Evidentemente, la mia risposta implica che si può fa un'automazione oggetto disponibile per lo scripting dal tuo BHO. È anche possibile che il tuo oggetto venga istanziato con un nuovo ActiveXObject.In tal caso, non dimenticare di dire a IE che il tuo oggetto è sicuro per lo scripting (nota a margine: rendi il tuo BHO sicuro per gli script, assicurati che il sito Web dannoso non sia in grado di sfruttare il tuo BHO).

  • Penso che window.myBho sia un posto migliore di window.external.myBho. Semanticamente, 'external' è quando il controllo del browser mshtml è ospitato in un'altra applicazione.

Spero che questo ha aiutato.

+0

Grazie per la risposta. Ho provato il tuo suggerimento ma quando accedo all'oggetto bho nel mio javascript mi ​​sta dando l'oggetto undefined. Si prega di consultare la mia risposta aggiornata, ho incluso il codice BHO e il javascript corrispondente. – Favonius

+0

Ho modificato queste due istruzioni 'params.rgvarg = & varTanduBar; params.rgdispidNamedArgs = null; 'a questo' params.rgvarg = & varMyBho; params.rgdispidNamedArgs = NULL; '. Altrimenti ho usato lo stesso codice. – Favonius

+0

Inoltre, ho provato 'DISPATCH_PROPERTYPUTREF' e' DISPATCH_PROPERTYPUT'. Ma in questo caso entrambi non funzionano. – Favonius

Problemi correlati