What is the best way to deal with kludgy interface hierarchies (MSHTML)?
I'm using the MSHTML API from C# 4.0 and the logistics of running code are not a problem. Writing the code, however, is a pain due to the way that MSHTML and/or COM interfaces are designed. Specifically, there is no interface hierarchy when there should be one. For example, IHTMLDocument7
does not extend IHTMLDocument6
, which doesn't extend IHTMLDocument5
, and so on (IHTMLDocument2
does extend IHTMLDocument
, though).
To further confuse matters there is an HTMLDocument
interface that extends DispHTMLDocument
(which has all of the methods of the IHTMLDocument*
interfaces) and HTMLDocumentEvents_Event
(which provides some, but not all, events). To add to the mess, HTMLDocumentClass
is a coclass that implements all of the aforementioned interfaces and then some, such as IDocumentSelector
and HTMLDocumentEvents4_Event
.
I'd really like to be able to work with the API of HTMLDocumentClass
, but trying to cast to it gave me:
System.InvalidCastEx开发者_开发技巧ception: Unable to cast COM object of type 'mshtml.HTMLDocumentClass' to class type 'mshtml.HTMLDocumentClass'. Instances of types that represent COM components cannot be cast to different types that represent COM components; however they can be cast to interfaces as long as the underlying COM component supports QueryInterface calls for the IID of the interface.
In addition, some of the interfaces don't have an associated coclass; e.g., there are IHTMLElement*
interfaces but no HTMLElement
interface nor a HTMLElementClass
class. Overall, I am finding it difficult to program to an interface.
Are there good techniques for wrangling with this interface train wreck, or should I give up IntelliSense and use dynamic
everywhere? I considered writing wrapper classes that implemented all of the interfaces, but there are so many MSHTML interfaces and each of them has a ton of members so a practical solution has to be automated.
IHTMLDocument6 doesn't extend IHTMLDocument5
Even if it extends IHTMLDocument5, per COM rules, you are still supposed to QueryInterface to get IHTMLDocument5, not to use inheritance. I am glad that they did not let you wonder how you can QI for an interface that is already implemented by the wrapper class as a side effect of inheritance.
I suggest you to not use any of the wrapper classes and switch to backward compatible interfaces when you control the objects. The COM wrapper CLR generated for IE looks like a mshtml.HTMLDocumentClass class from a different assembly, based on the error message.
In COM programming you would see the factory pattern quite often. For the html element object, the factory method is IHTMLDocument2.createElement. Usually you can not create the object on your own if the author choose to use this pattern.
Visual Studio would automatically reference the PIA if one exists, otherwise it uses tlbexp.exe to generate interop assembly prefixed with "Interop". However most of time you would be using a handful interfaces in the PIA, so you can write your own interop types (or copy from Google Code Search) and get ride of this big assembly.
精彩评论