WindowsScriptHost と レジストリ


 
WSH
 

 WSH ( Windows Scripting Host )はWin98で採用されました。
C:\WINDOWS\wscript.exeがその正実体です。windowsOSの標準部品で、たいていのPCにはインストールされています。

 Windowsでは、javaScriptでOSマクロが書けます。

 javaScriptで記述した作業手順を拡張子JSで保存すれば、それをダブルクリックで自動実行できる。Windows版バッチファイルということだ。

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

 

ActiveXコントロール
 

 MicrosoftWindowsOSはアプリケーションに対して基本機能をAPIとして提供する構造です。それと引換えに、アプリケーションは直接ハードウエアにアクセスしないのがルールです。この構造は、アプリケーションが共通部品から構成されている事を意味し、アプリケーション間の連携が容易なことが想像できる。

 Microsoftは、Windows 98で,従来のアプリケーション連携(OLE)をActiveXコントロールに模様変えしました。

 戦略商品インターネットブラウザを、このActiveXコントロールで組立てています。

 クリップボードの利用、OFFICE製品間の文書貼り付け、図形貼り付けといったデーター連携は、ActiveXコントロールの採用により、アプリケーション間データ通信に模様替えされました。ActiveXコントロールの本質はCOM(Componet Object Model)と呼ばれるアプリケーション間通信です。

 システムとして大規模になったパソコンOS,アプリケーションは、小さなプログラムの集合体として構築されています。OSの部品はEXCELのようなアプリケーションにも利用されます。ブラウザも利用します。このように利用するため、パーツであるプログラムは形が決まっていません。利用する時に初めて形(変数の値)を決めます。

 プログラム用語の「オブジェクト」がこのパーツに対応すると考えていいでしょう。「インスタンス」という用語がよく出てきますが、オブジェクトにインスタンスを与えるということは、変幻自在の共通部品(オブジェクト)を、使う段階で形(インスタンス)を確定してメモリーに保存し使い回すという方法です。

 共通部品を利用するには、インターフェースがいります。中身を一々点検しなくてもインプットに対してアウトプットが保証されていなければ利用できません。このルールがインターフェースです。中身はブラックボックスでも、効果の保証がありさえすれば、皆で利用しましょう。というコンセプトです。

 ネットにIPアドレスがあるように、部品にもアドレスがあります。ActiveXコントロールは対応するアドレスをPCのレジストリに記録します。アプリケーションは、使うActiveXコントロールがある場合、レジストリに記録されたIDを調べます。そこから対応するプログラム(ファイル)を識別して利用を開始します。

 
JavaScript
 

 HTMLに動的な要素を加えたのがJavaScriptです。1995年にNetscape が、Sun と共同で開発しました。

 Microsoft は、JavaScriptと同等な機能をVisual Basic を元に開発し、VBScript として1997 年のWin98で世に送り出しました。JavaScripコンパテイブルのJScriptも同時に公開しました。

 ページ記述言語JavaScriptは、MicrosoftWindowsOSではスクリプト言語として、OSの操作、アプリケーションのカスタマイズに深く関係することになりました。
 本来、ローカルPCのファイルアクセスを制限した設計であったJavaScriptは、MicrosoftMicrosoftOS上ではScripting.FileSystemObjectを利用でき、システムの奥深く操作可能な強力なスクリプトワールドを提供することになったのです。

 マルチタスクOSであるWindowsは、同時に多くアプリケーションが動いています。そのアプリケーション間では通信が行なわれています。Windowsアプリケーションを操作することは、オブジェクトをスクリプトで操ることであり、スクリプトに強力なパワーを授けることになりました。


 こような観点に立って、MicrosoftWindowsスクリプトワールドの指令塔、WindowsScriptHost、WSCRIPT.EXEを見てみましょう。

 
ActiveXの司令塔
 

 スクリプトにCOM(Componet Object Model)インターフェースを提供するWSCRIPT.EXEは、ActiveXの司令塔です。

 Windows Scripting Hostは、メッセージを画面に出力したり、CreateObject や GetObject コマンドでCOM機能をスクリプトに対して提供します。各オブジェクトのプロパティとメソッドを操作する環境を提供します。

 それ自体、WScriptというオブジェクトですが、メッセージ画面は貧弱なものです。
 WScriptオブジェクト自体のメソッドとプロパティは次のようなものがあります。

