سؤال

I have BHO written in C#. Initialization is performed in DocumentComplete event handler in main thread and then I start separate thread and want to use IMarkupServices in that thread but receive following error:

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

Here is how initialization is done:

void ieInstance_DocumentComplete(object pDisp, ref object URL)
{
    InternetExplorer explorer = pDisp as InternetExplorer;
    _ieHtmlDocument2 = (IHTMLDocument2)explorer.Document;
    _markupServices = (IMarkupServices)_ieHtmlDocument2;
    _markupServices.CreateMarkupPointer(out _markupPointerBeginGlob);  // No exception here
    _workerThread = new Thread(WorkerThread);
    _workerThread.IsBackground = true;
    _workerThread.SetApartmentState(Thread.CurrentThread.GetApartmentState());
    _workerThread.Start();
}

Here is thread proc:

void WorkerThread()
{
    _markupServices.CreateMarkupPointer(out _markupPointerBeginGlob);  // Exception here!
}

EDIT: It seems in C++ it is necessary to call following functions to achieve what I need:

CoMarshalInterThreadInterfaceInStream
CoGetInterfaceAndReleaseStream

EDIT2:

Tried to call CoMarshalInterThreadInterfaceInStream / CoGetInterfaceAndReleaseStream explicitly but still no results (same exception is thrown when trying to use markup services)

هل كانت مفيدة؟

المحلول

In order to pass IMarkupServices into worker thread, the interface need to be implemented and available in first place, and it should also be marshalable. You don't seem to have luck with the latter: the interface does not have a reference to a type library, or custom proxy/stub class. Neither it can be provided with automatic proxy/stub pair. Hence, no passing across apartment boundary available for it.

UPD. The whole point here is that if you can pass interface into another thread, the object itself will indicate the underlying marshaling mode (depends on apartment type, on object's IMarshal implementation availability), whether you can access the object directly from background thread, or this access is going to be serialized into original thread. In latter case marshaling into worker thread might leave UI thread responsive but the whole processing might be slower in total due to increased marshaling expense. In your case you are unable to marshal in first place.

Basically you are only left the option to work with it in original thread. In native domain you could also attempt to violate COM guidelines and pass the raw pointer into background thread and work with it from there. This is not guaranteed to work and is something I would not advise to do, but depending on the object itself it sometimes works out well. In managed code this trick is even less recommended (if possible at all).

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top