VBAで他のアプリケーションを操作する

RPAツール関係のお話が2017年からぐわっと盛り上がっていますが、実際の所このRPAと呼ばれるツール群やその話題は昔からあったもので、いわばペンキ塗り替えでRPAと称して再登場してるものです。代表的なものはExcelのマクロやVBA、UWSCといった自動化ツール、またウェブ操作系のデバッグツールとしては、Seleniumなどなど。

今までは事務方が現場で自分のために部分最適化の一例として細々と実践してたものなのです。ASPSaaSと名を変えてリバイバルしたようなものです。ちょっと前まで「Excelマクロで楽するなんてずるい」とか言ってた時代だったのに、変わったものです。さて、そんなRPAですが、Excel VBAでどこまで行けるのか?まとめてみました。

※ここに記載している他にもマウスの座標をコントロールしてボタンをクリックさせる手法や、VBAでは出来ない事をVBSやWSHなどの別のスクリプトにやらせてVBAはそれを呼び出す事に徹する方法、スクレイピングしてデータを取得する方法など様々な遠隔操作手法があります。

今回使用するファイル

※今回のサンプルはSeleniumBasicは参照設定ではなく、実行時バインディングでコードを記述してあります。

新方式が登場しました

IE11の廃止に伴い、SeleniumやNode.jsやらといった手段を使わず、またPuppeteerと同様の手法(CDPを叩く)でVBAとEdge/ChromeのみでOAuth2.0認証する手段が登場しました。スクレイピングも可能になっています。以下のエントリーを参考にしてみてください。この手法は最も制限が無く、もっともすぐれた選択肢になると思います。

VBAでOAuth2.0認証 - 新方式を試してみた

Excel VBAで他のアプリを操作する

SendKeysを使った手法

問題点と解消法

最もポピュラーでこれまでもよく利用されていた手法です。キーボードの操作をプログラムのコードで送りつけて、アプリケーションを操作します。ショートカットキーなども利用出来るのですが、いくつかの問題点があって、結構癖があり扱いにくいです。問題点は

  1. コピペなどの場合、マクロ側でコピーをしたものでないとコピーがうまく動かない
  2. 一方的にキーを無造作に送りつけるので、起動が遅いとか処理が間に合わなくても送ってしまう
  3. 実行中に下手に操作すると、アクティブウィンドウが変わって、そちらにキーを送ってしまう
  4. 印刷ダイアログなどではショートカットキーが効かない
  5. ESCキーを送りたい場合、マクロが途中停止する事がある。
  6. NumLockが外れることがある

コピペはCtrl+C, Ctrl+Vだとうまく動かない時には、VBAでクリップボードにコピーする関数を間に挟んで処理をする。日本語の文字化け処理に対してもこの方法は有効です。

処理が間に合わない場合にはスリープを入れてあげる。Win32 APIを使うので冒頭で宣言が必要です。

印刷ダイアログ等では、ショートカットキーではなく、TABキーやスペースキー(" ")、Enterキーなどを組み合わせて送ってあげます。例えば、TABを6回、スペースを1回ならば

ESCキーはマクロの停止と被るので、これをまずは受け付けないようにブロックしておく

NumLockがオフになったりする現象がバグとして公式で報告されています。これが鬱陶しいのでこの場合には、NumLockをSendkeysで送ってしまうか?こちらのサイトで紹介されてるような、NumLockの状態を取得して必ずオンになるようにするコードを追加してみる。

基本、SendKeysを使うようなVBAの場合、実行中は操作してはいけません。

サンプルコード

今回は、「irfanviewを起動し、特定フォルダ内の画像を全てBMP形式で特定のフォルダに変換して保存する」といった処理をSendKeysで作ってみました。sleepをさせるコードと特殊フォルダのパスを取得する関数を別に用意してあります。

※SHGetFoldPathは特殊なフォルダパスを取得する為のルーチンです。

※StopTimeはミリセカンド単位でsleepをさせる為のルーチンです。

※事前にデスクトップにsaveとkinokoというフォルダを作っておき、kinokoフォルダに画像類を入れておく必要があります。

実行してみた

sendkeysでirfanviewを操作する

WSHのSendKeysを使った手法

概要

VBAのSendKeysメソッドはバグがあるということで、避けている人が代替としてよく利用しているのがこの手法。同じSendKeysなのですが、Windows Scripting Hostと呼ばれるスクリプト実行環境版です。これをVBAから利用します。参照設定より、「Microsoft Scripting Runtime」を追加すればOKですが、一時的に使用したいような場合には、CreateObjectで呼び出すのも良いでしょう。

※但し、VBAではWindows Scripting HostのSleepは使えません。また、書き方は殆ど同じですが、()で括る点やRunでアプリを起動させる時にProgram Filesのような半角スペースを含むものは、パスの渡し方がちょっと独特です。

