Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Calling C# BHO methods from Javascript (still not working)

I followed this answer exactly and read and reread all google findings. Unfortunately mostly all of them simply the copy and pastes of the referenced answer (including the 'Stop banging your head against the wall and go celebrate!') sentence and it does not work for me... So after a half day working I am really about to start banging my head...

My simple error: The javascript windows.myExtension object is 'undefined' so calling Foo on it throws error. See the full source below. It seems the property set is not viewable in javascript side.

Some more information:

  • I use the Debugger.Launch() statement for convenient way to debug my extension, and the breakpoint is hit, and all BHO extension functions are properly called and run.
  • The commented alternative (with property.SetProperty) also does not work, with the same error:

    console.log(window.myExtension); // writes 'undefined', why?

  • Using VS 2010, Windows 7 x64, IE 9

Please let me help running this... Thx in advance

The simple test page:

<!DOCTYPE html>
<html>
<head>
    <script type="text/javascript">
    console.log(window.myExtension);  // Writes undefined why? It should be an object...
    var result = window.myExtension.Foo("bar"); // Obviously throws and error if window.myExtension is undefined 
    </script>
    <title></title>
</head>
<body>

</body>
</html>

BrowserHelperObject.cs

using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.Expando;

using Microsoft.Win32;

using SHDocVw;

namespace IEExtensionTest
{
[ComVisible(true)]
[Guid("DA8EA345-02AE-434E-82E9-448E3DB7629E")]
[ClassInterface(ClassInterfaceType.None)]
[ProgId("MyExtension")]
[ComDefaultInterface(typeof(IExtension))]
public class BrowserHelperObject : IObjectWithSite, IExtension
{
    private WebBrowser webBrowser;

    public int Foo(string s)
    {
        return 0;
    }

    public void OnDocumentComplete(dynamic frame, ref dynamic url)
    {
        Debugger.Launch();
        dynamic window = webBrowser.Document.parentWindow;
        var windowEx = (IExpando)window;
        windowEx.AddProperty("myExtension");
        window.myExtension = this;
        //var property = windowEx.AddProperty("MyExtension");
        //property.SetValue(windowEx, this, null);
    }


    public static string BHOKEYNAME = "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Browser Helper Objects";

    [ComRegisterFunction]
    public static void RegisterBHO(Type type)
    {
        RegistryKey registryKey = Registry.LocalMachine.OpenSubKey(BHOKEYNAME, true);

        if (registryKey == null)
            registryKey = Registry.LocalMachine.CreateSubKey(BHOKEYNAME);

        string guid = type.GUID.ToString("B");
        RegistryKey ourKey = registryKey.OpenSubKey(guid);

        if (ourKey == null)
            ourKey = registryKey.CreateSubKey(guid);

        ourKey.SetValue("Alright", 1);
        registryKey.Close();
        ourKey.Close();
    }

    [ComUnregisterFunction]
    public static void UnregisterBHO(Type type)
    {
        RegistryKey registryKey = Registry.LocalMachine.OpenSubKey(BHOKEYNAME, true);
        string guid = type.GUID.ToString("B");

        if (registryKey != null)
            registryKey.DeleteSubKey(guid, false);
    }

    public int SetSite(object site)
    {

        if (site != null)
        {
            webBrowser = (WebBrowser)site;
            webBrowser.DocumentComplete += OnDocumentComplete;
        }
        else
        {
            webBrowser.DocumentComplete -= OnDocumentComplete;
            webBrowser = null;
        }

        return 0;

    }

    public int GetSite(ref Guid guid, out IntPtr ppvSite)
    {
        IntPtr punk = Marshal.GetIUnknownForObject(webBrowser);
        int hr = Marshal.QueryInterface(punk, ref guid, out ppvSite);
        Marshal.Release(punk);

        return hr;
    }
}

IObjectWithSite.cs

using System;
using System.Runtime.InteropServices;

namespace IEExtensionTest
{
[ComVisible(true)]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid("FC4801A3-2BA9-11CF-A229-00AA003D7352")]
public interface IObjectWithSite
{
    [PreserveSig]
    int SetSite([MarshalAs(UnmanagedType.IUnknown)] object site);

    [PreserveSig]
    int GetSite(ref Guid guid, out IntPtr ppvSite);
}
}

IExtension.cs

using System;
using System.Runtime.InteropServices;

namespace IEExtensionTest
{
[ComVisible(true)]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid("FC4801A3-2BA9-11CF-A229-00AA003D7352")]
public interface IObjectWithSite
{
    [PreserveSig]
    int SetSite([MarshalAs(UnmanagedType.IUnknown)] object site);

    [PreserveSig]
    int GetSite(ref Guid guid, out IntPtr ppvSite);
}
}

Post build step is configured as follows (and runs properly):

"C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\Bin\NETFX 4.0 Tools\gacutil.exe" /f /i "$(TargetDir)$(TargetFileName)"
"C:\Windows\Microsoft.NET\Framework\v4.0.30319\RegAsm.exe" /unregister "$(TargetDir)$(TargetFileName)"
"C:\Windows\Microsoft.NET\Framework\v4.0.30319\RegAsm.exe" "$(TargetDir)$(TargetFileName)"
like image 217
g.pickardou Avatar asked Feb 25 '13 14:02

g.pickardou


1 Answers

I have a hack-y solution to this. It is working for me right now so I am posting it here. If I encounter any issue with it, I will update this post.

@Eli Gassert identifies the problem correctly. In the SetSite function we are adding a handler to DocumentComplete event. So it would not have executed when we are in $.ready().

So what I have done is to add handler to BeforeScriptExecute event. This is the relevant part of my BHO.cs

public int SetSite(object site)
    {
        this.site = site;
        if(site != null)
        {
            webBrowser = (IWebBrowser2)site;
            ((DWebBrowserEvents2_Event)webBrowser).BeforeScriptExecute += S2_BeforeScriptExecute;
            //((DWebBrowserEvents2_Event)webBrowser).DocumentComplete += S2_DocumentComplete;
        }
        else
        {
            ((DWebBrowserEvents2_Event)webBrowser).BeforeScriptExecute -= S2_BeforeScriptExecute;
            //((DWebBrowserEvents2_Event)webBrowser).DocumentComplete -= S2_DocumentComplete;
            webBrowser = null;
        }
        return 0;
    }

The signature for the handler is different, and here is the code for the handler. This is still in BHO.cs

   private void S2_BeforeScriptExecute(object pDispWindow)
    {
        //if (pDisp != this.site) { return; }

        dynamic window = webBrowser.Document.parentWindow;
        IExpando windowEx = (IExpando)window;
        windowEx.AddProperty("myprop");
        window.myprop = this;
    }

I just pasted the code from the DocumentComplete method and commented out the condition at the top.

As a result, I am able to see myprop in the jquery $.ready(). This has solved my immediate problem and I am moving ahead with my code.

Needless to say, my addon is only providing methods that will be called from javascript and does not need to do anything with document contents.

I don't yet know what is the use for pDispWindow and what is the impact of not checking if it is null, etc.

like image 128
Kinjal Dixit Avatar answered Sep 30 '22 01:09

Kinjal Dixit