WindowsOSScript


 
MSDOSのバッチファイル
 

 MSDOSがPCの標準だった頃、パソコンの環境設定はAUTOEXCEC.BATを操作していた。パソコンが立ちあがる時に自動で環境設定をするのがAUTOEXCEC.BATだった。作業の途中で環境を変えるにはコマンドラインから別のBATファイルを実行した。
 真っ黒な画面を前にしてキーボードからコマンドを打ち込む。それもいいのだが、コマンドラインの内容を事前にファイルに書いておいた。それがバッチファイルだった。

 真っ黒な画面といった風景はオフィスでは見かけなくないましたが、Windowsにその環境は生きている。DOSプロンプトといいます。C:\WINDOWS\COMMAND.COM。これがDOSプロンプトの正体です。Explorerでこれを探し、ダブルクリックで小さな黒い窓が開きます。これがDOSプロンプ、DOS窓です。チョット試してみますか?

 「メモ帳」の正体はC:\WINDOWS\NOTEPAD.EXEです。キーボードからNOTEPADと打ち込みEnterで「メモ帳」が開きます。
NOTEPAD test.txt
としたら、test.txtを読込んで開きます。test.txtが開かない?確かにtest.txtは作ったのに.......そんな場合はPathを確認します。

 もう忘れかけたテクニックです。何度もキーボードに打ち込むのは、いやになります。F3が前のコマンドの呼び出しでした。....DOSKEY。これがないと、とても辛かった。.................
 C:\WINDOWS\COMMANDには、ちゃんとDOSKEY.COMがあります。DOS窓を利用しなければ、削除してもいいのですが。
 DOSからWindowsに変わり、一番の違いはなんでしょう。DOSの時代はアプリケーションを立ち上げると、そのアプリケーションがパソコンを占領してしまいました。Windowsはメモリーの許す限り、複数のアプリケーションが同時に動きます。お互い通信もしています。WindowsOSはそのプラットホームです。では、このプラットホームのバッチファイルを使ってみましょう。

 
WSH
 

 WSH ( Windows Scripting Host )はWin98で採用されました。
C:\WINDOWS\wscript.exeがその正体です。見つかるはずです、確認してみましょう。

 Windowsでも、キーボードからのコマンド操作もできるが、javaScript、正確にはJScriptでマクロが書ける。javaScriptで記述した手順のページを拡張子JSで保存すれば、そのファイルをダブルクリックで作業を自動実行できます。Windows版バッチファイルです。

 拡張子JSのスクリプトファイルがダブルクリックされると、関連づけに従ってその内容はwscript.exe(スクリプティングホスト)に渡され、スクリプティングホストは、JScriptのスクリプトエンジンを呼び出す。同時に、オートメーションコントローラーとして、各アプリケーションが提供するActiveXコントロールを制御します。

 
WSHのオブジェクト モデル
 

 javaScriptで操作できるWindows Scriptはオブジェクトモデルを持っています。
MSDNWindows Script Host
には、次のような階層構造が示されています。
WScriptオブジェクト モデル


 Windows Scripting Hostは、メッセージを画面に出力したり、CreateObject や GetObject などの基本的COM機能をスクリプトに対して提供します。つまり、各オブジェクトのプロパティとメソッドを操作することで処理を実行する環境を提供します。
 wscript.exe自体がCOM化されているので、WSHの機能は、他のアプリケーション,たとえばEXCELから呼び出すこともできます。ActiveXコントロールを提供するEXCELを操作することもできます。

 まずは、Windows Scripting HostのShell オブジェクトを利用してみましょう。

 
Shell オブジェクト
 

 Shell オブジェクトはWindowsOSの管理ルーチンです。この情報をプロパテで確認できます。

var WShell = WScript.CreateObject("Wscript.Shell"); var WshEnv = WShell.Environment("PROCESS"); WScript.Echo(WshEnv("PATH"));
「メモ帳」に上のように打ち込み、JSの拡張子で保存します。そしてダブルクリック。
1行目WShellという名前でWScript.Shellオブジェクトを作ります。
2行目そのプロパテの返り値を変数WsEnvに入れます。
3行目その配列のPATHの値を出力します。

 ダブルクリックの結果、小さなメッセージボックスが開き、パスのリストが表示される。DOS窓でPATHと打ち込んだと同じことだが、Windowsのデスクトップで行なわれる。GUI時代のバッチファイルということになる。
 Win98の場合、Environment("Process")として指定すると、COMSPEC、PATH、PROMPT、WINDIR、TEMP、TMPの6個が得られる。Environment("User")として指定すると、PATH、TEMP、TMPが得られる。 上のように変数に代入してINDEXで呼び出します。

 同じようにして、Windowsの特別なホルダーの階層構造を知る事ができる。
