问题描述
我目前正在使用 MS HTML 将 JavaScript 代码插入网站.
I'm currently working with MS HTML to insert a JavaScript code into websites.
我参考了 Microsoft HTML 对象库并键入了此代码.
I made a reference to Microsoft HTML Object Library and types this code.
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);
我在脚本的最后一行收到错误,这是消息.
I get an error on the last line of the script, this is the message.
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 没有太多经验,在代码中保留最后一行很重要,谁能帮我理解这意味着什么以及我如何解决它?
I don't have much experience with COM and it is important to keep the last line int he code, can any one help me understand what this means and how i solve it ?
推荐答案
从 mshtml.tlb 这样的类型库中获得的互操作类存在一个非常严重的问题.类型库导入器从类型库中的 coclass 定义生成合成 .NET 类.您可以从它们的类型名称中识别它们,它们以类"结尾并以接口类型的名称开头.它们旨在提供帮助,允许您将 COM coclass 视为 .NET 类.
There is a very serious problem with the interop classes that you get out of a type library like mshtml.tlb. The type library importer generates synthetic .NET classes from the coclass definitions in the type library. You can recognize them from their type names, they end in "Class" and start with the name of an interface type. They are meant to be helpful, allowing you to treat the COM coclass as though it was a .NET class.
然而,它们会导致非常严重的版本控制问题.当我使用 oleview.exe 在我的机器上查看 mshtml.tlb 时,我看到了 HTMLHeadElement coclass 的这个定义:
They however cause a very serious versioning problem. When I use oleview.exe to look at mshtml.tlb on my machine then I see this definition for the 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 时,我会看到:
When I right-click mshtml.HtmlHeadElementClass in the source code and select Go To Definition then I see this:
public class HTMLHeadElementClass : DispHTMLHeadElement, HTMLHeadElement,
HTMLElementEvents_Event, IHTMLElement, IHTMLElement2, IHTMLElement3,
IHTMLElement4, IHTMLUniqueName, IHTMLDOMNode, IHTMLDOMNode2,
IHTMLHeadElement, IHTMLElementEvents2_Event {
// Lots of members
//...
}
注意两者之间的不匹配.我从 Oleview 得到的那个有 extra 接口,IHtmlElement5 和 IHtmlHeadElement2.原因很简单,我的机器上安装了IE9.互操作定义来自 mshtml PIA.这是一个旧版本,可能可以追溯到IE7.
Note the mismatch between the two. The one I got from Oleview has extra interfaces, IHtmlElement5 and IHtmlHeadElement2. The reason is simple to explain, I have IE9 installed on my machine. The interop definition came from the mshtml PIA. Which is an old version, probably dating back to IE7.
可怕的问题是新的 IHtmlElement5 接口被插入到继承列表中.这是一个非常严重的错误,至少就 .NET 代码而言.纯 COM 代码根本不重要,它只适用于接口,并通过 QueryInterface 向 coclass 请求接口指针.然而,对于 .NET 包装类来说,这是致命,所有通过 IHTMLDOMNode2 的方法都有错误的偏移量.所以如果你调用 IHTMLHeadElement.appendChild 方法,那么你最终会调用 wrong 方法.
The horrifying problem is that new IHtmlElement5 interface got inserted into the inheritance list. This was a very serious mistake, at least as far as .NET code goes. It doesn't matter at all to pure COM code, that only ever works with interfaces and asks the coclass for an interface pointer with QueryInterface. It is however fatal to the .NET wrapper class, all of the methods past IHTMLDOMNode2 have the wrong offset. So if you call the IHTMLHeadElement.appendChild method then you'll end up calling the wrong method.
这是一个无法解决的问题.您可以卸载 PIA 并生成您自己的 interop.mshtml.dll 互操作库,它将在您的机器上正常工作.但它在另一台没有安装 IE9 的机器上无法正常工作.
This is an unsolvable problem. You can uninstall the PIA and generate your own interop.mshtml.dll interop library, it will work correctly on your machine. But it won't work correctly on another machine that doesn't have IE9 installed.
有一个很好的解决方法,但是只是不要使用 XxxClass 类.仅使用接口类型.应该类似于这个(在 Winforms 应用程序中测试):
There's a good workaround available however, just don't use the XxxClass classes. Only use the interface types. Which ought to resemble this (tested in a Winforms app):
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 元素.
Where I intentionally used a cast to (IHTMLDOMNode) instead of (IHTMLHeadElement) because that lesser interface was already good enough to access the item element.
这篇关于MSHTML HTMLHeadElementClass COM 错误的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持跟版网!