サンプルコード

SendInputを使った手法

概要

SendKeysを送る手法のバグ対策の為に、WSHを使った手法を使いました。それ以外にもWindows APIを使ったSendInput関数を使ったキーの送信方法があります。但しこの手法は素のやり方でコードを書くと非常に煩雑になるので、こちらのサイトを参考にして、キー送信部分を関数化しておくと、扱い易くなります。

素の状態でコードを組んだ時、SendInputの場合はキーを押す&キーを離すといった動作まで細かく定義をしなければなりません。簡単にコピペとした場合でも、Ctrlキーを押す⇒Cキーを押す⇒Ctrlキーを離す⇒Cキーを離す⇒(何かの移動処理)⇒Ctrlキーを押す⇒Vキーを押す⇒Ctrlキーを離す⇒Vキーを離すといった処理を事細かに書かなければなりません。

※キーをいちいち作成するわけなのですが、例えばCtrlキーを押してそのままにしておくと、ずっとOSレベルで押しっぱなしの状態になるのでややこしいことになります。キーを作る時には注意が必要です。必ずCtrlキーをアップして開放してあげなければなりません。

※Altキーなどに秀Capsなどのキーボードショートカットコマンドを割り当てていると、反応してうまく動作しない事があります。常駐させている場合には注意が必要です。

サンプルコード

今回のサンプルコードはこちらのサイトの関数化されたプロシージャを利用しています。また、今回使用する分のキーコード定数だけを宣言しているので、他のキーコードを使う場合には追加で宣言に足してあげる必要があります。冒頭の宣言関係やキーコード定数は省略して記載しています。

また、特殊フォルダのパスの取得やirfanviewの起動に関してはWindows Scripting Hostを利用しています。clipman関数はクリップボードにデータを保存するための自作の関数です。

解説

SendInputは冒頭ジェネラルプロシージャ内で、別途宣言が必要になります。WindowsのAPIであってVBAの機能ではないので、必ずこれは必要になります。以下のような宣言文です。

今回利用させていただいたSendInputを簡略化して使える関数を利用すると、非常に楽にキーストロークの組み合わせを作って送信する事ができます。ひとつ注意したい点は、ALT(Menu)キーやCtrlキーについては、キーダウンとアップのキーコード定数が異なります。間違えるとOSレベルで押されっぱなしになり、再起動するまで解除されません。VK_CONTROL_DOWNの後には必ず、VK_CONTROL_UPでキーを送りましょう。

また、キーボード以外にもマウス操作も可能ですが今回は使用しませんでした。

プロシージャ内で送り込みたい文字列などはクリップボード関数でまずコピーしておき、Ctrl+Vを送るようなコマンドを送ると良いでしょう。今回も開くフォルダ、保存先フォルダの2つで利用しています。

コマンドラインで操作する手法

概要

これまでの操作は、人間がマウスやキーボードを操作する流れを、忠実にキーコードを送信して実現し、アプリケーションを操作する手法でした。最近はそれほど多くなくなりましたが、かつてのプログラムはGUIと同時にコマンドラインでも操作できるように実装されてるものがありました。irfanviewもそのうちの1つです(今はAPIのほうが主流ですね)。

IrfanViewのコマンドライン・オプション一覧を見て、コマンドプロンプトで入力すればCUIで同じ操作が可能になります。実際に不確実な面がどうしても拭えないキーコード送信よりも、100%確実に結果が期待できるコマンドラインのほうが結果的には面倒が少ないとも言えます。VBAからはWindows Scripting Hostを使ってコマンドプロンプトへコマンドラインを送り込んで実行可能です。これまでと同じ操作のコマンドラインを組んでみました。

*.*で対象フォルダ内にある全てのファイルが対象になり、$N.jpgで同じファイル名で拡張子がbmpのファイルに、/convertオプションでバッチ変換をする命令文です。

サンプルコード

※wshshell.execでコマンドラインを実行しているのですが、"%ComSpec% /c "はおまじないみたいなものです。comstrで組み立てたコマンドラインで、非常に高速に処理が完了します。

UI Automationを利用して操作する

最近巷で話題になっているPower Automate for Desktopなどでも利用されている、デスクトップアプリケーションをUI Automationを利用して操作する手法をVBAで行うテクニックです。

デスクトップのアプリにもSeleniumで操作する時のように、一個一個のコンポーネントには個別のIDのようなものが振られており、これらを利用してSendKeyを使わずにダイレクトに操作するのがUI Automationです。いずれ、詳しい構築の仕方を別エントリーで紹介したいと思います。

