技術情報

COM版と.NETのWebBrowserについて

VB6やVisualStudio2003(.NET Framework1.1)までのバージョンのWebBrowserコントロールは、COM版のWebBrowserです。
VisualStudio2005(.NET Framework2.0)以降のWebBrowserコントロールは、COM版ではなく、.NET版のWebBrowserです。

COM版と.NET版の違いですが、.NET版WebBrowserは内部でCOM版WebBrowserを呼び出しているだけで、同じものを使っています。
ただ、.NET版WebBrowserは一部のメソッドしか用意されていないため、通常の方法ではCOM版の全メソッド・プロパティにはアクセスできません。

参考

WebBrowser コントロールの概要


IE8のプロセスモデルについて

IE8のプロセスモデルについて

IEを操作するプログラムの配布について

VisualStudioでIEを操作するアプリケーションを作成する場合、「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

IEを操作するプログラムの配布について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

IE操作プログラムは、OSのbit数やIEのバージョンで動作が異なる

IEを操作するプログラムを作成する時に注意したいのが、OSのbit数やIEのバージョンによる動作の違いです。

どのバージョンだと何が変わるかは明確には調べていませんが、IE8からIE9にバージョンを上げた際は色々と問題がでました。
IE8では正常にエレメント操作が行われるコードでも、IE9だと例外が発生したり動作しなかったりしました。

特に困ったのが 「NotSupportedException: HRESULT からの例外: 0x800A01B6」の例外で、 サポートされていない例外なので情報が得られず、対処に困りました。
結論としては、型をキャストする事で回避出来ました。

IE8で「objIE.Document.XXX」のように直接プロパティやメソッドのアクセスを記載していたものを、 「DirectCast(objIE.Document, mshtml.HTMLDocument).XXX」に変更します。
どの型にキャストすればよいかは、デバッグ実行時のクイックウォッチで簡単に調べられます。

もしお客様にアプリケーションを提供する場合は、お客様の使用環境を確認し、同じ環境でテストしましょう。

IE操作プログラムは、OSのbit数やIEのバージョンで動作が異なる(その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へのアクセスでキャストエラーがでる現象

プログラムからのIE操作で、スレッドにより複数タブの制御を行っているのですが、 「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のレンダリングモードで動いており、サイトを表示した際にレイアウトが崩れる場合があります。

試しにWebBrowserコントロール上でUserAgentを表示してみると、IE7の情報が表示されます。

IE7より上のバージョンとして動作させるには、レジストリを変更する必要があります。
詳しくは以下の参考サイトを参照ください。

参考
WebBrowserコントロールのレンダリングモードをデフォルトのIE7から最新IEモードに変更する
WebBrowser コントロールで使われている Internet Explorerを最新のバージョンに変更する

IEの解放処理

参考
http://hanatyan.sakura.ne.jp/vbnetbbs/wforum.cgi?mode=allread&no=10181
http://www.geocities.co.jp/NatureLand/2023/reference/Web/web02e.html
http://nky-tech-sdt.blogspot.jp/2011/07/dynamiccom.html
http://hanatyan.sakura.ne.jp/yybbs/read.cgi?no=190