2012-04-17 20 views
1

我目前正在使用MS HTML將JavaScript代碼插入網站。MSHTML HTMLHeadElementClass COM錯誤

我提到了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太多的經驗,並保持最後一行詮釋他的代碼是很重要的,任何一個可以幫助我理解這意味着什麼,我該如何解決呢?

回答

6

從類型庫(如mshtml.tlb)中獲得的interop類存在非常嚴重的問題。類型庫導入器根據類型庫中的coclass定義生成合成.NET類。您可以從它們的類型名稱中識別出它們,它們以「類」結尾,並以接口類型的名稱開頭。它們意味着很有幫助,使您可以將COM coclass視爲.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並選擇轉到定義則我看到這個:

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接口得到插入到繼承列表。這是一個非常嚴重的錯誤,至少就.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),因爲較小的接口已經足夠好訪問項目元素。