views:

112

answers:

1

I am trying to write a Browser Helper Object (BHO) in C# that manipulates the DOM on a separate thread. I've seen several other questions related to this, and the answer seems to be "you need to marshal the DOM objects from the thread they were created on to your worker thread." Good advice, and it makes perfect sense, but I can find no C# examples on how to do this. There are some vague pointers to some P/Invoke APIs that need to be used, but I'm having difficulty seeing how to implement that into a BHO.

I learn best by example, and the documentation is woefully short of .NET examples of this sort of thing. Can someone point me to an example where, within the context of a managed code BHO, the DOM is manipulated via a worker thread?

+1  A: 

You shouldn't have to perform any manual marshaling; the managed runtime handles any cross-apartment COM object marshaling on your behalf.

Here's an example; this sample managed BHO waits until the DocumentComplete event fires and spins up a ThreadPool background thread that waits for a second then changes the title of the page to "Hello, StackOverflow!" and adds a new text node with a special message:

private void OnDocumentComplete(object frame, ref object urlObj)
{
    System.Threading.ThreadPool.QueueUserWorkItem((o) =>
    {
        System.Threading.Thread.Sleep(1000);
            HTMLDocument document = (HTMLDocument)this.browser.Document;
            document.title = "Hello, StackOverflow!";

            IHTMLDOMNode greetings = document.createTextNode("Hi there!");

            IHTMLDOMNode body = document.body as IHTMLDOMNode;
            body.insertBefore(greetings, body.firstChild);                
    }, this.browser);
}

#region IObjectWithSite Members

int IObjectWithSite.SetSite(object site)
{
    if (site != null)
    {
        this.browser = (WebBrowser)site;
        this.browser.DocumentComplete +=
         new DWebBrowserEvents2_DocumentCompleteEventHandler(
          this.OnDocumentComplete);
    }
    else
    {
        if (this.browser != null)
        {
            this.browser.DocumentComplete -=
             new DWebBrowserEvents2_DocumentCompleteEventHandler(
              this.OnDocumentComplete);
            this.browser = null;
        }
    }
    return 0;
}

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

    return hr;
}

#endregion
Andy Hopper