var WShell = WScript.CreateObject("Wscript.Shell"); WScript.Echo(WShell.SpecialFolders("Desktop"));
Shell オブジェクトの SpecialFolders プロパティは、SpecialFolders オブジェクトを返す。

特殊フォルダは次のとおり。

  • AllUsersDesktop
  • AllUsersStartMenu
  • AllUsersPrograms
  • AllUsersStartup
  • Desktop
  • Favorites
  • Fonts
  • MyDocuments
  • NetHood
  • PrintHood
  • Programs
  • Recent
  • SendTo
  • StartMenu
  • Startup
  • Templates

 このコレクションでは、フォルダ名をインデックスとして指定して、特殊フォルダへのパスを取得します。特殊フォルダは、コンピュータにログオンするユーザーごとに異なります。

 Shell オブジェクトの Environment プロパティによって返された環境変数もコレクションだった。コレクションはオブジェクト。上のWindows Scripting Hostオブジェクトで、黄色はコレクション。コレクションは集まりのことだが、結局はメモリー上に確保されたデータ。アクションの対象になるのがオブジェクトだから、データもオブジェクトということになる。

 オブジェクト言語で、何がオブジェクトか。オブジェクトの親子関係。オブジェクトの持っているプロパティ。利用可能なメソッド。これらを知る事が第一。 慣れるよりしょうがないとも言える。
    Shell オブジェクトのプロパテイ
  • CurrentDirectory(アクティブなプロセスの現在の作業ディレクトリへの絶対パス)
  • Environment
  • SpecialFolders
    Shell オブジェクトのメソッド
  • AppActivate メソッド
  • CreateShortcut メソッド
  • ExpandEnvironmentStrings メソッド
  • LogEvent メソッド
  • Popup メソッド
  • RegDelete メソッド
  • RegRead メソッド
  • RegWrite メソッド
  • Run メソッド
  • SendKeys メソッド
  • Exec メソッド


Shortcut オブジェクト

 普段、ショートカットは右クリック&ドラッグドロップで作る。コマンドラインではどうして作るのだろう?

 Shell オブジェクトのCreateShortcut メソッドが用意されている。これを使うのだろう。

var WshShell = WScript.CreateObject("WScript.Shell"); strDesktop = WshShell.SpecialFolders("Desktop"); var oShellLink = WshShell.CreateShortcut(strDesktop +"\\メモ帳.lnk"); oShellLink.TargetPath = "C:\\WINDOWS\\NOTEPAD.EXE"; oShellLink.WindowStyle = 1; oShellLink.Hotkey = "CTRL+SHIFT+F"; oShellLink.IconLocation = "notepad.exe, 0"; oShellLink.Description = "メモ帳"; oShellLink.WorkingDirectory = "D:\\temp"; oShellLink.Save();

 CreateShortcut メソッドによりShortcutオブジェクトが作成される。メモリー上にインスタンスが作成されるのでSaveメソッドでデイスクに保存する。SaveメソッドはShortcutオブジェクトのメソッドという位置つけのようです。

    Shortcut オブジェクトのプロパテイ
  • TargetPath  実行ファイルへのパスを示す文字列
  • WindowStyle  1:ウインドウをアクイブにする 3:ウインドウ最大にする 7:ウインドウ最小化
  • Hotkey  キー入力ショートカットを示す文字列
  • IconLocation  ショートカットアイコンのリンク先
  • Description  説明文を示す文字列が格納されている
  • WorkingDirectory  作業ホルダー
  • Arguments  ショートカットに引数の有る場合はWscript.Argumentsで指定。その値がこのプロパテイに格納される。
  • FullName  絶対パスを示す文字列が格納されている
 ショートカットアイコンは実行ファイルの中に記述されている。だから実行ファイル名を指定すればいい。何種類かあれば、0、1、2で指定できる。
 ショートカット設定ファイルの拡張子はLNKというWindowsの決まり。CreateShortcut()で.lnkがないとエラーでした。

UrlShortcut オブジェクト

 同様に、インターネットへのリンクもCreateShortcut を使用します 。
var WshShell = WScript.CreateObject("WScript.Shell"); var strDesktop = WshShell.SpecialFolders("Desktop"); var oUrlLink = WshShell.CreateShortcut(strDesktop + "\\aKoncool.url"); oUrlLink.TargetPath = "http://akon.cool.ne.jp/it/"; oUrlLink.Save();
 ショートカット設定ファイルの拡張子はURLというWindowsの決まり。CreateShortcut()で.urlがないとエラーでした。
    UrlShortcut オブジェクトのプロパテイ
  • TargetPath  実行ファイルへのパスを示す文字列
  • FullName  絶対パスを示す文字列が格納されている
 UrlShortcut オブジェクトにはDescriptionプロパテイは定義されていません。
