MSHTML HTMLHeadElementClass COM错误

Pat*_*ick 1 c# com mshtml comobject

我目前正在使用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);
Run Code Online (Sandbox Code Playgroud)

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

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
Run Code Online (Sandbox Code Playgroud)

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

Han*_*ant 6

你从类似于mshtml.tlb的类库中获得的互操作类有一个非常严重的问题.类型库导入器从类型库中的coclass定义生成合成.NET类.您可以从类型名称中识别它们,它们以"类"结尾,并以接口类型的名称开头.它们是有用的,允许您将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;
};
Run Code Online (Sandbox Code Playgroud)

当我右键单击源代码中的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
   //...
}
Run Code Online (Sandbox Code Playgroud)

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

可怕的问题是新的IHtmlElement5接口被插入到继承列表中.这是一个非常严重的错误,至少就.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);
Run Code Online (Sandbox Code Playgroud)

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