【WSH事始め】JScriptでCOMオブジェクトを利用しないプログラムを実行する。(RunメソッドとExecメソッドの比較とSendkyesメソッド)

はじめに

 わかっている人はわかってる事柄。コレとコレどう違うの? 存在意義あるの?って思っちゃうだろうなぁって事へ ちょっとした解説を加えます。

やりたい事

 COMオブジェクトを利用しないプログラムをJScriptで実行します。

 RunメソッドやExecメソッドを使うと任意のプログラムを起動して操作できるのですが、どちらもキー入力をシミュレートしている為、操作対象のプログラムがアクティブ且つ、確実にコマンドを受付け、完了したことを待たないと誤動作を起こしますから、スクリプト実行時には端末操作をしない様に注意が必要です。

 ということで、RunメソッドとExecメソッドの比較と、AppActivate メソッドとSendkyesメソッドを解説します。

 尚、noteは表が挿入できないので箇条書き的にだらだら記述しています。うまい比較方法が思いついたらまとめ直しをするかもしれません。


説明丸投げの外部リンク

 WshShellオブジェクトを利用し、スクリプトから外部プログラムを起動する方法を解説しています。基礎知識としてのRunメソッドとExecメソッドの違いと使い方の解説をお任せするサイトです。後ほどまとめを行います。

・「RunメソッドとExecメソッド」の比較と「Runメソッドの使い方」の解説

 「RunメソッドとExecメソッド」の比較と「Runメソッドの使い方」を解説しています。(記事の2頁目)


 MS Docsから。Runメソッドは『新しいプロセス内でプログラムを実行します。』


・「Execメソッドの使い方」の解説

「Execメソッドの使い方」を解説しています。(記事の3頁目)


 MS Docsから。Execメソッドは『子コマンドシェルでアプリケーションを実行します。アプリケーションから StdIn/StdOut/StdErr ストリームにアクセスできるのでCScript向きです。』


・SendKeysメソッドとAppActivate メソッドの解説

「キー・ストロークの送信:SendKeysメソッド」について解説(記事の4頁目)


 MS Docsから。SendKeysメソッドは『キーボードから入力したときのように、1 つ以上のキー ストロークをアクティブなウィンドウに送ります。』


 MS Docsから。AppActivateメソッドは『アプリケーション ウィンドウをアクティブにします。』


Runメソッドの特徴

 Run メソッドを使うと完全に別プロセスとしてプログラムを起動します。Runメソッドを使った場合に直接操作できるのは、起動の際に最小化・最大化などの表示方法を指定することと、プログラムの終了を待つ場合に終了コードを取得することのみです。

・Runメソッドの引数と戻り値

・Arg1:実行するコマンド ライン(引数を含む)を示す文字列値を指定する。
・Arg2:ウィンドウの表示方法を指定。(0から10までの整数。通常は"1"で使用する)
・Arg3:起動したプログラムの終了を待つ(=終了するまで処理が止まる)というオプションが指定できる。
・戻り値:起動したプログラムの終了を待たなずにスクリプトを実行したとき(終了待ちフラグ=false)、"0"
実行したコマンドの終了を待ってスクリプトを実行したとき、(終了待ちフラグ=true)、実行したコマンドのエラーコード。

・Runメソッドの解説まとめ

・Run メソッドは新しい Windows プロセス内でプログラムの実行を開始します。
・Arg3の指定によりスクリプト内の次の処理に進まずにプログラムの実行が終了するまでスクリプトを待機させることができます。これにより、スクリプトとプログラムを同期させて実行することが可能となります。
・AppActivateメソッドでプログラムをアクティブにするとき、アプリケーションのタイトル文字列だけで判断する。ので、同じタイトル文字のウインドウが立ち上がっていると判別できない。