oUrlLink.Description = "僕のインターネット白書";
と書いても、エラーが帰ってきました。


 
RUN メソッド
 

 Shell オブジェクトのRun メソッドという位置付けでコマンドやスクリプトが実行される。

 DOS窓を開くと、大抵C:\WINDOWSにいる。DIRとコマンドを打ち込むとファイルリストが見られる。膨大なリストだから/Pオプションをつけ途中で停止させ、Enterで次の画面を見る。コマンドオプションを間違えたりするとエラーメッセージが現れたりする。
 キー入力のコマンドをファイルに記述して拡張子batで保存する。そのファイル名を入力すると,コマンドが順次自動実行された。  このDOS時代の仕組みはCommand.comがホスト役をしていた。キー入力を解釈するインタープリターがいて、OSに指示をする。ファイルからコマンドを読み取る仕組みを付け足せば、後は同じ流れに乗せられる。バッチファイルの仕掛だ。
 Windows時代の同じ仕掛が、ShellのRUNメソッドだろう。
var WShell = WScript.CreateObject("Wscript.Shell"); var oExec = WshShell.Run("dir /s");
 上のコードをファイルに保存してDIR.JSとした。ダブルクリックでDOS窓が開き、ファイルリストが表示される。開いたままのDOS窓はコマンドを受けつけない。プロセスは終了しているということだろう。出力のみが保持されている ということだろう。
 DOS窓を開き、wscript DIR.JSと実行した。もう一つDOS窓が開き、状況は同じ。コマンドのオプション /sは、受け渡されていないようだ。または、その機能がWSHにないのだろう。


 
SendKeys メソッド
 

 あるアプリケーションにキーストロークを送信することができる。対象となるものは現在アクティブなウィンドウのプロセス。そのためにAppActivate メソッドが用意されている。

var WshShell = WScript.CreateObject("WScript.Shell"); WshShell.Run("notopad"); WScript.Sleep(100); WshShell.AppActivate("メモ帳"); WScript.Sleep(100); WshShell.SendKeys("1{+}"); WScript.Sleep(500); WshShell.SendKeys("2"); WScript.Sleep(500); WshShell.SendKeys("~"); WScript.Sleep(500); WshShell.SendKeys("*3"); WScript.Sleep(500); WshShell.SendKeys("~"); WScript.Sleep(2500);

 Activexのオブジェクトならオブジェクト間通信でデータの受け渡しができる。このSendKeysメソッドは、その機能のない場合の手段になりそうだ。
 上の例はメモ帳に文字を送信した例。AppActivate("メモ帳")で指定したアプリケーション名はタイトル バーに表示されるタイトル文字列を使っている。


 
Exec メソッド
 

 Shell オブジェクトのExec メソッドでも、コマンドやスクリプトが実行される。

MSDN Windows Script Hostリファレンスを見てみると、こんな解説がある。
「WshScriptExec オブジェクトは WshShell オブジェクトの Exec メソッドによって返されます。これは、スクリプトまたはプログラムの実行終了後または実行開始前のいずれかに行われます。」

 Exec メソッドの実行で、Shell オブジェクトの子供オブジェクトであるScriptExec オブジェクトが作られる。この関係は上の図を見れば、直感的に理解できる。
    ScriptExec オブジェクトのプロパテイ
  • Status 0: 実行中 1: 実行終了
  • StdOut スクリプトから標準出力に送信された全情報のコピー(読取専用モード)
  • StdIn プロセスにデータを引き渡すプロパティ
  • StdErr プロセスのエラー情報を取得するプロパティ
 DOS窓のコマンドの結果は、標準では画面に表示される。何かエラーがあれば、メッセージも画面に表示される。コマンド処理はキーボードから入力するのが標準。これらは標準出力(StdOut)、標準エラー出力(StdErr)、標準入力(StdIn)といい、特に指定しない場合のデバイスだ。出力をTXTファイルにしたい場合は、DIR >filelist.txt のように特別に指示した。画面への出力は消えるが、ファイルに記録されている。

 Exec メソッドでは、スクリプト実行時の標準出力や標準エラー出力をキャプチャーしてくれる。プロパテイとして処理できるということだろう。
 「子コマンドシェルでアプリケーションを実行します。アプリケーションから StdIn/StdOut/StdErr ストリームにアクセスできます。」とリファレンスに書いてある。子コマンドシェルということは、まずはDos窓を開き、そこからスクリプトを実行してプロセスを走らせることを前提にしているようだ。

 StdErrのリファレンス掲載の下記のスクリプトを実行してみた。

 スクリプトファイルをダブルクリックすると、一瞬Dos窓が開くが、閉じてしまう。Dos窓を開き wscript dire.jsと実行したのだが、何も反応がない。cscript dire.jsと実行した。正常終了だった。
 標準でWSHをインストールすると、C:\WINDOWS\COMMANDにcscript.exeが保存される。Exec メソッドはcscript.exeを使って実行する仕様のようだ。