メソッド.
CreateObject object.CreateObject(strProgID[,strPrefix])
GetObject object.GetObject(strPathname [,strProgID], [strPrefix])
Echo メッセージボックスにデータを表示する。項目間はカンマで区切る。
Quit プロセスを終了させる。終了コードは省略可能。その場合0
Sleep プロセスを中断。ミリ秒単位で指定
ConnectObject オブジェクトの出力インターフェイスをスクリプト ファイルに接続
DisconnectObject 接続解除。オブジェクトのイベントに応答しなくする
プロパティ .
Interactive true false falseにすると入出力をしなくなる。
Arguments コマンド引数のコレクション
Name WScript オブジェクト (ホストファイル) の名前
Version WScript オブジェクト (ホストファイル) のバージョン
FullName WScript オブジェクト(ホストファイル)の絶対パス
Path WScript オブジェクト(ホストファイル)の格納デイレクトリ
ScriptName 実行中のスクリプトファイルのファイルの名
ScriptFullName 実行中のスクリプトファイルの絶対パス


WScript.Interactive = true; WScript.Echo ("Host="+WScript.Name," Version="+WScript.Version ); WScript.Echo (WScript.Path," ", WScript.FullName); WScript.Echo ("Script="+WScript.ScriptName," ", WScript.ScriptFullName); WScript.Quit(1);


「メモ帳」に上のように打ち込み、JSの拡張子で保存する。そしてダブルクリック。

 メッセージボックスが開く。

1行目 インタラクテイブモードに設定。
2行目 スクリプトホストの名前とバージョンの表示。
3行目 スクリプトホストのある場所の表示。
4行目 実行中のスクリプトの名前とファイルのある場所の表示。
5行目 プロセスを終了させる。

 ダブルクリックの結果、小さなメッセージボックスが開き、OKをクリックで順次表示して終わる。
 1行目のtrueをfalseにして実行すると、何も表示をしない。スクリプトは、裏方の仕事が多く、このほうがいい場合が多い。


Arguments

 コマンドには,引数が使える。スクリプトでは、WScriptオブジェクトのプロパティとして処理できる。
WScript.Interactive = true; WScript.Echo ("Host="+WScript.Name," Version="+WScript.Version ); WScript.Echo (WScript.Path," ", WScript.FullName); WScript.Echo ("Script="+WScript.ScriptName," ", WScript.ScriptFullName); for (i = 0; i < WScript.Arguments.length; i++) { WScript.Echo(WScript.Arguments(i)); } WScript.Quit(1);
 最初の例に,5〜8行目を付け加えてJSファイルを保存しておく。引数を表示させるルーチンだ。

 こんどは、エクスプローラーでファイルを選択し、JSファイルに、ドラッグ&ドロップする。

 結果は、選択したファイル名を順次表示して終わる。

 EXCELのショートカットにデータファイルをドロップすると、データを読み込んでEXCELが立ちあがった。Windows のアプリケーションは,この機能を実装している。

 GUI環境でのスクリプトワールドの司令塔、WSCRIPT.EXEが、スクリプトにもドラッグ&ドロップの機能を提供している。


 では,TXTファイルを数個選択してドラッグ&ドロップすると、すべて「メモ帳」で開くスクリプトを書いてみる。
var WShell = WScript.CreateObject("WScript.Shell"); for(var i = 0; i < WScript.Arguments.length; i++) WShell.Run("notepad \"" + WScript.Arguments(i) + "\"");
 このソースを「メモ帳」にコピー&ペーストして、JSの拡張子で保存する。デスクトップに置いておく。エクスプローラーでTXTファイルを選択してドラッグ&ドロップする。すると、全部「メモ帳」で開いて待機してくれる。便利! 。

 
WSHのオブジェクト モデル
 

 javaScriptで操作できるWindows Scriptは,次のような階層構造のオブジェクトモデルを持っている。
WScriptオブジェクト モデル


 Windows Scripting Hostは、CreateObject や GetObject でスクリプトにCOM機能を提供する。つまり、オブジェクトのプロパティとメソッドを操作できる環境を提供する。

 Windows Scripting HostのShell オブジェクトは、WindowsのShell(管理ルーチン)をCOM化してオブジェクトとして利用できるようにしたもの。

上の例では、Shell オブジェクトのRUNメソッドを使った。Shellの実体はexplore.exeであるが、エクスプローラーで実行ファイルをダブルクリックで実行が開始された。 Shell オブジェクトのRUNメソッドはこれに対応する。

 
Shell オブジェクト
 

