How to marshall call to mshtml object that was created on a different thread? RRS feed

  • Question

  • I know that in .NET when you want to call a method/property of Windows Forms from a different thread you use Control.Invoke(). Is there an equivalent way to do this sort of thing when calling a method of a COM object that was created in a different thread?  I see in COM articles there's talk of using IGlobalInterfaceTable or CoMarshalInterThreadInterfaceInStream but I don't know if this is necessary from CLR objects or whether there's a .NET way of marshalling them, or even where I'd start using those COM functions from my .NET code.

    The reason I ask is that I'm getting an exception some of the time when I use mshtml objects from threads called by other than the one that created them (I think):

    I have a .NET app (Browser Helper Object) that uses the mshtml objects loaded in Internet Explorer windows/tabs. When documents are loaded in IE I retain references to the IHTMLDocument3 objects for a short period of time, and then use them when needed. I have references to IHTMLDocument3 objects created from the current window and also the window that opened the current window, so I'm guessing these were created on different threads. In one case I'm getting an exception when I'm calling a method on the IHTMLDocument3 object from the opener window in an event that is fired by the current window.

    The message from the System.InvalidCastException is:
    Unable to cast COM object of type 'mshtml.HTMLDocumentClass' to interface type 'mshtml.IHTMLDocument3'. This operation failed because the QueryInterface call on the COM component for the interface with IID '{3050F485-98B5-11CF-BB82-00AA00BDCE0B}' failed due to the following error: No such interface supported (Exception from HRESULT: 0x80004002 (E_NOINTERFACE))

    Interestingly, the exception isn't raised whenever I access an IHTMLDocument3 object from the opener window. I have no problems using the IHTMLDocument3 object that is currently loaded in the opener window, but the exception is raised when I use the IHTMLDocument3 object that was previously loaded in the opener window. This might suggest the object is long gone but I can still use such objects in events raised by the window that owned the object, suggesting to me that it may be a threading issue.

    any suggestions greatly appreciated,

    - Rory
    Saturday, November 22, 2008 2:54 PM

All replies

  • This is not a cross thread issue, you are likely using an invalid handle; that is, the object is long gone which is why the cast is failing.

    >I have references to IHTMLDocument3 objects created from the current window and also the window .that opened the current window

    No, this is still the main thread.

    If you can reliably reproduce the problem, set the debugger to break on all exceptions and examine the stack.
    Thursday, February 12, 2009 12:11 AM
  • Try to keep track of DocumentComplete events (simple delegate would suffice). If the event fired you just need to refresh the reference.



    Thursday, September 1, 2011 3:31 PM
  • Having to maintain a ATL/MFC BHO myself, I can tell you that SetSite() is called within the thread that manages the document, and as far as I can tell, every window has its own thread.  This means that the opener window's HTML document object definitely belongs to another thread.  Since you use .Net and since you say you can in fact access the opener document just fine, I doubt that you need to marshal the interface pointer yourself, so you can forget about the GIT or about streaming it.

    A couple of things to note:  IE can load many types of documents, HTML being just one of those.  Therefore, take into account that at some point a user may open a non-HTML document that will not support the IHTMLDocument3 interface (or any other HTML interface for that matter).  IPLUSI is correct about the DocumentComplete event, plus I think you should release the reference to the document in the BeforeNavigate2 event.

    Thursday, September 1, 2011 3:51 PM