C#でWin32API(Windows API)を使ってみる
はじめに
この記事では自分で作成したブラウザを
Win32 API(以下、Windows API)を使って操作してみる話を書きます。
主にWebBrowser コントロールから HtmlDocument および HtmlElement を取得する方法について書いています。
動作環境
- Windows10 Pro 21H2
- Visual Studio Code
- dotNET Core 6.0.101
前提知識
Windows APIとは
Microsoftが提供するWindowsで動作するAPI
またはWindows提供するアプリケーションインターフェイスのことを指します。
特に32bit 時代のWindowsではWin32 APIと呼ばれていましたが
現在のWindows OSは64bit が圧倒的に多いということもあり、現代では単にWindows APIと呼ばれます。
Windows APIはしばしば、いろんなプログラミング言語で呼び出されることが多いですが
Windowsの機能を呼び出すことがキモとなる為、Windows系の技術でよく利用されます。
もちろん、CやC++などWindows API を呼び出せる技術であれば、利用可能です。
今回は次に説明する WebBrowser コントロールをWindows APIでタッチしにいきます。
WebBrowser コントロール(WebBrowser クラス)
皆さんご存知のInternet Explorer(IE)に使われているブラウザコントロールまたはクラスです。
IEとVBAが連携するのも極端な話、コイツのおかげです。
とても便利ですが、今後廃止されるかもしれないクラスですので多用は禁物です。WebView2を使いましょう。
ちなみにMicrosoft Edge に搭載される IEモードはおそらく、このWebBrowser コントロールが担っています。
※なぜそうなるのか理由は後述します。
今回はWebBrowser にホストされている(内にある)HtmlDocument を操作します。
HtmlDocument
HtmlDocumentとはWebBrowser内にあるHTMLドキュメントを示すクラスのことです。
名前空間はSystem.Windows.Forms です。
※参考:Microsoftのドキュメント
なお、HtmlDocument は HtmlElementを取得するメソッドおよびプロパティを有しています。
Web系技術に詳しい人であれば、直観的に理解できると思います。
本題
準備
まずはこちらから ブラウザをダウンロードします。
解凍してデスクトップなどに配置します。
解凍ができましたら、フォルダ内のLegacyBrowser.exeをクリックします。
起動時表示されるページはあらかじめ設定しておいたホームページが表示されます。
※おそらく、大半の人はMSN がトップに出るかもしれません
次にgit cloneでリポジトリをクローンします。
git clone https://github.com/ymd65536/CSharpWin32.git
今回は特定のパッケージの利用が必須となる為、nuget で取得します。
cd CSharpWin32
dotnet add package HiNetCloud.Microsoft.mshtml --version 21.8.1.23
実行してみる
最後にdotnet run で実行します。
donet run
実行結果
プロセス名:LegacyBrowser
タイトル名:MSN Japan - ニュース, 天気, メール (Outlook, Hotmail), Bing検索, Skype
+ 0x00010800 - 戻る WindowsForms10.Button.app.0.33c0d9d_r3_ad1 戻る
+ 0x00010802 - 進む WindowsForms10.Button.app.0.33c0d9d_r3_ad1 進む
+ 0x00010804 - WindowsForms10.Edit.app.0.33c0d9d_r3_ad1
+ 0x00010806 - Go WindowsForms10.Button.app.0.33c0d9d_r3_ad1 Go
+ 0x00010808 - Home WindowsForms10.Button.app.0.33c0d9d_r3_ad1 Home
+ 0x000307E6 - WindowsForms10.Window.8.app.0.33c0d9d_r3_ad1
++ 0x000307EE - Shell Embedding
+++ 0x000207F8 - Shell DocObject View
++++ 0x00010818 - Internet Explorer_Server
+++ 0x00010818 - Internet Explorer_Server
++ 0x000207F8 - Shell DocObject View
+++ 0x00010818 - Internet Explorer_Server
++ 0x00010818 - Internet Explorer_Server
+ 0x000307EE - Shell Embedding
++ 0x000207F8 - Shell DocObject View
+++ 0x00010818 - Internet Explorer_Server
++ 0x00010818 - Internet Explorer_Server
+ 0x000207F8 - Shell DocObject View
++ 0x00010818 - Internet Explorer_Server
+ 0x00010818 - Internet Explorer_Server
+ 0x0001080A - WindowsForms10.Window.8.app.0.33c0d9d_r3_ad1
実行結果の説明
プロセス名:LegacyBrowser
タイトル名:MSN Japan - ニュース, 天気, メール (Outlook, Hotmail), Bing検索, Skype
プロセス名:今回のプログラムの名前
タイトル名:フォームののキャプション
- 0x00010800 - 戻る WindowsForms10.Button.app.0.33c0d9d_r3_ad1 戻る
上記の出力は以下のように並んでいます。
階層 ハンドル値 コントロールのキャプション クラス名 タイトル名
実行結果からわかること
Windows Fomrs の コントロールはクラス名がWindowsForms10.Button.app.0.33c0d9d_r3_ad1であり
Windows APIのEnumChildWindowsは左から右へ上からの下に探索を実行するということがわかります。
今回、一番興味深いところとしては
Microsoft Edge に搭載されたIEモードでスクレイピングする時に見かけるクラスである Internet Explorer_Server が作成したWebブラウザにもいたということです。
とするとIEモードおよびIEで動くVBAマクロもこのブラウザで動くのかと言うと動きます。
もちろん、従来のShell.Application でウィンドウを捉えてVBAを実行する方法も動きました。(検証済)
劣化版ですが、IEで自動化している人はこのブラウザを使えば、今までどおりまでとはいかずとも
自動化できると思います。
で、スクレイピングはどうやるの?
今回、LegacyBrowserのハンドルを取得する為に作成したProgram.csの56行目に以下のような記述があると思います。
// Console.WriteLine(ReHtml.getElementsByName("q").length);
この ReHtmlにはHtmlDocumentが格納されている為、ここでgetElementsByName を実行すると
タグを取得できるのではないかなと考えています。
検証しましたが、メソッドではうまいことデータが取れませんでした。
なお、innerHTML や innerText ではHtmlDocument 内にあるテキストを取得できました。
まとめ
今回の調査で判明したこと
- 自作のブラウザに対して
Shell.Applicationを使えば、dotNETによるWebオートメーションが見込めそう -
HtmlDocumentによる画面の取得はあんまり融通が効かなさそう- 特にJavaScript を実行するのが難しい
-
Windows APIは難しい
これでIEの代わりにゴリゴリと自動化できれば、いろんなところで活躍しそうです。
ただし、WebBrowser コントロールは将来的には廃止されるかもしれないので多用は禁物です。WebView2 を使いましょう。
所感
Windows API は難しいですが、できることが広がるので楽しいですね。
検証しているときが一番楽しかったです。
ただ、普通に生きていればここまでWindows API に携わる機会はないと思います。
そういうことも考えるとわりと面白い検証でした。