title: Calling BHO method from Javascript?
body: <p>I am trying to call my BHO method from the javascript. The problem is same as stated in the the following posts:</p>
  
  <ol>
  <li><a href="https://stackoverflow.com/questions/5474808/call-bho-from-javascript-function">Call BHO from Javascript function</a></li>
  <li><a 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/</a></li>
  <li><a href="https://stackoverflow.com/questions/3747414/calling-c-function-from-javascript-script-running-in-a-web-browser-control">Calling C++ function from JavaScript script running in a web browser control</a></li>
  </ol>
  
  <p><strong>Third link</strong> 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.</p>
  
  <p>If it helps my BHO is written in C++ using ATL. </p>
  
  <p><strong>What I have tried:</strong></p>
  
  <p>I have written a very basic BHO and tried the approach as mentioned <a href="http://groups.google.com/group/microsoft.public.inetsdk.programming.webbrowser_ctl/browse_thread/thread/afb353de46bae2f8?pli=1" rel="nofollow noreferrer">here</a> by <strong>Igor Tandetnik</strong>. There is no exception generated but when I open the following html file in IE then it says <strong>object undefined</strong>.</p>
  
  <pre><code>&lt;html&gt;
      &lt;head&gt;
          &lt;script language='javascript'&gt;
              function call_external(){
                  try{
                  alert(window.external.TestScript);
                  //JQueryTest.HelloJquery('a');
                  }catch(err){
                      alert(err.description );
                  }
              }
          &lt;/script&gt;
      &lt;/head&gt;
      &lt;body id='bodyid' onload="call_external();"&gt;
          &lt;center&gt;&lt;div&gt;&lt;span&gt;Hello jQuery!!&lt;/span&gt;&lt;/div&gt;&lt;/center&gt;
      &lt;/boay&gt;
  &lt;/html&gt;
  </code></pre>
  
  <p><strong>Question:</strong></p>
  
  <ol>
  <li>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 <strong>jeffdav</strong> in <a href="http://social.msdn.microsoft.com/Forums/en-US/ieextensiondevelopment/thread/91d4076e-4795-4d9e-9b07-5b9c9eca62fb/" rel="nofollow noreferrer">[2]</a> )? If yes then how to do it.</li>
  <li>Basically i want to extend the <code>window.external</code> but the way shown in the above link <a href="http://social.msdn.microsoft.com/Forums/en-US/ieextensiondevelopment/thread/91d4076e-4795-4d9e-9b07-5b9c9eca62fb/" rel="nofollow noreferrer">[2]</a> uses <code>var x = new ActiveXObject("MySampleATL.MyClass");</code>; Is both the calling conventions are same or different? </li>
  </ol>
  
  <p><strong>Note:</strong> </p>
  
  <ol>
  <li>There is a related post on SO which gives hint that it is possible through inserting this <code>[id(1), helpstring("method DoSomething")] HRESULT DoSomething();</code> in the BHO IDL file. I am not sure how it was done and couldn't find any supporting resource through google.</li>
  <li>I am aware of this post <a href="http://blogs.msdn.com/b/nicd/archive/2007/04/18/calling-into-your-bho-from-a-client-script.aspx" rel="nofollow noreferrer">calling-into-your-bho-from-a-client-script</a>, but haven't tried it as it is solving the problem using ActiveX. </li>
  <li>My reason to avoid ActiveX is primarily due to the security restrictions.</li>
  </ol>
  
  <p><strong>Edit 1</strong>
  <hr>
  It seems there is a way to extend the <code>window.external</code>. Check <a href="http://msdn.microsoft.com/en-us/library/aa770041%28v=vs.85%29.aspx" rel="nofollow noreferrer">this</a>. Specially the section titled <code>IDocHostUIHandler::GetExternal: Extending the DOM.</code>Now assuming our IDispatch interface is on the same object that implements IDocHostUIHandler. Then we can do something like this:</p>
  
  <pre><code>HRESULT CBrowserHost::GetExternal(IDispatch **ppDispatch) 
  {
      *ppDispatch = this;
      return S_OK;
  }
  </code></pre>
  
  <p>The <strong>problem</strong> with this approach is that it won't append to the existing windows methods, but rather replace them. Please tell if I am wrong.</p>
  
  <p><strong>Edit 2</strong>
  <hr>
  <strong><code>The BHO Class:</code></strong></p>
  
  <pre><code>class ATL_NO_VTABLE CTestScript :
      public CComObjectRootEx&lt;CComSingleThreadModel&gt;,
      public CComCoClass&lt;CTestScript, &amp;CLSID_TestScript&gt;,
      public IObjectWithSiteImpl&lt;CTestScript&gt;,
      public IDispatchImpl&lt;ITestScript, &amp;IID_ITestScript, &amp;LIBID_TestBHOLib, /*wMajor =*/ 1, /*wMinor =*/ 0&gt;,
      public IDispEventImpl&lt;1, CTestScript, &amp;DIID_DWebBrowserEvents2, &amp;LIBID_SHDocVw, 1, 1&gt;
  {
  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&lt;IWebBrowser2&gt;  m_spWebBrowser;
      BOOL m_fAdvised;
  };
  </code></pre>
  
  <p><strong><code>// TestScript.cpp : Implementation of CTestScript</code></strong></p>
  
  <pre><code>#include "stdafx.h"
  #include "TestScript.h"
  
  
  // CTestScript
  
  STDMETHODIMP CTestScript::SetSite(IUnknown* pUnkSite)
  {
      if (pUnkSite != NULL)
      {
          HRESULT hr = pUnkSite-&gt;QueryInterface(IID_IWebBrowser2, (void **)&amp;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&lt;CTestScript&gt;::SetSite(pUnkSite);
  }
  
  void STDMETHODCALLTYPE CTestScript::OnDocumentComplete(IDispatch *pDisp, VARIANT *pvarURL)
  {
          CComPtr&lt;IDispatch&gt; dispDoc;
          CComPtr&lt;IHTMLDocument2&gt; ifDoc;
          CComPtr&lt;IHTMLWindow2&gt; ifWnd;
          CComPtr&lt;IDispatchEx&gt; dispxWnd;
  
          HRESULT hr = m_spWebBrowser-&gt;get_Document( &amp;dispDoc );
          hr = dispDoc.QueryInterface( &amp;ifDoc );      
          hr = ifDoc-&gt;get_parentWindow( &amp;ifWnd );
          hr = ifWnd.QueryInterface( &amp;dispxWnd );
  
          // now ... be careful. Do exactly as described here. Very easy to make mistakes
          CComBSTR propName( L"myBho" );
          DISPID dispid;
          hr = dispxWnd-&gt;GetDispID( propName, fdexNameEnsure, &amp;dispid );
  
          CComVariant varMyBho( (IDispatch*)this );
          DISPPARAMS params;
          params.cArgs = 1;
          params.cNamedArgs = 0;
          params.rgvarg = &amp;varMyBho;            
          params.rgdispidNamedArgs = NULL;
          hr = dispxWnd-&gt;Invoke( dispid, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_PROPERTYPUT,
              &amp;params, NULL, NULL, NULL );
  
  }
  </code></pre>
  
  <p><strong><code>The Javascript:</code></strong></p>
  
  <pre><code>&lt;script language='javascript'&gt;
              function call_external(){
                  try{
                  alert(window.ITestScript);
                  }catch(err){
                      alert(err.description );
                  }
              }
          &lt;/script&gt;
  </code></pre>
  
  <p><strong>Edit 3</strong>
  <hr>
  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). <code>I will also suggest you to see the MSDN IDispatcEx sample</code>. If you find a solution then please post, if I find a solution then I will definitely update here. </p>
  
  <p><strong>Edit 4</strong>
  <hr>
  <strong><code>See my last comment in URI's post. Issue Resolved.</code></strong></p>