以下は簡単に電卓を起動して計算させる64bit版コードです。別途参照設定にて、UIAutomationClientを追加して利用する事になります。

  • 電卓の起動およびウィンドウハンドルの取得時にスリープを入れていないと、掴む前にcalcmanが実行されてしまうので、必須の処理です
  • calcmanでは送られてくるコマンドのタイプ毎に詳細なコマンドを構築し直し、最後に電卓側にUI Automationにて送り込んでいます。
  • これらのUI要素の解析には、UIAutomation SpyもしくはInspectといったツールで解析します。

図:2005年から実装された結構歴史のあるAPI

SeleniumBasicでウェブアプリを操作する

Seleniumとは、ウェブアプリケーションのデバッグテストなどで使用してる、自動テストを行わせる為のツールです。ブラウザを操作してくれるので、これそのものでもRPA的な使い方も可能です。しかし、VBAからブラウザ操作はあまりにも面倒な上に、昔のIEの操作に関するものが殆どで、現代的なものではありません(今更IEを使う事もないですし、Edgeを使う気にはなれないですし)。

そこで使用するのが、Selenium Basic(旧Selenium VBA)です。Chromeなどのブラウザも操作可能になりますが、その代理をしてくれるのが、SeleniumBasicです。管理者権限無しでインストールや、WebDriverの自動更新などについては、以下のエントリーを参考にしてみてください。

VBAでOAuth2.0認証 - Windows11対応版

セットアップ

SeleniumBasicのセットアップは簡単。以下の手順でセットアップをします。

  1. 配布元に行き、Release Pageへ入る
  2. 最新版をクリックしてダウンロード
  3. ダウンロードされたインストーラを起動して適当に進める。
  4. 途中、Web Driverのインストールに関する項目がでるけれど全てそのままインストール
  5. 完了したらインストール自体は完了。

ただし、このWeb Driverが最新とは限らないので、以下の手順で最新版にすげ替える。今回はChromeで操作を考える。

  1. Chrome Driverの配布元に行き、Downloadページにゆく
  2. 最新版をダウンロード
  3. 解凍すると、exeが1個入ってる。
  4. Explorerを起動し、パスの部分に「%LOCALAPPDATA%\SeleniumBasic」を入れて移動する
  5. 3.で解凍されたexeをそのフォルダに放り込み上書きする

これで完了です。

図:インストール自体はとても簡単

図:ブラウザとドライバのバージョンが違うとエラーが出る

実際に操作してみる

使用するには事前に「Selenium Type Library」を参照設定で入れておく必要性があります。インストールした後であれば参照設定のリストに出てくるはずです。チェックを入れておきましょう。コードの中で事前バインディングではなく、実行時バインディングで使う場合には、以下のようなコードになります。今回のサンプルは、実行時バインディングで記述してあります。

図:事前バインディングの場合参照設定をする

早速コードを書いてみます。今回は、Googleのページで「Excel 自動化」で検索をした結果を、スクリーンショットで取得して、Excelに貼り付けるといった一連のフローを書いてみます。

古いSelenium VBA時代のコードとかなり変わっているので、入力補完などを使ってメソッドは組み立てる必要があります。しっかりした作りのウェブアプリケーションであれば、ほぼログインからデータの取得まではこなせるだけでなく、ウェイトなどを駆使して、Excelのデータを元に申請などを半自動で送り込めるのではないかと思います。

※もともとはウェブアプリケーションの実行テストの為のものなので、かなり細かく挙動をコントロール出来ます。

図:自動でスクショ取って閉じてくれる

Name属性ボタンをクリックできない時の対処法

Googleのトップページなど、ボタンにはID属性がなくName属性が付けられています。このボタンをクリックさせることで検索がされるわけなのですが、Selenium BasicでClickをさせてもクリックされない事があります。こんな時用にキーコードを送ることでクリックさせたことと同じ処理を行わせる事ができます。

この場合、事前にkeysを設定する必要があります。実行時バインディングであれば

事前バインディグさせている場合であれば

冒頭が異なりますが、Keyを送る時には、Clickではなく、SendKeys(keys.Enter)で、Enterキーを送りつけています。

Headless Chromeで実行

最新のChromeはPhantomJSのようなHeadless Modeという特殊なモードを備えています。これは、Chrome自体を表示させずにバックグラウンドで非表示のブラウザを使って処理を進める為のモードです。Excelでも高速化処理の時に「Application.ScreenUpdating = False」を加えて処理を非表示にする事がありますが、似たようなものです。

Headlessで動かす場合には、Startする前にseleniumオブジェクトにオプションを指定してあげます。

実行してみると一切ブラウザを表示せずに後ろで作業が進められます。

また、Chromeの場合、Google謹製のChrome自動化を実現するPuppeteerと呼ばれるheadless chromeなNode.jsモジュールがあります。nexeと呼ばれるモジュールでNode.jsまるごと1個のexeに出来ます。これをVBAから呼び出して上げる手法も非常に有効でしょう。

その他の選択肢

