技術情報
- COM版と.NETのWebBrowserについて
- InternetExplorer8のプロセスモデルについて
- InternetExplorerを操作するプログラムの配布について
- InternetExplorerを操作するプログラムの配布について2
- InternetExplorer操作プログラムは、OSのbit数やIEのバージョンで動作が異なる
- InternetExplorer操作プログラムは、OSのbit数やIEのバージョンで動作が異なる(その2)
- objIE.Document.ParentWindowへのアクセスでキャストエラーがでる現象
- WebBrowserコントロールのレンダリングモードを最新に変更する
- InternetExplorerの解放処理
COM版と.NETのWebBrowserについて
ExcelVBAやVB6やVisualStudio2003(.NET Framework1.1)までのWebBrowserコントロールは、COM版(ActiveX)のWebBrowserです。
VisualStudio2005(.NET Framework2.0)以降のWebBrowserコントロールは、COM版をラッパーしたマネージドのWebBrowserです。
.NET版WebBrowserは内部でCOM版WebBrowserを呼び出しているだけで、実態としては同じものを使っています。
ただ、.NET版WebBrowserはCOM版より用意されているメソッドが少なく、通常の方法ではCOM版の全メソッド・プロパティにはアクセスできません。
代用できるメソッドを使ったり、DomDocument/DomElementでアンマネージの処理を行う方法があります。
参考
WebBrowser クラス(.NET版)
WebBrowserコントロールの概要
WebBrowserコントロール(COM版)
IE8のプロセスモデルについて
InternetExplorerを操作するプログラムの配布について
VisualStudioでInternetExplorerを操作するアプリケーションを作成する場合、「Microsoft Internet Controls (SHDocVw.DLL)」と「Microsoft HTML Object Library (MSHTML.DLL)」を参照設定していますが、
プログラムを動作させるPC環境や、.NET Frameworkのバージョンによって配布するファイルが異なります。
もしexeの実行時に以下の様なエラーで起動できない場合、必要なファイルが不足しています。
System.IO.FileNotFoundException: ファイルまたはアセンブリ 'Microsoft.mshtml, Version=7.0.3300.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'、またはその依存関係の 1 つが読み込めませんでした。指定されたファイルが見つかりません。
.NET Frameworkが4.0より前のバージョンの場合
動作させるPCに「Microsoft.mshtml.dll」が存在するかどうかで配布ファイルが異なります。
PCに.NETの開発環境(VisualStudio、.NET SDK)やOfficeがインストールされていれば「Microsoft.mshtml.dll」
もインストールされていますので、exeファイルのみの配布で問題ありません。
もしインストールしていない場合は、exeファイルの他に「Microsoft.mshtml.dll(Interop.MSHTML.dll)」と「Interop.SHDocVw.dll」も一緒に配布する必要があります。
.NET Frameworkが4.0以降のバージョンの場合
VisualStudioにて、参照している各DLLのプロパティ「相互運用機能型の埋め込み」がTrue(デフォルト)の設定でビルドしていれば、 exeのみの配布で問題ありません。
その他
・.NET Frameworkが4.0より前のバージョンでも、Type.InvokeMemberメソッドを利用して動的に参照するプログラムにしておけば、DLLの配布は不要です。
参考
http://blog.rocaz.net/2004/07/76.html
http://d.hatena.ne.jp/wave1008/20080813
http://d.hatena.ne.jp/mikihiro/20110717/p1
http://bbs.wankuma.com/index.cgi?mode=al2&namber=21847&KLOG=42
https://msdn.microsoft.com/ja-jp/library/ee317478(v=vs.110).aspx
InternetExplorerを操作するプログラムの配布について2
なぜ配布するdllに違いがあるかのmemoです。
IE操作で使用する「Microsoft Internet Controls」と「Microsoft HTML Object Library」はアンマネージコードのCOMなので、
.NET Frameworkのマネージコードのアプリケーションから使用する為には、「相互運用機能アセンブリ(IA)」が必要になります。
PCに.NETの開発環境(VisualStudio、.NET SDK)やOfficeがインストールされていれば、GACにこれらDLLの「プライマリ相互運用機能アセンブリ(PIA)」
が配置されます。
(PIAは、COMの発行者によって署名されているIA)
よって、GACにこれらDLLのPIAが存在する場合はexeのみの配布でよく、存在しない場合は、各IAのdllもexeと同封する必要があります。
(IAのdll(Interop.XX.dll)は、VisualStudioでビルドした際に、exeと同フォルダに作成される。)
.NET Framework4以降のVisualStudioでは、参照している各DLLのプロパティ「相互運用機能型の埋め込み」がTrue(デフォルト)の設定でビルドしていれば、
アプリケーション自身に参照するCOMの型情報が埋め込まれるため、PIAの配置は不要で、exeのみで動作します。
参考
https://msdn.microsoft.com/ja-jp/library/dd297671.aspx
https://msdn.microsoft.com/ja-jp/library/aa302338.aspx
https://msdn.microsoft.com/ja-jp/library/ee317478(v=vs.110).aspx
InternetExplorer操作プログラムは、OSのbit数やInternetExplorerのバージョンで動作が異なる
IEを操作するプログラムを作成する時に注意したいのが、OSのbit数やIEのバージョンによる動作の違いです。
どのバージョンだと何が変わるかは明確には調べていませんが、IE8からIE9にバージョンを上げた際は色々と問題がでました。
IE8では正常にエレメント操作が行われるコードでも、IE9だと例外が発生したり動作しなかったりしました。
特に困ったのが 「NotSupportedException: HRESULT からの例外: 0x800A01B6」の例外で、
サポートされていない例外なので情報が得られず、対処に困りました。
結論としては、型をキャストする事で回避出来ました。
IE8で「objIE.Document.XXX」のように直接プロパティやメソッドのアクセスを記載していたものを、
「DirectCast(objIE.Document, mshtml.HTMLDocument).XXX」に変更します。
どの型にキャストすればよいかは、デバッグ実行時のクイックウォッチで簡単に調べられます。
もしお客様にアプリケーションを提供する場合は、お客様の使用環境を確認し、同じ環境でテストしましょう。
InternetExplorer操作プログラムは、OSのbit数やInternetExplorerのバージョンで動作が異なる(その2)
前回、DirectCastにて該当の型に変換すれば解決できると書いていますが、速度が遅くなる問題があります。
例えば以下のコードは全リンクをループしてhrefを表示するものですが、
IE8の記述では数秒で終わっていたものが、IE9でDirectCastを用いると数十秒かかったりします。
Dim doc As mshtml.HTMLDocument = DirectCast(_objIE.Document, mshtml.HTMLDocument) Dim elementIE9 As mshtml.IHTMLElement For loopLink As Integer = 0 To doc.links.length - 1 'IE8 'Debug.WriteLine(_objIEControl.IE.Document.links(loopLink).href) 'IE9 elementIE9 = DirectCast(doc.links(loopLink), mshtml.IHTMLElement) Debug.WriteLine(DirectCast(elementIE9, mshtml.HTMLAnchorElementClass).href) Next
この場合、DirectCastで時間がかかっているので、代わりにType.InvokeMemberメソッドを使用します。
プログラム的には見難くなりますが、速度低下は抑えられます。
'Imports System.Reflection Dim typ As Type = Type.GetTypeFromProgID("mhtmlfile") Dim ieDoc As mshtml.HTMLDocument = typ.InvokeMember("document", BindingFlags.GetProperty, Nothing, _objIE, Nothing) For loopLink As Integer = 0 To CInt(typ.InvokeMember("length", BindingFlags.GetProperty, Nothing, typ.InvokeMember("links", BindingFlags.GetProperty, Nothing, ieDoc, Nothing), Nothing)) - 1 Debug.WriteLine(CStr(typ.InvokeMember("href", BindingFlags.GetProperty, Nothing, typ.InvokeMember("links", BindingFlags.GetProperty, Nothing, ieDoc, Nothing)(loopLink), Nothing))) Next
参考
http://d.hatena.ne.jp/mikihiro/20110719/p1
http://hpcgi3.nifty.com/midori_no_bike/bbs/wforum.cgi?mode=allread&no=247&page=0
objIE.Document.ParentWindowへのアクセスでキャストエラーがでる現象
プログラムからのInternetExplorer操作で、スレッドにより複数タブの制御を行っているのですが、
「objIE.Document.ParentWindow」へのアクセス部分でキャストの例外(InvalidCastException)が発生しました。
調べてみると、スレッドでSTAではなくMTAでアクセスした場合に、本現象が発生するようです。
現状の実装がBackgroundWorkerを使っているため、MTAとなっていました。
以下コードで検証し、MTAだとダメでSTAだとOKになるのを確認できました。
参考1
参考2
Public Class Form1 'スレッド未使用で正常 Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click 'サイト表示 Dim objIE = OpenYahoo() '画面スクロール objIE.Document.Parentwindow.ScrollTo(100, 100) End Sub 'スレッド使用(MTA)でエラーが再現 Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click 'サイト表示 Dim objIE = OpenYahoo() 'MTAスレッドで実行 Dim threadWorker As New BackgroundWorker AddHandler threadWorker.DoWork, AddressOf MTAthreadWorker_DoWork threadWorker.RunWorkerAsync(objIE) End Sub 'スレッド使用(STA)で正常 Private Sub Button3_Click(sender As Object, e As EventArgs) Handles Button3.Click 'サイト表示 Dim objIE = OpenYahoo() 'STAスレッドで実行 Dim staThread As New Thread(New ParameterizedThreadStart(AddressOf STAthreadWorker_DoWork)) staThread.SetApartmentState(ApartmentState.STA) staThread.Start(objIE) End Sub Private Function OpenYahoo() As SHDocVw.InternetExplorer Dim objIE As SHDocVw.InternetExplorer objIE = CreateObject("InternetExplorer.Application") objIE.Navigate("http://www.yahoo.co.jp") objIE.Width = 200 objIE.Height = 200 objIE.Visible = True While objIE.Busy = True OrElse objIE.ReadyState <> SHDocVw.tagREADYSTATE.READYSTATE_COMPLETE System.Threading.Thread.Sleep(100) Application.DoEvents() End While Return objIE End Function Private Sub MTAthreadWorker_DoWork(ByVal sender As Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Dim objIE As SHDocVw.InternetExplorer = DirectCast(e.Argument, SHDocVw.InternetExplorer) '画面スクロール objIE.Document.Parentwindow.ScrollTo(300, 100) 'InvalidCastException発生 End Sub Private Sub STAthreadWorker_DoWork(ByVal obj As Object) Dim objIE As SHDocVw.InternetExplorer = DirectCast(obj, SHDocVw.InternetExplorer) '画面スクロール objIE.Document.Parentwindow.ScrollTo(300, 100) End Sub End Class
WebBrowserコントロールのレンダリングモードを最新に変更する
VisualStudioのWebBrowserコントロールですが、InternetExplorer11がインストールされていても、
デフォルトではIE7のレンダリングモードで動いており、サイトを表示した際にレイアウトが崩れる場合があります。
こちらを参考にして設定してください。