技術情報

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のレンダリングモードで動いており、サイトを表示した際にレイアウトが崩れる場合があります。

こちらを参考にして設定してください。

InternetExplorerの解放処理