Execメソッドの特徴

 パラメータとして実行するプログラムを指定する点ではRunメソッドと違いはない。が、Execメソッドを利用すれば、終了コード(要CScript実行)の取得だけでなく、標準入出力(要CScript実行)を通してコマンドにオプションを与えたり、コマンド実行の結果を取得したりできるようになる。

 尚、別プロセスが立ち上がらないのと、コマンド同期パラメタを持たない以外はRunメソッドの上位互換っぽい。(WSH 5.6からの機能)

・Execメソッドの引数と戻り値

・Arg1:実行するコマンド ライン(引数を含む)を示す文字列値を指定する。
(リモートのスクリプトを実行することはできません。)
・戻り値:WshScriptExecオブジェクトを返す。
(終了コードの取得だけでなく、標準入出力を通してコマンドにオプションを与えたり、コマンド実行の結果を取得したりできるようになる。)

・Execメソッドの特徴まとめ

・AppActivateメソッドでPrecessIDを使ってプログラムをアクティブにできる。
・Runメソッドと同様の実行したコマンドとの同期をとるなら、明示的にループを回すことでプログラムの終了を待つ必要がある。実行例としてプログラムの実行終了(Status=0)の判定後、ループを抜けて終了コード(ExitCodeの値)判定をすることができる。


SendKeysメソッドの特徴


・アクティブなウィンドウが対象。
 (Run, ExecメソッドでAPを起動し AppActivateメソッドで切り替えておく必要がある)
・起動されているプログラムにキー・ストロークを送信する。
・制御の可否が時間的なタイミングに大きく影響を受ける問題がある。
(できれば、SendKeysメソッドを使わず、プログラムを制御する別の方法がないかを検討することが望ましい。)

※)操作するプログラムによっては送信するキーの大文字、小文字の別でキーを受け付けない場合がある。
 (例:[Ctrl]+A というショートカットキーがあったとして、SendKeysメソッドでキー送信すると、[Ctrl]+A と [Ctrl]+a のどちらか片方のみ有効となる場合がある。)


AppActivate メソッドの特徴


・ウィンドウ・タイトルの文字列、もしくはプロセスIDを指定することで、そのアプリケーション・ウィンドウをアクティブにする。
・タイトルの検索順 :タイトルは完全に一致する文字列でなくても、最完全に一致するもの→先頭が一致するもの→末尾が一致するものの順に検索される。
(複数ヒットした場合には目的のウィンドウがアクティブになる保証無し。)
・プロセスIDの取得方法:Execを使って起動したプログラムでは、WshScriptExecオブジェクトのProcessIDプロパティから取得できる。
・実行に成功すればTrueを、失敗すればFalseを返す。



「RunメソッドとExecメソッド」の比較に追加の解説

 「RunメソッドとExecメソッド」の比較をさらに追加してまとめると下記の様になります。

------------------------------< 基本動作 >------------------------------
終了コード取得  Runメソッド :○
         Execメソッド:○
プロセスID取得  Runメソッド :×
         Execメソッド:○
最大化・最小化での実行  Runメソッド :○
             Execメソッド:×
コマンドラインでの標準入出力の利用  Runメソッド :×
                   Execメソッド:○
ウインドウ    Runメソッド :新しいコマンドプロンプトで表示
         Execメソッド:CScriptであれば表示しない

------------------------------< 引数の指定 >------------------------------

ウインドウ表示方法(最大化、最小化等)
  Runメソッド :引数で指定可能
  Execメソッド:指定不可

起動したAPの終了を待つかどうか
  Runメソッド :引数で指定可能
  Execメソッド:ループでAPの終了タイミングを計る必要あり

------------------------------------< 戻り値 >------------------------------------
戻り値
  Runメソッド :完了コード、又はエラーコード
  Execメソッド:WshScriptExecオブジェクト
         (以下のプロパティ&メソッド有り)
            .ExitCodeプロパティ (終了状態)終了コード
            .ProcessIDプロパティ
            .Statusプロパティ (起動状態)0 or 1
            .StdIn,StdOut,StdErr の各プロパティ
            .Terminateメソッド
  
