处理混乱的界面层次结构 (MSHTML) 的最佳方法是什么?
我正在使用 MSHTML API从 C# 4.0 开始,运行代码的逻辑不是问题。然而,由于 MSHTML 和/或 COM 接口的设计方式,编写代码是一件痛苦的事情。具体来说,当应该有接口层次结构时,却没有接口层次结构。例如,IHTMLDocument7
不会扩展 IHTMLDocument6
,而 IHTMLDocument6
不会扩展 IHTMLDocument5
,依此类推(IHTMLDocument2
会扩展)不过,请扩展 IHTMLDocument
)。
更令人困惑的是,有一个 HTMLDocument
接口扩展了 DispHTMLDocument
(它具有 IHTMLDocument*
接口的所有方法)和 HTMLDocumentEvents_Event
(提供一些但不是全部事件)。更糟糕的是,HTMLDocumentClass
是一个组件类,它实现了上述所有接口以及一些接口,例如 IDocumentSelector
和 HTMLDocumentEvents4_Event
。
我真的很希望能够使用 HTMLDocumentClass
的 API,但尝试转换为它给了我:
System.InvalidCastException:无法 强制转换类型的 COM 对象 'mshtml.HTMLDocumentClass' 到类 输入“mshtml.HTMLDocumentClass”。 代表 COM 的类型的实例 组件不能转换为不同的 代表 COM 组件的类型; 但是它们可以转换为接口 只要底层的COM 组件支持 QueryInterface 调用接口的IID。
此外,某些接口没有关联的组件类;例如,有 IHTMLElement*
接口,但没有 HTMLElement
接口,也没有 HTMLElementClass
类。总的来说,我发现对接口进行编程很困难。
是否有好的技术可以解决这个界面问题,或者我应该放弃 IntelliSense 并在任何地方使用 dynamic
?我考虑编写实现所有接口的包装类,但是 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.InvalidCastException: 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.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
即使它扩展了 IHTMLDocument5,根据 COM 规则,您仍然应该使用 QueryInterface 来获取 IHTMLDocument5,而不是使用继承。我很高兴他们没有让您想知道如何对包装类已经实现的接口进行 QI 作为继承的副作用。
我建议您在控制对象时不要使用任何包装类并切换到向后兼容的接口。根据错误消息,为 IE 生成的 COM 包装器 CLR 看起来像是来自不同程序集的 mshtml.HTMLDocumentClass 类。
在 COM 编程中,您会经常看到工厂模式。对于html元素对象,工厂方法是IHTMLDocument2.createElement。如果作者选择使用此模式,通常您无法自行创建对象。
Visual Studio 将自动引用 PIA(如果存在),否则它使用 tlbexp.exe 生成前缀为“Interop”的互操作程序集。然而,大多数时候您会在 PIA 中使用少数接口,因此您可以编写自己的互操作类型(或从 Google 代码搜索复制)并使用这个大型程序集。
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.