C# MSHTML HTMLHeadElementClass COM错误

C# MSHTML HTMLHeadElementClass COM错误,c#,com,mshtml,comobject,C#,Com,Mshtml,Comobject,我目前正在使用MSHTML将JavaScript代码插入网站 我参考了MicrosoftHTML对象库并键入了这段代码 IHTMLDocument2 doc = BrowserHost.Document as HTMLDocumentClass; IHTMLElement head = (IHTMLElement) ((IHTMLElementCollection)doc.all.tags("head")).item(null, 0); IHTMLScriptElement scri

我目前正在使用MSHTML将JavaScript代码插入网站

我参考了MicrosoftHTML对象库并键入了这段代码

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这样的类型库中获取的互操作类存在一个非常严重的问题。类型库导入器从类型库中的coclass定义生成合成.NET类。您可以从类型名称中识别它们,它们以“Class”结尾,并以接口类型的名称开头。它们是有帮助的,允许您将COM类当作.NET类来对待

但是,它们会导致非常严重的版本控制问题。当我使用oleview.exe查看机器上的mshtml.tlb时,我看到了HTMLHeadElement类的以下定义:

[
  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。互操作定义来自MSHTMLPIA。这是一个旧版本,可能可以追溯到IE7

可怕的问题是新的IHtmlElement5接口被插入到继承列表中。至少就.NET代码而言,这是一个非常严重的错误。对于纯COM代码来说,这一点都不重要,因为纯COM代码只与接口一起工作,并向coclass请求带有QueryInterface的接口指针。然而,对于.NET包装类来说,这是致命的,所有超过IHTMLDOMNode2的方法都有错误的偏移量。因此,如果调用IHTMLHeadElement.appendChild方法,那么最终将调用错误的方法

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

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


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

从类型库(如mshtml.tlb)中获得的互操作类存在非常严重的问题。类型库导入器从类型库中的coclass定义生成合成.NET类。您可以从类型名称中识别它们,它们以“Class”结尾,并以接口类型的名称开头。它们是有帮助的,允许您将COM类当作.NET类来对待

但是,它们会导致非常严重的版本控制问题。当我使用oleview.exe查看机器上的mshtml.tlb时,我看到了HTMLHeadElement类的以下定义:

[
  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。互操作定义来自MSHTMLPIA。这是一个旧版本,可能可以追溯到IE7

可怕的问题是新的IHtmlElement5接口被插入到继承列表中。至少就.NET代码而言,这是一个非常严重的错误。对于纯COM代码来说,这一点都不重要,因为纯COM代码只与接口一起工作,并向coclass请求带有QueryInterface的接口指针。然而,对于.NET包装类来说,这是致命的,所有超过IHTMLDOMNode2的方法都有错误的偏移量。因此,如果调用IHTMLHeadElement.appendChild方法,那么最终将调用错误的方法

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

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

在这里,我故意使用cast to(IHTMLDOMNode)而不是(IHTMLHeadElement),因为较小的接口已经足够好,可以访问item元素