----------------------------< 使用可能メソッド >----------------------------
AppActivateメソッドでの指定方法
  Runメソッド :△(タイトルバーのみ)
  Execメソッド:○(タイトルバー&ProcessID)
Sendkeysメソッドでのキー送信
  Runメソッド :○
  Execメソッド:○


サンプルスクリプト

 メモ帳をメソッドで起動してSendkeysメソッド使ってそのまま終了するスクリプトを3パターン作りました。

・Runメソッドでメモ帳起動 → タイトル文字列でアクティブ化 → "Alt+f+x" のキー ストローク(メモ帳の終了)を送信
・Execメソッドでメモ帳起動 → タイトル文字列でアクティブ化 → "Alt+f+x" のキー ストローク(メモ帳の終了)を送信
・Execメソッドでメモ帳起動 → PrecessID でアクティブ化 → "Alt+f+x" のキー ストローク(メモ帳の終了)を送信


 ただ、ちょっと困ったことに、各スクリプトの順番を入れ替えると上手くいかないケースがあるのです。正常終了する順番で記載しておくので、実行時には実行パターンの順番を入れ替えてみてください。なぜSendkyesメソッドがうまくいかないときがあるのかは不明。RunメソッドとExecメソッドの実装時期からくるものなのでしょうか?

/* --------------------------------------------- */
/* Runメソッド使用(タイトル文字列でアクティブ化) */
/* --------------------------------------------- */
var objWshShell,objBoolean;
objWshShell = WScript.CreateObject("WScript.Shell");	// WshShellオブジェクトを生成する
objWshShell.Run("notepad.exe", 1, 0);					// メモ帳を起動する (プログラムの終了を待たずに処理を進めるので戻り値は0固定)

WScript.Sleep(1500);		// 起動にかかる時間を適当に見積もる

objWshShell.AppActivate("無題 - メモ帳");
/*sleepなくてもOK  WScript.sleep(0500); */
objWshShell.Sendkeys("%fx");
WScript.Echo("実行を終了しました("
	,"\n\tRun#1:上手くいく"
	,"\n\tRun#2:失敗する?(終了処理がほぼ失敗する。)"
	,"\n\tRun#3:失敗する?(終了処理がほぼ失敗する。)"
);

/* ----------------------------------------- */
/* Execメソッド使用(ProcessIDでアクティブ化) */
/* ----------------------------------------- */
var objWshShell,objApp;
objWshShell = WScript.CreateObject("WScript.Shell");	// WshShellオブジェクトを生成する
objApp = objWshShell.Exec("notepad.exe");				// メモ帳を起動する 

while(objApp.Status != 0){	WScript.Sleep(100); }

objWshShell.AppActivate(objApp.ProcessID);
/*sleep必須*/	WScript.sleep(0500);
objWshShell.Sendkeys("%fx");
WScript.Echo("実行を終了しました("
	,"\n\tExec.ProcessID#1:上手くいく"
	,"\n\tExec.ProcessID#2:上手くいく"
	,"\n\tExec.ProcessID#3:上手くいく"
	);

/* ---------------------------------------------- */
/* Execメソッド使用(タイトル文字列でアクティブ化) */
/* ---------------------------------------------- */
var objWshShell,objApp;
objWshShell = WScript.CreateObject("WScript.Shell");	// WshShellオブジェクトを生成する
objApp = objWshShell.Exec("notepad.exe");				// メモ帳を起動する 

while(objApp.Status != 0){	WScript.Sleep(100); }

objWshShell.AppActivate("無題 - メモ帳");
/*sleep必須*/	WScript.sleep(0500);
objWshShell.Sendkeys("%fx");
WScript.Echo("実行を終了しました("
	,"\n\t.Exec.タイトル文字#1:上手くいく"
	,"\n\t.Exec.タイトル文字#2:上手くいく?(たまに終了処理に失敗する。)"
	,"\n\t.Exec.タイトル文字#3:上手くいく?(たまに終了処理に失敗する。)"
);



この記事が気に入ったらサポートをしてみませんか?