空想犬猫記

※当日記では、犬も猫も空想も扱っておりません。(旧・エト記)

JavaScript(JScript)で自動化するための準備とインテリセンスを利用するためのメモ

背景

今まで業務で使用する自動化をRubyで書き溜めてきたものの、Windows上のRubyというのは敷居が高くいまいち手離れが悪いので、Windowsに標準でインストールされているWSHスクリプトで書き直そうとしている。WSHスクリプトはクライアントとして複数の言語をサポートしているので、近頃の流行を考えて、JavaScript互換のJScriptを利用する方針でいく。

JScriptとは何か?

MicrosoftによるJavaScript (ECMAScript) 互換の言語のこと。どんな目的で使うかによって(どのランタイムで走らせるかによって)使えるオブジェクトが違ったり、必要なリファレンスが違ってくるので注意が必要。大きく分けて

  1. ブラウザ上、Internet Explorer
  2. .NETランタイム上
  3. Wscript/Cscript上

の3つの使われ方がある。ドキュメントを漁るうえで、この区別を知らないと途端に道に迷ってしまう。1番目は説明の必要は無いだろう。2番目は昔はJScript.NETと呼ばれていた気がするが、今は単にJScriptと呼ばれているようだ。

今回やりたいことは3番目で、WSHJavaScriptで利用することである。なので.NET向けのJScriptドキュメントに迷い込んではいけない。Web開発者向けのブラウザ向けのドキュメントも違う。本当に必要なのはWindows Scripting Hostのページ。(ここでも思わず『どうしてこうなった』と思ってしまうくらい回りくどい日本語に辟易する。

WSH環境下でのJavaScript

最初のとっつきにくさはあったものの、WSHJavaScriptで利用するさいに押さえるべきポイントは以下の通り。

  1. ブラウザ上のJavaScriptがdocument/windowsオブジェクトありきであったのと同じように、WSH環境下ではWScriptオブジェクトが最初から存在している。WScriptのサブコンポーネントやCOMを利用する際は、全てこのオブジェクトを介して行う。Web上のサンプルでは new ActiveXObject(...) を使っているサンプルもあり、動いてしまうのだけれど、WScript.CreateObject を使うのが正統派。
  2. WScript以下のサブコンポーネントはWScript.CreateObjectを使って利用する。
  3. COMオブジェクトのプロパティ、メソッドは大文字小文字区別がない(変なの)。
  4. JavaScriptそのものには外部ファイルをインクルードする機能がないので、.wsfファイルとして保存する。(参照:id:language_and_engineering:20090125:1232940498)

開発環境

開発環境はVisual Studioがベストと思われる。ただしインテリセンスはInternet Explorer上の開発を想定しているためか、WScriptオブジェクトのメンバは見えない。そこで、以下のようなスタブを作り(作りかけ)、Visual StudioReference Directiveという機能を使い、インテリセンスにオブジェクトの情報を教えてあげる。

スタブファイル:WshStub.js
WshNamed = {
    Item:               function (key) { return null; },
    length:             -1,
    Count:              -1,
    Exists:             function (key) { return false; }
};

WshUnnamed = {
    Item:               function (index) { return null; },
    length:             -1,
    Count:              -1
};

WshArguments = {
    Item:               function (natIndex) { },
    length:             -1,
    Count:              -1,
    Named:              WshNamed,
    Unnamed:            WshUnnamed
};


WshShell = {
    CurrentDirectory:   "",
    Environment:        null,
    SpecialFolders:     null
};

TextStream = {
    Close:              function () { },
    Read:               function (numCharacters) { },
    ReadAll:            function () { },
    ReadLine:           function () { },
    Skip:               function (numCharacters) { },
    SkipLine:           function () { },
    Write:              function (string) { },
    WriteBlankLines:    function (numLines) {Z },
    WriteLine:          function (string) { },
    AtEndOfLine:        false,
    AtEndOfStream:      false,
    Column:             -1,
    Line:               -1
};

WScript = {
    Arguments:          WshArguments,
    Interactive:        false,
    FullName:           "",
    Name:               "",
    Path:               "",
    ScriptName:         "",
    StdErr:             TextStream,
    StdIn:              TextStream,
    StdOut:             TextStream,
    Version:            0,
    CreateObject:       function (strProgID, strPrefix) { },
    ConnectObject:      function (strObject, strPrefix) { },
    DisconnectObject:   function (obj) { },
    Echo:               function (args) { },
    GetObject:          function (strPathName, strProgID, strPrefix) { },
    Quit:               function (intErrorCode) { },
    Sleep:              function (intTime) { }
};
JavaScriptファイル:Sample.js
/// <reference path="WshStub.js"/>
WScript.Echo("Test De Gonsu");
for (x in WScript.Arguments) {
  WScript.Echo(x);
}
WScript.Echo("Sleep Suru De Gonsu");
WScript.Sleep(100);
WScript.Echo("Motto Sleep Suru De Gonsu");
WScript.SleeP(500);
WScript.Echo("Sleep Shita De Gonsu");

ダブルクリックで実行するときには最初の行はコメントとして無視される。

スクリプトの規模が多くなると、スクリプトを複数ファイルに分割したくなるが、残念ながらJavaScript自体には外部ファイルをインクルードする機能はない。

しかし、HTMLで複数のJavaScriptをインポートできるのと同様、wsfファイルに複数のjsファイルをリストアップすることにより目的を達することが出来る。

起動ファイル:Launch.wsf
<package>
  <job>
    <script language="JavaScript" src="SomeLibrary.js"></script>
    <script language="JavaScript" src="Sample.js"></script>
  </job>
</package>

先に書いたとおり、COMオブジェクトのプロパティおよびメソッド名は大文字小文字の区別がない。好みに合わせてcamelCaseやCamelCaseでStubを書き直せば、自分好みのコンベンションでインテリセンスを使ってコーディングできるようになる。

おしまい