1

私は現在、MS HTMLを使用して、JavaScriptコードをWebサイトに挿入しています。

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の経験があまりないので、コードの最後の行を保持することが重要です。これが何を意味し、どのように解決するかを理解するのに役立つ人はいますか?

4

1 に答える 1

7

mshtml.tlbのようなタイプライブラリから取得する相互運用機能クラスには非常に深刻な問題があります。タイプライブラリインポーターは、タイプライブラリのコクラス定義から合成.NETクラスを生成します。それらはタイプ名から認識できます。これらは「クラス」で終わり、インターフェースタイプの名前で始まります。これらは役立つことを目的としており、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を右クリックし、[定義に移動]を選択すると、次のように表示されます。

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

2つの間の不一致に注意してください。Oleviewから入手したものには、追加のインターフェイスIHtmlElement5とIHtmlHeadElement2があります。理由は簡単に説明できます。私のマシンにIE9がインストールされています。相互運用機能の定義は、mshtmlPIAから取得されました。これは古いバージョンで、おそらくIE7にまでさかのぼります。

恐ろしい問題は、新しいIHtmlElement5インターフェイスが継承リストに挿入されたことです。これは、少なくとも.NETコードに関する限り、非常に重大な間違いでした。純粋なCOMコードにはまったく関係ありません。これは、インターフェイスでのみ機能し、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);

(IHTMLHeadElement)の代わりに(IHTMLDOMNode)へのキャストを意図的に使用した場合、その下位のインターフェイスはすでにitem要素にアクセスするのに十分でした。

于 2012-04-17T17:26:37.890 に答える