Shell オブジェクトのプロパテイ
  • CurrentDirectory(アクティブなプロセスの現在の作業ディレクトリへの絶対パス)
  • Environment
  • SpecialFolders Shell オブジェクトのメソッド
  • AppActivate メソッド
  • CreateShortcut メソッド
  • ExpandEnvironmentStrings メソッド
  • LogEvent メソッド
  • Popup メソッド
  • RegDelete メソッド
  • RegRead メソッド
  • RegWrite メソッド
  • SendKeys メソッド
  • Run メソッド
  • Exec メソッド

  •   Environmentプロパテイ

    var WShell = WScript.CreateObject("Wscript.Shell"); var WshEnv = WShell.Environment("PROCESS"); WScript.Echo(WshEnv("PATH"));
    「メモ帳」に上のように打ち込み、JSの拡張子で保存します。そしてダブルクリック。

    1行目 WShellという名前でWScript.Shellオブジェクトを作ります。
    2行目 そのEnvironmentプロパテの返り値を変数WsEnvに入れます。
    3行目 そのPATHの値を出力します。

     ダブルクリックの結果、小さなメッセージボックスが開き、パスのリストが表示される。DOS窓でPATHと打ち込んだと同じことだが、GUI時代のバッチファイルということ。

     Win98の場合、
    Environment("Process")として指定すると、COMSPEC、PATH、PROMPT、WINDIR、TEMP、TMPの6個が得られる。

    Environment("User")として指定すると、PATH、TEMP、TMPが得られる。


      SpecialFoldersプロパテイ

     同じようにして、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

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


      CreateShortcut メソッド

     普段、ショートカットは右クリック&ドラッグドロップで作る。スクリプトでは、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  絶対パスを示す文字列が格納されている
     ショートカットアイコンは実行ファイルの中に記述されている。Windowsアプリケーションの決まり。だから実行ファイル名を指定すればいい。何種類かあれば、0、1、2で指定できる。
     ショートカット設定ファイルの拡張子はLNKというのはWindowsの決まり。この拡張子がないとエラーになる。

      UrlShortcut オブジェクト

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


     
    SendKeys メソッド
     

     Shellから、アプリケーションにキーストロークを送信することができる。対象となるものは現在アクティブなウィンドウのプロセス。そのために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("メモ帳")で指定したアプリケーション名はタイトル バーに表示されるタイトル文字列を使っている。

    + は{ }でくくる。など、特殊ルールがありますので、あまり利用しません。

    例えば、EXCELのシートに数字を書き込むなら、activeXですから、とても簡単です。
    WScript.Interactive = true; var ExcelApp = WScript.CreateObject("Excel.Application"); ExcelApp.Visible = true ExcelSheet = new ActiveXObject("Excel.Sheet"); ExcelSheet.ActiveSheet.Cells(1,1).Value = "ここは、A1"; ExcelSheet.ActiveSheet.Cells(3,3).Value = "ここは、C3"; var WshShell = WScript.CreateObject("WScript.Shell"); WScript.Sleep(1000); WshShell.SendKeys("1"); WScript.Sleep(1000); WshShell.SendKeys("3"); WScript.Sleep(1000); WshShell.SendKeys("{ENTER}"); WScript.Sleep(2500); WScript.Echo (); WScript.Quit(1);
     "ここは、A1"とデータを送っていますが、ここに送りたい数字や文字を書き込めば,それで終り。スクリプトのWSHのオブジェクトとアプリケーションのEXCELオブジェクトは、常にコンポーネント同士の通信をしていますから、Cells(1,1).Valueのように住所を指定してやればデータの受け渡しが出来るということです。
     上の例は、SendKeysの実験をしていますが、実用上は不用です。


     
    プロパテイとオブジェクト
     

     WSHのオブジェクト モデルの図を、もう一度見よう。

     オブジェクト型の言語、javaScriptで、何がオブジェクトで、どんなプロパテイがあり、どんなメソッドで操作出来るのか。これがポイントなのだが。

     例えば、Shell オブジェクトのCreateShortcut メソッド で、Shortcutオブジェクトが作成される。メモリー上にオブジェクトとして作成されるのだが、Saveメソッドで保存しないと、スクリプトが終了すると消えてしまう。メソッドの対象になるものはオブジェクト。UrlShortcut オブジェクト もそう。
     階層構造のWScript.Shell.UrShortcutがフルネームのオブジェクト

     これは、毎回Saveメソッド保存されるから、一度に多数は発生しない。だから、コレクションにはならない。

     Shell オブジェクトの Environment プロパティは、Processといったインデックス で、COMSPEC、PATH、PROMPT、WINDIR、TEMP、TMPの6個が取り出される。メソッドとして明示的ではないのだが、COMSPECなどのインデックスでアクセスされる配列変数だろう。プロパティから自動生成されたオブジェクト、複数あるからオブジェクトのコレクションと理解できる。

     SpecialFoldersプロパテイも同様。ただ、SpecialFoldersが複数形なのに、Environmentは複数形ではない。このへんは厳密といえる。

    スクリプトのルートオブジェクトWScript。そのプロパティArgumentsは、
    WScript.Arguments(0)
    WScript.Arguments(1)
    WScript.Arguments(2)

    と表記される。配列変数イメージなのだが、プロパティ。
     同時に、Argumentsオブジェクトも存在している。オブジェクトの方は、
    WScript.Arguments.NamedとWScript.Arguments.Unnamedとに分類されて保存されている。

     スクリプトのルールで,引数はスペースで分離して幾つでも指定できるが、
      /名前:値 で表記されるものを、名前付きパラメータとして区別する。

    呼び出し方も、WScript.Arguments.Unnamed(n)とWScript.Arguments.Named(”名前”)に 区別される。

     このような背景があり、プロパティからオブジェクトが自動生成される。両者は一体のもので、二つの顔を持つと理解できる。


     
    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 /p");
     上のコードをファイルに保存してRUN-DIR-P.JSとする。ダブルクリックでDOS窓が開き、ファイルリストが表示される。一気に表示が最後まで進み、開いたままのDOS窓はコマンドを受けつけない。プロセスは終了しているということだ。出力のみが保持されている。
     /pオプションは、表示を中断してEnterで次のページを見るオプションだった。Run メソッドは、実行中の標準入出力を受け付けない。そのため、キーボードからのEnterは無視され、一気に終了してしまう。

     Run メソッドは、次ぎのように第2,第3引数を指定できる。
     object.Run(strCommand, [intWindowStyle], [bWaitOnReturn])
    WindowStyleが7だと、起動したプログラムは最小化表示され、現在のアクティブ・ウィンドウが切り替わらない。表の作業に影響なく,バックグランド実行したいプログラムには好都合だ。
    WaitOnReturnをTrueを指定すれば、起動したプログラムが終了するまでスクリプトの実行が停止するようになる。いくつかのプログラムを順次実行させる場合に好都合だ。

     Run メソッドは、WSH 5.6以前の環境で開発された。WSH 5.6以降はExecメソッドが推奨されている。ただ、Execメソッドは、GUI環境のwscript.exeでは実行できない。GUI環境のwscript.exeでEXCELや「メモ帳」などのアプリケーションを実行する場合に利用する。プロセス途中の、入出力を利用するなら、DOS窓コマンド型の、Execメソッドを利用する。


     
    Exec メソッド
     

     Shell オブジェクトのExec メソッドは、コマンドやスクリプトをDOS窓で実行させることを想定している。

    MSDN Windows Script Hostリファレンスには、こんな解説図がある。

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

     Exec メソッドでは、「子コマンドシェルでアプリケーションを実行します。アプリケーションから StdIn/StdOut/StdErr ストリームにアクセスできます。」とリファレンスにある。Dos窓からスクリプトを実行し、子プロセスを走らせることを前提にしている。

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

     デスクトップでスクリプトファイルをダブルクリックすると、一瞬Dos窓が開くが、閉じてしまう。
     Dos窓を開き wscript dire.jsと実行したのだが、何も反応がない。
     Dos窓を開き 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に書き換えて実行する。リストが表示されて正常終了。標準出力(StdOut)、標準エラー出力(StdErr)をスクリプトで、キッチリ処理しているので成功ということだ。

     そこで、"%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メソット、これを提供している。スクリプトでコントロール可能で、標準入力(StdIn)のコードをそれなりに用意しなければならない。

     VBScriptなら、Inputダイアログがあるが、JavaScriptには入力部品がない、一工夫が必要だ。


     
    レジストリにアクセス
     

     Windows98の環境管理はsystem.datuser.datでおこなわれる。
     PCを立ち上げると,この二つのファイルがメモリーに展開される。それがレジストリー。

     Windows Scripting Hostは、javaScriptでレジストリを読む、書換える,削除することができる。 この意味は,正確に理解する必要がある。

     Windows ScriptingのShellオブジェクトには、レジストリ操作メソッドが用意されている。

    レジストリを読む RegReadメソッド
    var WShell = WScript.CreateObject("Wscript.Shell"); a = WShell.RegRead("HKCU\\Software\\aaa\\date"); WScript.Echo(a);

    レジストリを削除する RegDeleteメソッド
    var WShell = WScript.CreateObject("Wscript.Shell"); WShell.RegDelete("HKCU\\Software\\aaa\\date");
       キーを指定すると、その下のキー・値は全て削除される。

    レジストリに書く RegWriteメソッド
    var WShell = WScript.CreateObject("Wscript.Shell"); var strDate = (new Date()).toLocaleString(); WShell.RegWrite("HKCU\\Software\\aaa\\date", strDate);
    WshShell.RegWrite(strName, anyValue, [strType])
    • キーは 最後に\付け、値の名前は\を付けない。
    • データ型を指定すると、値が適当に変換されて書きこまれる。
    データ型 strType 説明 メモ
    REG_SZ 文字列 .
    REG_DWORD 数値 整数
    REG_BINARY 2 進数の値 32ビット整数値
    REG_EXPAND_SZ 展開可能な文字列 "%windir%\\calc.exe"
    REG_MULTI_SZ 文字列の配列 VBArray (文字列)

      レジストリを理解する

     レジストリにアクセスするには、キーが分かっていないとできない。バイナリーなのでエディターで見ることはできない。
     システムツールとして、Regedit.exeが用意されている。
    • ウインドホルダーのRegedit.exeをダブルクリックする。
    • スタート>ファイル名を指定して実行>C:\WINDOWS\Regedit.exeを参照>OK
    どちらかで、Regedit.exeを立ち上げる。



     立ち上げ画面。 6つのルートキーがある。 管理ファイルとの関係は
    • HKEY_CLASSES_ROOT
    • HKEY_CURRENT_USER     
    • HKEY_LOCAL_MACHINE   <-----system.dat
    • HKEY_USERS       <-----user.dat
    • HKEY_CURRENT_CONFIG
    • HKEY_DYN_DATA
     HKEY_DYN_DATAはRAM上の環境値を表現している。刻一刻と変化する環境を示す。 ここを編集することはない。
    • HKEY_LOCAL_MACHINE   <-----system.dat
    • HKEY_USERS       <-----user.dat
    この二つキーを編集すると、管理ファイルトも書換えられる。注意しよう。
    他の三つは、下記のような関連で、原本のコピーにあたる。

    • HKEY_CLASSES_ROOT     (B)のコピー
    • HKEY_CURRENT_USER     (C)のコピー
    • HKEY_LOCAL_MACHINE   <-----system.dat
    •  +Config     (A)
    •  +Enum
    •  +FileUserTree
    •  +hardware
    •  +Network
    •  +Security
    •  +SOFTWARE
    •      +
    •      +Classes (B)
    •      +
    •  +System
    • HKEY_USERS
    •  +Default   <--C:\WINDOWS\user.dat
    •  +us1 (C)<---C:\WINDOWS\system\Profile\us1\user.dat
    •  +us2    <---C:\WINDOWS\system\Profile\us2\user.dat
    • HKEY_CURRENT_CONFIG   <-----(A)のコピー

    ルートキー短縮形内容
    HKEY_CLASSES_ROOT HKCRファイルの種類と設定が格納されている。ファイルのアイコンや、右クリックメニューの設定が変更できる。CLSIDキーは、COMに関する情報。
    HKEY_CURRENT_USER HKCU 現在Windowsにログインしている「ユーザ」固有の設定。アプリケーション関連は、このSoftwareに格納されている。
    HKEY_LOCAL_MACHINE HKLM デバイスドライバや、接続されているハードウェアに関する設定。SOFTWAREにアプリケーションの設定、 Systemに起動時にロードするドライバの情報がある。SOFTWARE→Microsoft→Windows→CurrentVersion→Uninstallとインストール情報にたどり着く。
    HKEY_USERS . 全「ユーザ」の設定。アプリケーションは、この階層下の Softwareに格納されている。
    HKEY_CURRENT_CONFIG . ディスプレイ・プリンタ等のハードウェア情報。


      レジストリのキー


     レジストリの全体構造を理解したなら、個々のキーを見てみよう。

    キー   HKEY_CLASSES_ROOT\WScriptShell\CLSID
    値  {72C24DD5-D70A-438B-8A42-98424B88AFB8}
    キー   HKEY_CLASSES_ROOT\WScriptShell\CurVer
    値   WScript.Shell.1
     並んでShell オブジェクトのCLSIDとProgIDが定義されている。

    キー   HKEY_CLASSES_ROOT\WSHFile\DefaultIcon
    値   C:\WINDOWS\WScript.exe,1
    GUIベースのWScript.exeが既定値。コマンドライン用のCScript.exeではない。

    キー  HKEY_CLASSES_ROOT\WSHFile\ShellEx\DropHandler
    値   {60254CA5-953B-11CF-8C96-00AA00B8708C}
    このCLSIDは何を指しているか? Regeditの検索を利用して追跡。

    キー  HKEY_CLASSES_ROOT\CLSID\{60254CA5-953B-11CF-8C96-00AA00B8708C}
    値   Shell Extension For Windows Script Host
    キー  HKEY_LOCAL_MACHINE\Software\CLASSES\CLSID\{60254CA5-953B-11CF-8C96-00AA00B8708C}\InProcServer32
    値   C:\WINDOWS\SYSTEM\WSHEXT.DLL


     こうして、JSファイルに、選択ファイルをドラッグ&ドロップでスクリプトが実行されるのは、WSHEXT.DLLというActiveXコントロールが実現していることが分かった。

    HKEY_CLASSES_ROOTは、ファイル拡張子のリストで始まる。ここは、スタート>設定>フォルダオプション>ファイルの種類  でも確認できるが、レジストリのここと連動している。

    拡張子JSは、次のように設定されている。
    JScript スクリプト ファイル
    MS−DOSプロンプトで開く
      C:\WINDOWS\COMMAND\CScript.exe "%1" %*
    開く   
      C:\WINDOWS\WScript.exe "%1" %*

    JSファイルを、右クリックで、CScript.exeとWScript.exe を使い分けられる。 CScript.exe専用のExec メソッドは、右クリック>MS−DOSプロンプトで開く で実行するようになっている。

    JSファイルをダブルクリックは、「開く」に対応する。JSファイルに、選択ファイルをドラッグ&ドロップでも、このコマンドが実行される。そのため、引数が付けてある。

    Windowsスクリプトでは、引数は%0 %1 %2 %3 %4.....と表記される。
    %0には実行ファイルのフルパスが受け渡される。%1には、実行ファイルの名称が入る。 スペースが入る可能性があるので ” ” でくくる。
    %*は引数残り全部という意味。


      レジストリのバックアップ

     レジストリの操作の前にはバックアップをする。Win98では、システムツールとして、scanregw.exeが用意されている。
    • ウインドウホルダーのscanregw.exeをダブルクリックする。
    • スタート>ファイル名を指定して実行>C:\WINDOWS\scanregw.exeを参照>OK
    どちらかで、scanregw.exeを立ち上げる。

    万一の場合は、以下の手順で設定を戻す
    • パソコンの電源を入れたら[Ctrl]キーを押しっぱなしにする
    • “Startup Menu”が表示されたら“Command Prompt Only”を選択する
    • 入力可能状態になったら、“scanreg”と入力してから[Enter]キーを押す
    • 日付の一覧が表示されるので、その中で復元したいバックアップデータを選ぶ
    • 再起動を求められるので、再起動する


      ActieXコンポーネントとレジストリ

     ActieXコンポーネントは、レジストリの情報なくしては動かない。WSHの、これだけの情報を確認しただけで想像できる。では、ActieXコンポーネントを手動でインストールするには、どうするの?

     Regsvr32.exe で登録する。Regsvr32.exe は、コンポーネントの DllRegisterServer メソッドを呼び出し、Windows レジストリにエントリ セットを作成する。

     ActieXコンポーネントでないソフトは、MicrosoftWindowsのアプリケーソンとしては, 今や考えられない。ダウンロードしたソフトは、レジストリに書き込みをする。そのデータはinfファイルに記述してある。

     ActieXコンポーネントとレジストリの関係をマイクロソフトの技術文書から抜粋メモしておきます(原本http://support.microsoft.com/default.aspx?scid=kb;ja;183771)。

    HKEY_CLASSES_ROOT を展開すると、最初にファイル拡張子がリストされます。ファイル拡張子の後には、スペシャル キーで構成される ProgID (Programmatic Identifier) が続きます。
    通常 ProgID の名前には、コンポーネントの ClassID を見分けやすい名前が付けられています。ProgID の標準フォーマットは Application.Class.Version ですが、Version 部分はよく省略されています。ProgID の例としては、"Word.Application.8"、"Excel.Chart" などがあります。

    各 ProgID は ClassID を参照します。ClassID (または CLSID) のフォーマットは次の通りです。
         {xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx}

    HKEY_CLASSES_ROOT には、ProgID の他に AppID、CLSID、Component Categories、Interface、Licenses、TypeLib などスペシャル キーも定義されています。ActiveX コンポーネントに関する情報は、こうしたスペシャル キーのどれかに入力されます。

    レジストリで目的の ActiveX コンポーネントの参照を探す手順を示します。
    1. レジストリ エディタ (Regedit.exe) を起動します。
    2. まず最初に ProgID (Programmatic Identifier) を探します。ProgID は HKEY_CLASSES_ROOT 直下にあります。(例: MSComDlg.CommonDialog.1)
    3. 目的のコンポーネントの ProgID が見つかったら、キーを展開して CLSID サブキーを選択します。
    4. 右側のペインに、その ProgID に関連付けられている ClassID が表示されます。 (例: {F9043C85-F6F2-101A-A3C9-08002B2F49FB})
    5. [(標準)] 文字列アイコンをダブルクリックして [文字列の編集] ウインドウを表示して、[値のデータ] フィールドから ClassID 値をコピーします。
    6. HKEY_CLASSES_ROOT\CLSID を選択して展開します。以下のように ClassID で検索して探す。
    7. [編集] メニューから [検索] を選択します。先ほどコピーした ClassID 値を [検索する値] フィールドに貼りつけます。[次を検索] ボタンをクリックすると、該当する値が検出されます。
    8. 検出されたサブキーを展開します。このとき、InprocServer32、ProgID、TypeLib、Version がサブキーとして表示されます。
    9. TypeLib サブキーを選択し、右側ペインの [(標準)] 文字列アイコンをダブルクリックして [文字列の編集] ウインドウを表示し、TypeLibID の値のデータをコピーします。
    10. HKEY_CLASSES_ROOT\TypeLib を選択して展開します。
    11. [編集] メニューから [検索] を選択します。先ほどコピーした TypeLibID 値を [検索する値] フィールドに貼りつっけたら、[次を検索] ボタンをクリックします。検出されたサブキーには、コンポーネントの Type Library に関する情報が格納されています。
    12. HKEY_CLASSES_ROOT\Interface を選択して展開します。
    13. [編集] メニューから [検索] を選択します。先ほどコピーした TypeLibID 値を [検索する値] フィールドに貼りつ桁ら、[次を検索] ボタンをクリックします。
    14. これまでの検索と違い、Interface サブキーでの検索は、TypeLibID とその TypeLibID に関連付けられている InterfaceID に対して行われるため、検索結果が一対多となります。そのため InterfaceID をすべて検索するには、[次を検索] ボタンをクリックするか [F3] キーを押してください。


     ActieXコンポーネントのレジストリを読む。

     上の1〜14の手続きは、 ActieXコンポーネントの環境を確認する作業。これを自動化するスクリプトが書けないだろうか。

    Windowsスクリプトの司令塔,C:\WINDOWS\WScript.exeを例に考えてみよう。

    ProgID      WScript.Shell.1
    ClassID (CLSID)  {72C24DD5-D70A-438B-8A42-98424B88AFB8}


    • Implemented Categoriesキー・・・・コンポーネントをカテゴリとして識別するIDキー
         Automation Objects
    • InprocServer32・・・・・・・・登録されたCOMの実行モジュールへのパス
         C:\WINDOWS\WScript.exe
    • ProgID・・・・・・・・・・・プログラムを識別するIDキー
         WScript.Shell.1
    • TypeLib・・・・・・・・・・・タイプライブラリの識別
         {F935DC20-1CF0-11D0-ADB9-00C04FD58A0B}


     C:\WINDOWS\SYSTEM\WSHOM.OCX。このActiveXコントロールが、インターフェイス定義情報をプログラムに提供する。この情報を使うインターフェイスは多い。
     WSHOM.OCXは、WindowsOSの中核Shellの情報を、アプリケーションに部品として提供している。

     ProgIDから逆引きでCLSIDを取得して、コンポーネントを探る。有名ProgIDは、
    • Word.Application
    • Excel.Application
    • InternetExplorer.application
    • WScript.Shell
    • Scripting.FileSystemObject
     ProgIDをキーワードとして、「検索」をフル活用で探るが、データ欄のCLSIDを コピー&ぺーストの際、誤ってカットしてしまう危険性がある。
     そこで、TypeLibのCLSIDを読み取るスクリプトを書けば、
    var WShell = WScript.CreateObject("Wscript.Shell"); a = WShell.RegRead("HKCR\\CLSID\\{72C24DD5-D70A-438B-8A42-98424B88AFB8}\\TypeLib\\"); WScript.Echo(a);

     次ぎに、HKCR\TypeLib\a\の内容を調べる。

    var WShell = WScript.CreateObject("Wscript.Shell"); a = WShell.RegRead("HKCR\\CLSID\\{72C24DD5-D70A-438B-8A42-98424B88AFB8}\\TypeLib\\"); WScript.Echo(a); b = WShell.RegRead("HKCR\\TypeLib\\"+a+"\\"); WScript.Echo(b);
     このキーにエントリー(名前:値)があればいいのだが、さらに下に階層化されていると、そのキーを知らなければならない。なぜかといえば、存在しないキーにはエラー終了が帰ってくる。レジストリーを読むコマンドはあるがあるし、読み取ることでレジストリは壊れない。ただ、キーの構造が分かってないと、うまいスクリプトは組めない。あくまで、スクリプトはエンジニアの補助手段。

    ただ、次ぎの事が分かる。Win98の場合だが、HKEY_CLASSES_ROOTには、
    •  WIN.INI時代の情報とActiveXコントロールの情報が混在している。
    • CLASSES_ROOTは、COMの情報データベース。
    • WScript.CreateObjectに必要なProgID、ここを検索すれば確認できる。
    • オブジェクトの実体は、InprocServer32キーで確認できる。
    • インターフェイスの関連も、TypeLibキーから確認できる。

    この情報で、ActiveXコントロールのオブジェクト、プロテイ、メソットを表示してくれるのが、MicroSoft Officeの「オブジェクトブラウザ」だろう。

     さて、アンタッチャブルといわれるレジストリーだが、親しむとWindousの仕組みが分かってくる。

    • 1987年MSDOSにクリップボード
    • 1992年Windows3.1 アプリケーション連携 OLE
    • 1995年Windows95 COM
    • 1997年Windows98 DCOM
    • 1999年WindowsME COM+
     COM+では、オブジェクト間の呼び出し規則をバイナリ・レベルで定めているため、原理的にはプログラミング言語を選ばない。 C++、Visual Basic、Java、VBA、VBScript、JScriptなどの言語がCOM+をサポートしている。
     スクリプト言語の場合には、オートメーションを使用することで、実行時にインターフェイス情報を取得して動的にオブジェクトを呼び出す。
     タイプライブラリは、インターフェイス定義情報を実行時にプログラムに提供するバイナリ・ファイルで、すべてのプログラミング言語からアクセスできる統一フォーマットを導入している。
     オートメーションは、通常IDispatchと呼ばれるインターフェイスをオブジェクトが実装することによって実現される。IDispatchインターフェイスには、オブジェクトのインターフェイス情報を取得するためのGetTypeInfoメソッドが用意されており、クライアントは事前の知識なしに、オブジェクトの任意のメソッドを呼び出すことが可能。

     こんなプログラム技術情報も、レジストリを読むと理解できるような気分になる。

     レジストリのキーを消す。

     IEのセキュリテイ設定により、安全マーク(safe for scripting)が付いているときだけ、スクリプトからActiveXコントロールを呼び出せるようにできる。

     ActiveXコントロールに安全マークがつくのは、HKEY_CLASSES_ROOT\CLSIDの「Implemented Categories」に、「7DD95801-9882-11CF-9FA9-00AA006C42C4」というキーがある場合。

    var ClassId = "{06290BD5-48AA-11D2-8432-006008C3FBFC}"; var safely = "{7DD95801-9882-11CF-9FA9-00AA006C42C4}"; var WShell = WScript.CreateObject("Wscript.Shell"); a = WShell.RegRead("HKCR\\CLSID\\"+ClassId+"\\ProgID\\"); b = WShell.RegRead("HKCR\\CLSID\\"+ClassId+"\\InprocServer32\\"); WScript.Echo(a+" "+b); c=WShell.RegRead("HKCR\\CLSID\\"+ClassId+"\\Implemented Categories\\"+safely+"\\"); WScript.Echo(ClassId+"  safely scriptable");
      ClassIdに値をセットして、このスクリプトを実行する。

     安全マークがついていれば、safely scriptableと表示する。「Implemented Categories」が無かったたり、 Categoryが{7DD95801-9882-11CF-9FA9-00AA006C42C4}でなければスクリプトエラー表示となる。

     上のClassIdをもつActiveXコントロールはScriptlet.Typelib。Microsofは、1999年にこれの安全マークを取り外した(thttp://www.microsoft.com/technet/security/bulletin/fq99-032.mspx)。
     もし、ProgIDにScriptlet.Typelibと表示し、safely scriptableと表示したら、 安全マーク付のまま。IEをバージョンアップすれば取り外されるのだが、 手動でImplemented Categoriesのキーを削除する。

    var ClassId = "{06290BD5-48AA-11D2-8432-006008C3FBFC}"; var safely = "{7DD95801-9882-11CF-9FA9-00AA006C42C4}"; var WShell = WScript.CreateObject("Wscript.Shell"); a = WShell.RegRead("HKCR\\CLSID\\"+ClassId+"\\ProgID\\"); b = WShell.RegRead("HKCR\\CLSID\\"+ClassId+"\\InprocServer32\\"); WScript.Echo(a+" "+b); WShell.RegWrite("HKCR\\CLSID\\"+ClassId+"\\Implemented Categories\\"+ safely+"\\"); WScript.Echo(ClassId+"  safely scriptable");
       試に、安全マークをつけてみる。

    var ClassId = "{06290BD5-48AA-11D2-8432-006008C3FBFC}"; var safely = "{7DD95801-9882-11CF-9FA9-00AA006C42C4}"; var WShell = WScript.CreateObject("Wscript.Shell"); a = WShell.RegRead("HKCR\\CLSID\\"+ClassId+"\\ProgID\\"); b = WShell.RegRead("HKCR\\CLSID\\"+ClassId+"\\InprocServer32\\"); WScript.Echo(a+" "+b); WShell.RegDelete("HKCR\\CLSID\\"+ClassId+"\\Implemented Categories\\");
       安全マークを消しておく。

       最近、スパイウエアが話題になっている。レジストリを操作して迷惑プログラムを起動させる。

    HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run
    HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\RunOnce
    HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run
    HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce

     これらのレジストリキーに登録されるプログラムは、システムの起動に自動実行される。オペレーティングシステムが自動実行させているものや、アプリケーションによる登録もある。一度点検しておく。
     不都合が発生した時、覚えのないプログラムがあれば削除してみる。

    HKEY_CLASSES_ROOT\comfile\shell\open\command
    HKEY_CLASSES_ROOT\exefile\shell\open\command
    HKEY_CLASSES_ROOT\piffile\shell\open\command

     このキー内のエントリが変更されて、別プログラムが実行されたケースもある。
     OSをクリアする事態の前に、このレジストリの値をDeleteして様子を観察して見るのは無駄でない場合が多い。スパイウエアも、ソフトウエア。きっかけがないと動かないものである。


    2005.03.29
    by Kon