今回からはWShellオブジェクトについて解説する。最初は外部プログラムの起動や制御を行うRun、AppActivate、SendKeysメソッドについて解説。
今回から数回にわたって、WSH(Windows Script Host)の内部オブジェクトであるWshShellオブジェクトとそこから派生するオブジェクトについて解説していく。
まずはWshShellオブジェクト(ProgIDは“WScript.Shell”)と、それから派生するオブジェクト群(WshEnvironmentオブジェクト、WshSpecialFoldersオブジェクト、WshShortcutオブジェクト、WshUrlShortcutオブジェクト、WshScriptExecオブジェクト)の階層構造とそれらのメンバ(プロパティ/メソッド)を図で概観してみよう。
このように、WshShellオブジェクトは、多数のオブジェクト、メンバを内包していることが分かる。今回からはWshShellオブジェクトのメンバを中心に解説し、次回以降に派生するオブジェクトとそのメンバの使い方について述べたいと思う。
まずは、基本となる、WSHからのアプリケーション起動方法について述べる。WSHからアプリケーションを起動するにはRunメソッドとExecメソッドが使用できる。実行時にアプリケーションのウィンドウ・スタイルなどを指定可能なのがRunメソッド、実行中にオブジェクトを通じてアプリケーションと通信を行えるのがExecメソッドである。Runメソッドの方がより簡略にアプリケーションを実行可能だ。今回はこのRunメソッドについて見ていこう。
ここでは、Windows OSに付属する標準のテキスト・エディタである「メモ帳(実行ファイル名はnotepad.exe)」を起動する例を見てみよう。notepad.exeの存在するパスは環境によって異なるので適宜書き換えてもらいたい。
※ファイルRunNotepad.vbs
Option Explicit
'オブジェクト変数の宣言とWshShellオブジェクトの作成。
Dim objWshShell
Set objWshShell = WScript.CreateObject("WScript.Shell")
'メモ帳を起動。
objWshShell.Run "C:\Windows\System32\notepad.exe"
MsgBox "メモ帳を起動しました。"
Set objWshShell = Nothing
実行結果は例えば次のようになる。
このスクリプトでは、最初にWshShellオブジェクトをWScriptオブジェクトのCreateObjectメソッドで作成し、オブジェクト変数objWshShellに代入している。これでWshShellオブジェクトを利用可能になる。CreateObjectメソッドについての詳細は前回の「3.COMオブジェクトの操作(1)」を参照してもらいたい。
このようにRunメソッドは引数に実行ファイル(拡張子exeのファイル)のパスを記述すると、そのアプリケーションを実行する。
Runメソッドの引数は環境変数(% ~ %で環境変数名をくくる)を展開するので、
objWshShell.Run "%windir%\system32\notepad.exe"
と書くこともできる。
なお、パスの通っているフォルダ(環境変数PATHにフォルダのフルパスが定義されているフォルダ)の実行ファイルを実行する場合、フォルダのフルパスは省略可能である。Windows XPやWindows Server 2003、Windows Vistaの32bitバージョン(x86)ではそれぞれC:\Windows\System32にパスが通っているので、
objWshShell.Run "notepad.exe"
と記述しても起動できる。また、スクリプトのあるフォルダにある実行ファイルも同じくフォルダ・パスは省略できる。カレント・ディレクトリにある実行ファイルも同様である(WSHからカレント・ディレクトリを設定する方法については後の回で述べる)。
実行ファイルのパスに半角スペースが含まれている場合は注意が必要だ。Runメソッドはそのような場合、実行ファイルのパスを ""(ダブル・クォーテーション記号)で囲む必要がある。すなわち、Internet Explorer(C:\Program Files\Internet Explorer\iexplore.exe)を起動するには、
objWshShell.Run """C:\Program Files\Internet Explorer\iexplore.exe"""
と書く必要がある。ちなみに、「"(ダブル・クォーテーション)」が3つ連続しているのは、"~"の中では、" を記述するためには "" と重ねて記述しなければならないからである。
Runメソッドでは、実行ファイルに渡すオプションも指定可能だ。Internet Explorerをキオスク・モード(全画面表示)で起動するには「-k」オプションを付けるが、その場合は次のようになる。
objWshShell.Run """C:\Program Files\Internet Explorer\iexplore.exe"" -k"
なお、キオスク・モードで開いたInternet Explorerのウィンドウは[Alt]+[F4]キーで閉じることができる。
Runメソッドはエクスプローラの関連付けに従ってファイルを開くこともできる。例えば、スクリプトが存在するフォルダにある「test.txt」というファイルを開くには、
objWshShell.Run "test.txt"
のようにする。すると.txtファイルに関連付けされたエディタ(デフォルトではメモ帳)が起動し、test.txtを読み込んで表示する。
Runメソッドには前述のとおり、実行するアプリケーションのウィンドウ・スタイルを省略可能な第2引数に指定できる。例えば、
objWshShell.Run "notepad.exe",3
のように記述すると、メモ帳が最大化されて実行される。第2引数に指定できる数値は次の表のとおりである。なお、ウィンドウをアクティブにするかどうかも決定できる。
第2引数に与える数値 | ウィンドウ・スタイル | アクティブ・ウィンドウ |
---|---|---|
0 | 非表示 | そのまま(変わらない) |
1(省略時の値) | 通常サイズ | 起動したアプリケーション |
2 | 最小化 | 起動したアプリケーション |
3 | 最大化 | 起動したアプリケーション |
4 | 通常サイズ | そのまま(変わらない) |
5 | そのまま | 起動したアプリケーション |
7 | 最小化 | そのまま(変わらない) |
Runメソッドの第2引数 Runメソッドの第2引数の数値を変更すると、実行するアプリケーションのウィンドウ・スタイルを指定できる。ほかにも第2引数に与える数値は10まで定義されているのだが、筆者の環境では有意な差が認められなかったのでここでは省略している。 |
Runメソッドには、アプリケーションの終了を待つか否かを決めるために、Boolean値を取る省略可能な第3引数も存在する。省略時はFalseで、アプリケーションの終了を待たずにRunメソッドの次の行が実行される(非同期処理)。Trueを指定すると、アプリケーションが終了するまでスクリプトが待機する(同期処理)。例えば次のように記述する。
objWshShell.Run "notepad.exe",,True
この場合、メモ帳のウィンドウが立ち上がり、ユーザーがウィンドウを閉じると、(「メモ帳を起動しました。」という)メッセージ・ボックスが表示される。
なお、Runメソッドは終了コードを戻り値として返す(正常終了時は0)。
AppActivateメソッドを使うと任意の(Runメソッドで起動したものも含め)ウィンドウをアクティブにでき、SendKeysメソッドを使うとアクティブなウィンドウに任意のキー・コードを送信できる。すなわち、キーボードからキーを打ち込む作業を自動化できる。
AppActivateメソッドは引数に指定したウィンドウ・タイトルに一致(完全一致だけでなく、前方一致、後方一致でも可)するものがあればそのウィンドウをアクティブにする(次回以降で述べるExecメソッドを使うとProcessIDを指定できるので起動したアプリケーションを厳密に特定してアクティブ化できる)。アクティブ化に成功したらTrueを返し、失敗したらFalseを返す。例えば次のコードを見ていただきたい。
※AppActivate.vbs
Option Explicit
'オブジェクト変数の宣言とWshShellオブジェクトの作成。
Dim objWshShell
Set objWshShell = WScript.CreateObject("WScript.Shell")
'電卓を非同期で起動。
objWshShell.Run "calc.exe"
'メモ帳を非同期で起動。
objWshShell.Run "notepad.exe"
'起動するのに1秒待つ。
WScript.Sleep 1000
Dim intCounter
For intCounter = 1 To 5
'電卓をアクティブにする。
objWshShell.AppActivate "電卓"
'1秒待つ。
WScript.Sleep 1000
'メモ帳をアクティブにする。
objWshShell.AppActivate "メモ帳"
'1秒待つ。
WScript.Sleep 1000
Next
MsgBox "終了。"
Set objWshShell = Nothing
このスクリプトをWindows Vistaで実行すると、電卓とメモ帳が立ち上がり、1秒ごとに交互にアクティブになり、5回繰り返すと終了する。メモ帳の正しいウィンドウ・タイトルは「無題 - メモ帳」だが、"メモ帳" を引数に指定すると後方一致するのでアクティブ化されるわけである。だがもし、"マイメロ帳"などと記述した場合は、一致するウィンドウがないので何も起こらない(戻り値はFalse)。
なお、Windows XP、Windows Server 2003では、(デフォルト状態のままでは)このスクリプトは思惑どおりに動作しない。最初のAppActivateのみ正しく動作し、ウィンドウはアクティブになりフォーカスを持つのだが、2回目以降のAppActivateではウィンドウはアクティブになろうとするもののフォーカスを奪えず、タスク・バーで該当ウィンドウのバーがデフォルトではオレンジ色(Windows Server 2003では青色)の点滅をするだけである(ただしこれは見掛け上アクティブになっていないだけで、AppActivateメソッドの戻り値はTrueであり、実際に次に述べるSendKeysメソッドでキー・コードを送るとフォーカスが得られる)。
さて、実際はAppActivateメソッドはSendKeysメソッドとともに使われるケースがほとんどである。というのも、SendKeysメソッドはアクティブなウィンドウにキー・コードを送るメソッドであるからだ。AppActivateメソッドでウィンドウをアクティブにしてからSendKeysメソッドでキー・コードを送る……、といった使い方がメインになる。実際に電卓で「18782+18782」を計算した結果を、メモ帳にコピーするというデモをご覧にいれよう。
※ファイルCalcAndPaste.vbs
Option Explicit
'オブジェクト変数の宣言とWshShellオブジェクトの作成。
Dim objWshShell
Set objWshShell = WScript.CreateObject("WScript.Shell")
objWshShell.Run "notepad.exe"'メモ帳を非同期で起動。
objWshShell.Run "calc.exe"'電卓を非同期で起動。
WScript.Sleep 1000'起動するのに1秒待つ。
objWshShell.AppActivate "電卓"'電卓をアクティブにする。
WScript.Sleep 100'0.1秒待つ。
objWshShell.SendKeys "18782"'電卓に"18782"と入力する。
WScript.Sleep 1000'1秒待つ
objWshShell.AppActivate "電卓"'電卓をアクティブにする。
WScript.Sleep 100'0.1秒待つ。
objWshShell.SendKeys "{+}"'電卓に"+"と入力する。
WScript.Sleep 1000'1秒待つ
objWshShell.AppActivate "電卓"'電卓をアクティブにする。
WScript.Sleep 100'0.1秒待つ。
objWshShell.SendKeys "18782"'電卓に"18782"と入力する。
WScript.Sleep 1000'1秒待つ
objWshShell.AppActivate "電卓"'電卓をアクティブにする。
WScript.Sleep 100'0.1秒待つ。
objWshShell.SendKeys "="'電卓に"="と入力する。
WScript.Sleep 1000'1秒待つ
objWshShell.AppActivate "電卓"'電卓をアクティブにする。
WScript.Sleep 100'0.1秒待つ。
objWshShell.SendKeys "^C"'電卓に[Ctrl]+[C]を送り、計算結果のコピー。
Wscript.Sleep 1000'1秒待つ
objWshShell.AppActivate "メモ帳"'メモ帳をアクティブにする。
WScript.Sleep 100'0.1秒待つ。
objWshShell.SendKeys "^V"'メモ帳に[Ctrl]+[V]を送り、計算結果を貼り付ける。
Set objWshShell = Nothing
このスクリプト実行中は誤動作を防ぐため、画面をクリックしたり、キーボードから入力しないようにしていただきたい。実行結果は例えば次のようになる。
SendKeysメソッドの引数にはキー・コードを指定する。「1」や「A」など単純な半角英数文字や「@」などの特殊文字はそのまま送ることができるが(ただし「+」「^」「%」「~」「[」「]」「{」「}」は、{+}のように、{ } で囲む)、「あ」などの日本語の文字列は残念ながら送ることができない。[Enter]などの特殊キーを送るには、{Enter}のように { } で特殊キー名を囲んで指定する。これらの特殊キーの一覧は「SendKeysメソッドの解説(MSDNサイト)」を参考にしてもらいたい。また、[Shift]や[Ctrl]や[Alt]キーをほかのキーと同時に押したときのキー・コードは、それぞれ「+」「^」「%」を同時押ししたいほかのキー・コードの前に付ける。例えば[Ctrl]+[C]を送るなら「^C」と指定する。「^」([Ctrl])を押し、続けて「C」「V」と入力したい場合は、「^C^V」と書いてもよいが、「^(CV)」という書き方もできる。これらのキー修飾は複数同時指定も可能で、[Shift]+[Ctrl]+[Esc](タスク・マネージャを起動するショートカット)を送るには、「+^{Esc}」とする。「X」という文字を10回入力したい場合は「XXXXXXXXXX」と書いてもよいが、「{X 10}」と書いてもよい。
なお、SendKeysメソッドはコンソール・アプリケーションにはキー・コードを送れない。代わりに後の回で述べるExecメソッドを用いる。
さて、先ほど掲載したスクリプトはいくつか問題点がある。AppActivateが何らかの原因で失敗し、Falseを返した場合、後続するSendKeysによって送られるキー・コードはどのウィンドウに送られるか分からない。そのため、それを防いだうえ、何度か再試行も行いたい。また、同じような動作を繰り返しているので連載第7回と第8回で説明したように、そういったコードはプロシージャ化するのが望ましい。ここではウィンドウをアクティブにしてキー・コードをそのウィンドウに送るActivateAndSendKeysプロシージャを定義し、コードを簡略化してみよう。少し難しくなるが問題としてみたい。
※ファイルActivateAndSendKeys.vbs
Option Explicit
'オブジェクト変数の宣言とWshShellオブジェクトの作成。
Dim objWshShell
Set objWshShell = WScript.CreateObject("WScript.Shell")
'メモ帳を非同期で起動。
objWshShell.Run "notepad.exe"
'電卓を非同期で起動。
objWshShell.Run "calc.exe"
'電卓に"18782"と入力する。
ActivateAndSendKeys "電卓", "18782", 1000
'電卓に"+"と入力する。
ActivateAndSendKeys "電卓", "{+}", 1000
'電卓に"18782"と入力する。
ActivateAndSendKeys "電卓", "18782", 1000
'電卓に"="と入力する。
ActivateAndSendKeys "電卓", "=", 1000
'電卓に[Ctrl]+[C]を送り、計算結果のコピー。
ActivateAndSendKeys "電卓"," ^C", 1000
'メモ帳に[Ctrl]+[V]を送り、計算結果を貼り付ける。
ActivateAndSendKeys "メモ帳", "^V", 0
Set objWshShell = Nothing
'*********************************************************
'用途: 指定したタイトルのウィンドウをアクティブにし、指定
' したキー・コードを送り、数ミリ秒待つ。
'受け取る値: strTitle: ウィンドウ・タイトル(String)
' strKey: 送るキー・コード(String)
' intWait: キー・コードを送った後待つミリ秒数
' (Integer)
'戻り値: 成功したらTrue、失敗したらFalseを返す(Boolean)。
'*********************************************************
Function ActivateAndSendKeys(strTitle, strKey, intWait) Dim intCounter '10回試行する。
For intCounter = 1 To 10 'AppActivateメソッドを実行し、戻り値がTrueなら、
If objWshShell.AppActivate(strTitle) Then
WScript.Sleep 100
'キー・コードを送る。
objWshShell.SendKeys strKey
'intWaitミリ秒待つ。
WScript.Sleep intWait
'成功を意味するTrueを返し、ループを抜ける。
ActivateAndSendKeys = True
Exit For
Else
WScript.Sleep 1000
'失敗を意味するFalseを返し、続行。
ActivateAndSendKeys = False
End If
Next
End Function
このスクリプトの実行結果は先ほどと同様なので省略する。このスクリプトのポイントは、Functionプロシージャ内でIf objWshShell.AppActivate(strTitle) Thenとすることで、AppActivateメソッドを実行すると同時に、その戻り値(成功=True、失敗=False)を判別していることである。これにより、成功していた場合は続けてSendKeysメソッドを実行して、指定ミリ秒待った後に試行ループを抜け、失敗した場合は1秒待ってから2度目のループを実行する。これを10回繰り返しても失敗するようなら、Functionプロシージャは失敗したものとする。
このFunctionプロシージャは実行結果を戻り値(成功=True、失敗=False)として返すので、もしAppActivateとSendKeysがうまくいったと思われる場合は次の処理へ、そうでない場合はエラーを出して終了させる、などの条件分岐も可能である。例えば、次のようなコードだ。
If ActivateAndSendKeys("タイトル1","aaa",2000) Then
ActivateAndSendKeys "タイトル2","bbb",500
Else
MsgBox "キー送出に失敗しました。"
WScript.Quit()
End If
ただし、AppActivateとSendKeysは万能選手ではない。このようにキー送出の「誤爆」を避ける処理を施しても、ほかのプログラムやユーザーの割り込みによって失敗する可能性は避けられない。そのことをよく念頭に置いて、ほかの手段(COMコンポーネントを通じてアプリケーションを外部から操作する、など)が用意されていない場合の最終手段として用いるようにしたい。
今回はWshShellオブジェクトの中核となる3つのメソッド、Run、AppActivate、SendKeysを掘り下げて詳説した。次回以降はWshShellオブジェクトの残りのメンバを紹介し、派生するオブジェクトのメンバを取り上げていく予定である。
Copyright© Digital Advantage Corp. All Rights Reserved.
Windows Server Insider 記事ランキング