var WshShell = WScript.CreateObject("WScript.Shell"); var oExec = WshShell.Exec("%comspec% /c dire"); function ReadAllFromAny(oExec) { if (!oExec.StdOut.AtEndOfStream) return oExec.StdOut.ReadAll(); if (!oExec.StdErr.AtEndOfStream) return "STDERR: " + oExec.StdErr.ReadAll(); return -1; } var allInput = ""; var tryCount = 0; while (true) { var input = ReadAllFromAny(oExec); if (-1 == input) { if (tryCount++ > 10 && oExec.Status == 1) break; WScript.Sleep(100); } else { allInput += input; tryCount = 0; } } WScript.Echo(allInput);

 javaScriptで書かれたこのサンプルを理解してみよう。

WshShell.Exec("%comspec% /c dire")は、DOSの環境変数comspecで指定されたCOMMAND.COMでDIRを実行する。/cオプションだから作業が終わってもDOS窓を閉じない。DIRをDIREと間違えているので、”コマンドが存在しない”とエラーメッセージが出る。それをStdErrプロパテを利用して表示させようとしている。

 ReadAllFromAnyという関数を用意しておく。StdOutに何かデータがあれば全部読んで帰る。StdErrに何かデータがあれば全部読んで帰る。両者を読み終わっているなら-1の帰り値で帰ってくる。

StdIn、StdOut、StdErrはTextStreamオブジェク形式なので、下記のメソットが使える。
メソッド.
Read( ) パラメータとして指定した文字数を読み込み、結果の文字列を返す
ReadAll() 現在位置以降の全体を読み込み、文字列を返す
ReadLine() 1行分のデータを読み込み、文字列を返す(改行含まず)
Skip( ) パラメータとして指定した文字数をスキップし、入力ポインタを進める
SkipLine() 次の行数に入力をスキップする
メソッド.
Write( ) パラメータとして指定した文字列が出力される。改行(CR+LF)は追加されない
WriteLine( ) パラメータとして指定した文字列が出力される。文字列の最後に改行(CR+LF)が自動的に追加される
WriteBlankLines( ) パラメータとして指定した数の空行が出力される
プロパティ .
AtEndOfLine データが行末になったかどうかを表す(ブール値)
AtEndOfStream 全データの最後かどうかを表す(ブール値)


 ます、用意した関数でStdOutとStdErrを読んで見る。読み終わって、プロセスが終了していて、10回読むことを繰り返したなら終わりにする(brake)。 それでないなら、0.1秒待ってみる。
 読み取って来たデータは、allInputに書き足して行く。
while (true)で無限ループを繰り返しているが、上のbrakeで、いつかは抜け出す。抜け出したらallInputを表示する。

 標準入出力で、スクリプトホストが二人いることがわかった。標準入出力はモニターを前にして、カタカタカタとブラインドタッチでコマンドを弾く管理者のための物。個人では、なかなかブラインドタッチとは行かない。そのため、スクリプトファイルを用意する訳だ。

 サンプルコードを実行すると、確かに”コマンドが存在しない”旨の熱せージが表示される。"%comspec% /c direを"%comspec% /c dirに書き換えて実行する。リストが表示されて正常終了。そこで、"%comspec% /c dir /pとして実行。

  dir /pは一画面ずつリストを見るオプション。キー入力で次の画面に進む。 そのはずなのだが、フリーズする。サンプルコードにはStdInの処理がされていない。”どれかキーを押してください”のメッセージもない。Ctrl+Cの強制終了も効かない。Ctrl+Alt+Deieteしか方法はない。

 "%comspec% /c dir /pをRUNメソッドでcscriptから実行してみた。DOS窓が開き、もう一つDOS窓でリストを表示、入力を待つ事なく全リスト表示。親のDOS窓は閉じるが、リストを表示したDOS窓はそのまま。DOS窓を終了させると消える。

 RUNメソッドの StdIn、StdOut、StdErrを開放して、コントロール可能にしたのがEXECメソットであることはこれで分かる。だが、コードをそれなりに用意しなければならない。
 Shell オブジェクトの全容がつかめた。まずはひと区切としよう。

2003.11.09
by Kon