SeleniumBasic以外にも同様にVBAから操作することのできるライブラリがいくつか公開されています。非常に小規模ながら必要最低限の操作の機能を装備している「TinySeleniumVBA」であったり、WebDriverのみで操作を可能にした「SeleniumWrapperVBA」などがあります。前者はChromeのDevtoolに対して直接命令をHTTPリクエストで送り込んでいるのに対して、後者はSeleniumBasicを必要とせずWebDriverのみで操作をする仕組みになっています。

こちらのサイトに詳しい解説があるので、Selenium以外の選択肢が必要な場合はチャレンジしてみると良いでしょう。

REST APIを叩く

上記のSeleniumを使った手法は、マウス操作というよりも、それをプログラム化するにあたって、HTMLのElementを操作する方法になります。RPAでも各ElementのIDなどを参照して、座標を使ったものよりも正確にボタンを叩くなどの動作を実現できるものがあります。

しかし、これらは基本ウェブアプリケーションのデザインが僅かに変わるだけでも、動作しなくなる恐れがあり、またウェブアプリケーション側も自動操作の禁止をしているケースなどもあり、あまり好ましい方法とは言えません。一方で、現在のウェブアプリケーションはその殆どが「REST API」を備えているので、VBAのWinHttpなどを使って、GETやPOSTで命令を送り、目的の結果を直接的に受け取ったり送ったりが出来るので、マウス操作を真似るまでもなく高速に処理が可能です。

ウェブアプリケーションによって、与えるパラメータやヘッダー内容が異なるので一概には言えませんが、BoxというストレージサービスのAPIを叩いてファイルをダウンロードしたり、アップロードしたり、情報を取得するものは最もベーシックなものになるので、参考になるでしょう。基本、REST APIはウェブサイトのデザインとは異なり、プログラムからの利用を想定しているので、大規模な変更は生じにくいですので、自動化をするならばこちらの手法を学んだほうが応用が効くのでお得です。

以下はVBAからBox APIを叩いてみるの実践編に於ける、ファイルのダウンロードの命令を送る事例です。実際にはこのコードの他にOAuth2.0認証をするコードや、Access Tokenを取得するコード、Refresh Tokenで新しいAccess Tokenを取得するコード、プロキシーサーバを経由する場合の対応などが必要になってきます。

※自分のケースだとGoogle Apps Script APIを使って、自分でREST APIを作りVBAと連携させたりなどもして活用しています。

また、この手のコードは最初のOAuth2.0認証で、Internet Explorerを使う事例が多いのですが、64bit OSの場合そのままだと「拡張保護モード」に起因する具合が悪いケースがあるので、あらかじめ設定などを行うコードに於いて、レジストリを操作するモジュールを利用して、VBAからセットしてやる方法がスマートです。以下のコードでこの2つの設定を無効化する事が可能です。

メソッドを使って他のアプリを直接操作

VBAでは遥かに昔から「参照設定」を用いて、他のアプリケーションにAPIを介して直接操作する事は常套手段でした。とりわけWordやExcel, PowerPoint、OutlookそしてInternet Explorerの操作もVBA内から参照設定でオンにする事で、命令を直接送って操作が可能です。また、これ以外であってもSDKがリリースされているソフトウェアの場合、VBAから操作する為のメソッドを利用できるようにしてあったりします(業務用のアプリケーションだとこの手のSDKがリリースされているケースが多いです。工場の大きな機械の稼働状況や命令の送信ですらVBAで可能です)。身近な事例だとテプラをVBAから操作するSDKなどがありますね。

ウェブ上でよく見かける資料でCreateObjectでInternet Explorerなどと指定し、IEを操作してスクレイピングさせる事例などはまさにこの事例になります。直接アプリケーションのAPIを介しての操作での事例として、Outlookでメール送信させる事例をここに記載しておきます。利用する為には参照設定より「Microsoft Outlook 1x.0 Object Library」を追加するか?もしくは、CreateObject("Outlook.Application")にて利用をします。前述のSeleniumでの操作もこのメソッドを使った操作の事例になります(向こうはウェブブラウザの操作でウェブアプリケーションの操作が目的ですが)

ちなみに参照設定をしてから使う方法を「アーリーバインディング」と言い、基本そのライブラリがそのPCに入ってる必要があり、コードを書くときにコード補完が効きます。後者を「レイトバインディング」といい、特定のバージョンでなければ合致するものを呼び出してくれる反面、コード補完は効きません。

メールを送るだけであれば、上記のコードの一番したにある「With objMail」の部分だけで十分です。ネットには「VBAは他のアプリケーションの操作が出来ない」「RPAは操作可能で範囲が広い」などといったノンプログラマーと思わしき者が書いた資料が出回っていますが、大きな過ちですのでご注意ください。

関連リンク

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)