MSHTML HTMLHeadElementClass COM错误

我目前正在使用MS HTML将JavaScript代码插入到网站中。

我引用了Microsoft HTML Object Library并输入了这段代码。

IHTMLDocument2 doc = BrowserHost.Document as HTMLDocumentClass; IHTMLElement head = (IHTMLElement) ((IHTMLElementCollection)doc.all.tags("head")).item(null, 0); IHTMLScriptElement scriptObject = (IHTMLScriptElement)doc.createElement("script"); scriptObject.type = @"text/javascript"; scriptObject.text = TTS.TTSWebFactory.GetJavascript(); ((HTMLHeadElementClass)head).appendChild((IHTMLDOMNode)scriptObject); 

我在脚本的最后一行收到错误,这是消息。

 Unable to cast COM object of type 'System._ComObject' to class type 'mshtml.HTMLHeadElementClass'. COM components that enter the CLR and do not support IProvideClassInfo or that do not havae any iterop assembly registered will be wrapped in the _ComObject type. Instances of this type cannot be cast to any other class; however they can be cast to interfaces as long as the underlying COM component supports QueryInterface calls for the IID of the interface 

我没有太多的COM经验,重要的是保持代码中的最后一行,任何人都可以帮助我理解这意味着什么以及我如何解决它?

你可以从像mshtml.tlb这样的类型库中获得一个非常严重的interop类问题。 类型库导入器从类型库中的coclass定义生成合成.NET类。 您可以从类型名称中识别它们,它们以“Class”结尾,并以接口类型的名称开头。 它们是有用的,允许您将COM coclass视为.NET类。

然而,它们会导致严重的版本控制问题。 当我使用oleview.exe在我的机器上查看mshtml.tlb时,我看到了HTMLHeadElement coclass的这个定义:

 [ uuid(3050F493-98B5-11CF-BB82-00AA00BDCE0B), noncreatable ] coclass HTMLHeadElement { [default] dispinterface DispHTMLHeadElement; [default, source] dispinterface HTMLElementEvents; [source] dispinterface HTMLElementEvents2; interface IHTMLElement; interface IHTMLElement2; interface IHTMLElement3; interface IHTMLElement4; interface IHTMLUniqueName; interface IHTMLDOMNode; interface IHTMLDOMNode2; interface IHTMLElement5; interface IHTMLDOMConstructor; interface IHTMLHeadElement; interface IHTMLHeadElement2; }; 

当我右键单击源代码中的mshtml.HtmlHeadElementClass并选择Go To Definition时,我看到:

 public class HTMLHeadElementClass : DispHTMLHeadElement, HTMLHeadElement, HTMLElementEvents_Event, IHTMLElement, IHTMLElement2, IHTMLElement3, IHTMLElement4, IHTMLUniqueName, IHTMLDOMNode, IHTMLDOMNode2, IHTMLHeadElement, IHTMLElementEvents2_Event { // Lots of members //... } 

注意两者之间的不匹配。 我从Oleview得到的那个有额外的接口,IHtmlElement5和IHtmlHeadElement2。 原因很简单,我在我的机器上安装了IE9。 互操作定义来自mshtml PIA。 这是版本,可能追溯到IE7。

可怕的问题是新的IHtmlElement5接口被插入到inheritance列表中。 这是一个非常严重的错误,至少就.NET代码而言。 纯粹的COM代码完全没有关系,只能使用接口并向coclass请求带有QueryInterface的接口指针。 然而,对于.NET包装类来说,所有过去IHTMLDOMNode2的方法都有错误的偏移量。 因此,如果您调用IHTMLHeadElement.appendChild方法,那么您最终将调用错误的方法。

这是一个无法解决的问题。 您可以卸载PIA并生成自己的interop.mshtml.dll互操作库,它将在您的计算机上正常运行。 但它不能在没有安装IE9的另一台机器上正常工作。

但是,有一个很好的解决方法, 只是不要使用XxxClass类 。 仅使用接口类型。 哪个应该类似于此(在Winforms应用程序中测试):

  var doc = (IHTMLDocument2)webBrowser1.Document.DomDocument; var headItems = (IHTMLElementCollection)doc.all.tags("head"); var scriptObject = (IHTMLScriptElement)doc.createElement("script"); scriptObject.type = @"text/javascript"; //scriptObject.text = TTS.TTSWebFactory.GetJavascript(); var node = (IHTMLDOMNode)headItems.item(null, 0); node.appendChild((IHTMLDOMNode)scriptObject); 

我故意使用强制转换为(IHTMLDOMNode)而不是(IHTMLHeadElement),因为较小的接口已经足以访问item元素了。