WSH/HTA HOLIC

Windows Scripting Host(WSH)/HTML Application(HTA)の色々なサンプルを載せてみます。

トップページへ


WSHって何ができるの?

WSHでは、バッチファイル(.bat)でやれる事もできます。それに加えて、以下の特徴があります。


HTAって何ができるの?

HTAでは、ブラウザを使って、WSHにUIをかぶせる事ができます。


WSH: ファイル名の一覧を作成する

以下のスクリプトは、Drag&Dropされた複数のファイル名を列挙したテキストファイルを作成し、関連づけられたアプリケーションで開きます。「送る」(SendTo)に置いて使用します。

// FilenameToText.wsf

<job id="FilenameToText">
<script language=JScript>
// ShellとFileSystemを作成しておきます
var sh=WScript.CreateObject("WScript.Shell");
var fso=new ActiveXObject("Scripting.FileSystemObject");

var i;
// 格納するテキストファイルを作業用フォルダに作成します
var ofilename=sh.ExpandEnvironmentStrings("%TEMP%")+'\\filename.txt';
var ofileobj=fso.CreateTextFile(ofilename, true);

// まず、ファイル名だけをリストアップします
for(i=0; i<WScript.Arguments.Length; i++)
  ofileobj.WriteLine(fso.GetFileName(WScript.Arguments(i)));

// 1行あけて
ofileobj.WriteLine('');

// フルパス(D&Dされたまま)をリストアップします
for(i=0; i<WScript.Arguments.Length; i++)
  ofileobj.WriteLine(WScript.Arguments(i));

ofileobj.Close();
// テキストファイルを関連づけを利用して開きます
sh.Run('rundll32.exe url.dll,FileProtocolHandler '+ofilename, 5);
</script>
</job>

WSH: Excelを操作する

Excelを起動して、色のインデックス一覧を作成します。

// CreateColorIndex.wsf

<job id="CreateColorIndex">
<script language=JScript>
var excel = new ActiveXObject("Excel.Application");
excel.Visible = true;
excel.Workbooks.Add
var sheet=excel.ActiveSheet
var i;
for(i=1; i<57; i++) {
    sheet.Cells(i,1)=i;
    sheet.Cells(i,2).Interior.ColorIndex=i;
};
</script>
</job>

WSH: ふたつのジョブを動かす

以下のスクリプトは、ふたつのジョブを動かす例です。

  1. UIジョブは実行許可を示すファイルを作成し、Workerジョブを起動します。
  2. Workerジョブは、仮に、実行許可ファイルが存在する間、エクセルの表示、非表示を繰り返すものとしています。
  3. UIジョブはメッセージボックスを出し、ユーザからの終了要求を待ちます。
  4. ユーザが終了要求したら、UIジョブは実行許可ファイルを削除し、Workerジョブが終了するのを待ちます。
// DualJob.wsf

<package>
<job id="UI">
<script language=JScript>
    var fs=new ActiveXObject("Scripting.FileSystemObject"); // filesystem
    var ScriptMutex=WScript.ScriptFullName+'.mtx';          // 実行許可を示すファイルのファイル名
    var file=fs.CreateTextFile(ScriptMutex); file.Close();  // それを作成し、すぐに閉じる
    var sh=WScript.CreateObject("WScript.Shell");           // shell
    var aScript=sh.Exec('wscript.exe "'+WScript.ScriptFullName+'" //Job:Worker'); // Workerジョブを起動
    var moduleName=fs.GetBaseName(WScript.ScriptFullName);
    sh.Popup('ボタンを押すと自動実行を終わります。', 0, moduleName, 0+64);
    fs.DeleteFile(ScriptMutex);                             // 終了させるために許可ファイルを削除
    while(aScript.Status==0) WScript.Sleep(100);            // 実際の終了を待ちます
    sh.Popup('終了しました。', 0, moduleName, 0+64);
</script>
</job>

<job id="Worker">
<script language=JScript>
    var fs=new ActiveXObject("Scripting.FileSystemObject"); // filesystem
    var ScriptMutex=WScript.ScriptFullName+'.mtx';          // 実行許可を示すファイルのファイル名

    var excel = new ActiveXObject("Excel.Application");
    while(fs.FileExists(ScriptMutex)) {                     // 許可ファイルがある間、実行する
        excel.Visible       = true;
        WScript.Sleep(2000);
        excel.Visible       = false;
        WScript.Sleep(2000);
    }
    excel.Quit();
</script>
</job>
</package>

WSH: ウインドウにキーを送る

以下のスクリプトは、画面の解像度を6秒ごとに切り替えます。画面のプロパティの詳細タブを出した状態から動かします。

// autoResolutionSwitch.wsf

<package>
<job id="UI">
<script language=JScript>
    var fs = new ActiveXObject("Scripting.FileSystemObject"); // filesystem
    var ScriptMutex=WScript.ScriptFullName+'.mtx';            // 実行許可を示すファイルのファイル名
    var file=fs.CreateTextFile(ScriptMutex); file.Close();    // それを作成し、すぐに閉じる
    var sh=WScript.CreateObject("WScript.Shell");             // shell
    var aScript=sh.Exec('wscript.exe "'+WScript.ScriptFullName+'" //Job:Worker'); // Workerジョブを起動
    var moduleName=fs.GetBaseName(WScript.ScriptFullName);
    sh.Popup('ボタンを押すと自動実行を終わります。', 0, moduleName, 0+64);
    fs.DeleteFile(ScriptMutex);                               // 終了させるために許可ファイルを削除
    while(aScript.Status==0) WScript.Sleep(100);              // 実際の終了を待ちます
    sh.Popup('終了しました。', 0, moduleName, 0+64);
</script>
</job>

<job id="Worker">
<script language=JScript>
    // 一般的な初期化処理
    var fs = new ActiveXObject("Scripting.FileSystemObject"); // filesystem
    var sh = WScript.CreateObject("WScript.Shell");           // shell
    var ScriptMutex=WScript.ScriptFullName+'.mtx';            // 実行許可を示すファイルのファイル名
    function WaitInMutex(wait_time)                           // ScriptMutexがある間は待つ(単位100ms)
    {
        for(wait_time=(wait_time+99)/100; wait_time>0 && fs.FileExists(ScriptMutex); wait_time--)
            WScript.Sleep(100);
        return fs.FileExists(ScriptMutex);
    }
    function SendKeysWnd(keys, active_window)                 // アクティブにしてキーを送る
    {
        if(active_window!="") {
            sh.AppActivate(active_window);
            WScript.Sleep(200);                               // これが短いとキーがドロップする
        }
        sh.SendKeys(keys);
    }
    // このスクリプト専用のdefine部分
    var WindowTitle1="画面のプロパティ";                      // アクティブにするウインドウ文字列
    var ChangeWait1=3000;                                     // 変更→Yes/Noメッセージ待ち時間
    var ChangeWait2=3000;                                     // Yes/Noメッセージ→次の待ち時間
    var reso=0;
    while(fs.FileExists(ScriptMutex)) {                       // 許可ファイルがある間、実行する
        // 解像度を変更
        SendKeysWnd("%S", WindowTitle1);                      // ALT+Sで画面の解像度にフォーカス
        SendKeysWnd("{LEFT 10}", "");                         // 最小解像度へ
        if(reso) SendKeysWnd("{RIGHT}", "");                  // 最小解像度の右へ
        reso=1-reso;
        SendKeysWnd("%A", "");                                // ALT+Aで適用
        if(!WaitInMutex(ChangeWait1)) break;                  // 確認メッセージを待つ
        // 確認メッセージへ応答し、残り時間を待つ
        SendKeysWnd("y", "");                                 // [y]で確認
        if(!WaitInMutex(ChangeWait2)) break;                  // 残り時間を待つ
    }
</script>
</job>
</package>

WSH: 正規表現を使う

以下のスクリプトは、InternetExplorerを使って、htmlファイルやwebコンテンツをテキストに変換します。正規表現の処理速度はあまり速くないので、大量の正規表現処理をする時に、Perlをインストールしてもかまわないなら、そちらを使用した方がよいです。

このスクリプトのオリジナルは、SONYのinfoCarry向けにテキスト変換するために作成した物です。

// Html2Text.wsf

<job id="html converter">
<script language=JScript>
// htmlをplain textに変更
// in: htmlの入った配列の参照 / out: 文字列
function html2text(contents)
{
    var inpre=0;                                                            // pre要素中
    var incomment=0;                                                        // コメント中
    var comma=0;                                                            // thのあるテーブル中
    var hr="_______________________________________";                       // hrの実際の文字
    var i;
    var all;
    var t;                                                                  // temporary string
    var cst,cend;                                                           // コメントの開始終了ポイント
    i=0;
    while(i<contents.length) {
        t=contents[i];
        if(!incomment) {                                                    // 非コメント期間中ならコメントの開始を検索
            cst=t.indexOf('<!--');
            if(cst!=-1) {                                                   // コメントの開始が見つかった
                cend=t.indexOf('-->',cst);                                  // 行内でコメントが終了するか?
                if(cend==-1) {                                              // いいえ、複数行コメントでした
                    t=t.substr(0,cst);                                      // 行末まで削除
                    incomment=1;                                            // コメント期間中へ
                } else {                                                    // 1行内でコメントが終了するので
                    contents[i]=t.substr(0,cst)+t.substr(cend+3);           // その部分を削除
                    continue;                                               // その行で次のコメントを探す(i++しない)
                }
            }
        } else {                                                            // コメント期間中なのでコメント終了を探す
            cend=t.indexOf('-->');
            if(cend==-1) {                                                  // まだ終了が見つからないので
                t='';                                                       // この行は削除
            } else {                                                        // 終了が見つかったので
                contents[i]=t.substr(cend+3);                               // コメントまでを除去
                incomment=0;                                                // 非コメント中へ
                continue;                                                   // この行のコメント開始を探す(i++しない)
            }
        }
        if(t.search(/<pre[^>]*>/i)>=0) inpre=1;
        if(t.search(/<\/pre>/i)>=0) inpre=0;
        if(inpre==1) t+='\n';                                               // 通常は行末の改行は不要
        if(t.search(/<table/i)>=0) comma=0;                                 // 通常のテーブルは,をつけないが
        if(t.search(/<th/i)>=0) comma=1;                                    // ヘッダつきの場合は,をつける
        if(comma) t=t.replace(/<\/td>/ig,'</td>,');
        if(comma) t=t.replace(/<\/th>/ig,'</th>,');
        t=t.replace(/<td[^>]*>[  ]*<\/td>/ig,'');                          // テーブルのプレースホルダとしての空白は削除
        t=t.replace(/<\/tr>/ig,'');                                         // trは余分な改行を発生するので削除
        t=t.replace(/<img[^>]*alt=([^ ]*)[^>]*>/ig,'[画像: $1]');           // 画像のaltは空白で終了(""で囲まれていない)
//      t=t.replace(/<title>[^<]*<\/title>/ig,'');                          // bodyにtitleは無いので不要
        t=t.replace(/<h1[^>]*>/ig,'\n■■■');                              // headingは見やすく整形する
        t=t.replace(/<\/h1>/ig,'■■■\n');
        t=t.replace(/<h2[^>]*>/ig,'\n■■■');
        t=t.replace(/<\/h2>/ig,'■■■\n');
        t=t.replace(/<h3[^>]*>/ig,'\n■■■');
        t=t.replace(/<\/h3>/ig,'■■■\n');
        t=t.replace(/<hr[^>]*>/ig,'\n'+hr+'\n');                            // hrを実際の文字列に変換
        t=t.replace(/<\/?(p|br|div|h\d|dt|dd|li|tr|table)[^>]*>/igm,'\n');  // ブロック要素のタグは改行に変換
        t=t.replace(/<\/?[^>]*>/g,'');                                      // 非ブロック要素のタグを除去
        t=t.replace(/&lt;/ig,'<');                                          // &xxx;を実体に戻す
        t=t.replace(/&gt;/ig,'>');
        t=t.replace(/&nbsp;/ig,' ');
        t=t.replace(/&quot;/ig,'"');
        t=t.replace(/&amp;/ig,'&');                                         // &amp;は最後に戻す
        contents[i]=t;
        i++;
    }
    contents.push("\n");                                                    // 最後に空行を補っておく
    all=contents.join('');
    all=all.replace(/[  ]+\n/g,'\n');                                      // 行末の空白を削除
    all=all.replace(/\n[ ]+/g,'\n');                                       // 行頭の全角スペースを削除
    all=all.replace(/ /g,' ');                                             // 全角スペースを半角スペースに
    all=all.replace(/\n\n+/g,'\n\n');                                       // 複数の空行は1行の空行に
    all=all.replace(/\n+$hr\n+/g,'\n'+hr+'\n\n');                           // hrの前の空行は不要
    all=all.replace(/^\n*/,'');                                             // 全体の最初の空行を削除
    all=all.replace(/\n+$/,'\n');                                           // 全体の最後の空行はひとつに
    return all;
}

// IEを使ってbody要素を取り出す
// in : url, timeout[ms], offline(0:接続可能/1:offline), textflag(0:html/-:text)
// ret: RetrieveUrlBody オブジェクト
// prop. html(html/text文字列) title(タイトル) modified(lastModified)
function RetrieveUrlBody(url,timeout,offline,textflag)
{
    var i,ie;
    ie=new ActiveXObject('InternetExplorer.Application');
    ie.Visible=0;
    ie.Offline=offline;
    ie.Silent=1;
    ie.Navigate2(url,2);                                                    // historyに登録しない
    for(i=0;i<Math.floor(timeout/100);i++) {
        if(!ie.Busy) break;
        WScript.Sleep(100);
    }
    this.html=(textflag==0) ? ie.Document.body.innerHTML : ie.Document.body.innerText;
    this.title=ie.Document.title;
    this.modified=ie.Document.lastModified;
    ie.Quit();
    return this;
}

// IEで開けるか確認(ショートカットはターゲットに変換)
function NormalizedURL(url)
{
    var sh=WScript.CreateObject("WScript.Shell");                           // 全体で使うshellobject
    if(url.search(/\.html?$/i)>=0) {                                        // htm/htmlファイル
        return url;
    } else if(url.search(/\.(url|lnk)$/i)>=0) {                             // url/lnkファイル
        url=sh.CreateShortcut(url).TargetPath;
        return url;
    }
    return '';
}

// ファイル名に適さない文字を_に変換
function NormalizedFilename(filename)
{
    return filename.replace(/[\\\/:,;\*\?"<>|]/g,'_');
}

// --- プログラムの開始 ---
if(WScript.Arguments.Length==0) {
    WScript.Echo('Drag&Dropで使用してください');
    WScript.Quit(1);
}
var url=NormalizedURL(WScript.Arguments(0));
if(url=='') {
    WScript.Echo('対象がhtmlファイルかリンクではありません');
    WScript.Quit(1);
}

var ie=WScript.CreateObject('InternetExplorer.Application');                // 経過表示用のブラウザ
ie.Visible=1;
ie.Offline=1;
ie.Silent=1;
ie.Navigate2('about:blank',2);                                              // historyに登録しない
ie.Document.clear();
ie.Document.write('<h1>HTML to Text Converter</h1><br>');
ie.Document.write('変換を開始しました<br>');

var fso=new ActiveXObject("Scripting.FileSystemObject");                    // 全体で使うFileSystemObject
var sh=WScript.CreateObject("WScript.Shell");                               // 全体で使うshellobject
var body=RetrieveUrlBody(url,5000,1,0);
var contents=body.html.split("\r\n");
var ofilename=sh.ExpandEnvironmentStrings("%TEMP%")+'\\'+NormalizedFilename(body.title)+'.txt';
var ofile=fso.CreateTextFile(ofilename, true);
ofile.WriteLine('URL: '+url);
ofile.WriteLine('タイトル: '+body.title);
ofile.WriteLine('最終更新: '+body.modified);
ofile.WriteLine('');
ofile.Write(html2text(contents));
ofile.Close();
ie.Document.write('変換を終了しました<br>');
WScript.Sleep(1000);
ie.Quit();
sh.Run('rundll32.exe url.dll,FileProtocolHandler '+ofilename, 5);
</script>
</job>

HTA: Excelを操作する

以下のスクリプトは、HTAでExcelを操作する例です。

  1. span要素にIDを設定し、2つのボタンオブジェクトと関連づけます。
  2. [開始]ボタンをクリックすると、Excelオブジェクトを作成すると共に、タイムアウトを設定します。
  3. タイムアウト毎にExcelの表示、非表示を切り替え、次のタイムアウトを設定します。
  4. [終了]ボタンをクリックすると、Excelを終了し、オブジェクトを削除します。
// excel.hta

<html>
<head>
<title>AutoExcel</title>
<style type="text/css">
.btnUpEn {border-style:outset; border-width:2px; padding:2px; color: BUTTONTEXT; cursor:hand}
.btnUpDis{border-style:outset; border-width:2px; padding:2px; color: GRAYTEXT}
.btnDn   {border-style:inset;  border-width:2px; padding:2px; color: BUTTONTEXT; cursor:hand}
</style>
<script language=JScript>
function CBtn(btnName, clickFunc, enable) // ボタンクラス
{
    this.dn=0;                            // mouseoutしても覚えておくため(0, 1)
    this.en=enable;                       // ボタンの有効無効(0, 1)
    this.s='document.all.'+btnName;
    this.click=clickFunc;
    function UpdtUpBtn(btn){eval(btn.s).className=btn.en ? "btnUpEn":"btnUpDis";};
    this.down =function Down(){
        window.event.cancelBubble=true;
        if(this.en){
            this.dn=1;
            eval(this.s).className="btnDn";
        }
    };
    this.up   =function Up(){
        window.event.cancelBubble=true;
        this.dn=0;
        UpdtUpBtn(this);
    };
    this.out  =function Out(){
        window.event.cancelBubble=true;
        UpdtUpBtn(this);
    };
    this.move =function Move(){
        window.event.cancelBubble=true;
        if(this.dn) {
            if(window.event.button) eval(this.s).className="btnDn";
            else this.dn=0;
        };
    };
    this.enable= function Enable(enable){
        if(enable!=this.en){
            this.en=enable;
            UpdtUpBtn(this);
        }
    }
}

var stat=0;    // このプロシジャの状態
var visible=0;
var excel;
var TimerID=0;
function TimeOutProc(proc)
{
    switch(proc) {
    case 0:  // 初期化要求
        if(stat) break;
        excel=new ActiveXObject("Excel.Application");
        excel.Visible=true;
        visible=1;
        stat=1;
        TimerID=window.setTimeout("TimeOutProc(1)", 2000);
        break;
    case 1:  // タイムアウト発生
    // 続く
    default:
        if(!stat) break;
        visible=1-visible;
        excel.Visible=visible ? true:false;
        TimerID=window.setTimeout("TimeOutProc(1)", 2000);
        break;
    case -1: // 終了要求
        if(!stat) break;
        excel.Visible=true;
        excel.Quit();
        delete excel;
        stat=0;
        window.clearTimeout(TimerID);
        break;
    }
}

function B1Click(){
    b1.enable(0);
    b2.enable(1);
    TimeOutProc(0); // 開始要求
}
function B2Click(){
    b1.enable(1);
    b2.enable(0);
    TimeOutProc(-1); // 終了要求
}

var b1=new CBtn('B1', B1Click, 1);
var b2=new CBtn('B2', B2Click, 0);

function btnUpAll() // 全てのボタンをUPに
{
    b1.up();
    b2.up();
}

window.resizeTo(160, 70);
function InitialUpdate(){
    btnUpAll();
}
</script>
<hta:application border=dialog borderstyle=normal caption=yes contextmenu=no innerborder=no
 scroll=no minimizebutton=no maximizebutton=no selection=no windowstate=normal icon="" />
</head>
<body onload="btnUpAll()" onmouseup="btnUpAll()" onmousedown="btnUpAll()">
<span id="B1" onmousedown="b1.down()" onmouseout="b1.out()"
 onmousemove="b1.move()" onclick="b1.click()"> 開始 </span>
&nbsp;&nbsp;
<span id="B2" onmousedown="b2.down()" onmouseout="b2.out()"
 onmousemove="b2.move()" onclick="b2.click()"> 終了 </span>
</body>
</html>

HTA: Transitionのデモ

以下のスクリプトは、HTAでTransitionをデモする例です。

// transition.hta

<html>
<head>
<title>TransitionDemo</title>
<script language=JScript>
window.resizeTo(512, 416);
var i;
var s="<font color=red>■</font><font color=green>■</font><font color=blue>■</font>";
var txt="";
for(i=0; i<7; i++) {
  txt+=s;
  txt+=s;
  txt+="<br>";
}
function chgT(){
  targ.innerHTML="";
  targ.filters.revealTrans.transition=fList.options[fList.selectedIndex].value;
  targ.filters.revealTrans.Apply();
  targ.innerHTML=txt;
  targ.filters.revealTrans.Play();
}
</script>
<hta:application border=dialog borderstyle=normal caption=yes contextmenu=no innerborder=no
 scroll=no minimizebutton=no maximizebutton=no selection=no windowstate=normal icon="" />
</head>
<body style="font-size: 12pt;padding: 0">
<font color=blue><b>Revealtrans demonstration</b></font>
<table border=0 width=100%><tr>
<td width=1>
<select id="fList" SIZE="24" onchange="chgT()" style="background: lightblue">
<option value= "0">Box in
<option value= "1">Box out
<option value= "2">Circle in
<option value= "3">Circle out
<option value= "4">Wipe up
<option value= "5">Wipe down
<option value= "6">Wipe right
<option value= "7">Wipe left
<option value= "8">Vertical blinds
<option value= "9">Horizontal blinds
<option value="10">Checkerboard across
<option value="11">Checkerboard down
<option value="12">Random dissolve
<option value="13">Split vertical in
<option value="14">Split vertical out
<option value="15">Split horizontal in
<option value="16">Split horizontal out
<option value="17">Strips left down
<option value="18">Strips left up
<option value="19">Strips right down
<option value="20">Strips right up
<option value="21">Random bars horizontal
<option value="22">Random bars vertical
<option value="23">Random
</select>
</td>
<td id="targ" style="font-size:36pt;line-height:34pt;filter:revealtrans(duration=0.5,transition=0)">
</td>
</tr></table>
</body>
</html>

HTA: StateMachineのひな形

以下のスクリプトは、HTAでStateMachineを使う時の一般的なひな形です。

// StateMachine.hta

<html>
<head>
<title>StateMachine</title>
<script language=JScript>
// stateの定義
var i=0;
var STATE_COLOR_NONE=i++;
var STATE_COLOR_RED=i++;
var STATE_COLOR_BLUE=i++;
var STATE_COLOR_GREEN=i++;
var STATE_COLOR_YELLOW=i++;
// 変数を準備
var mState=STATE_COLOR_NONE;        // 初期state
var mTimerID=0;                     // timerの初期化

// timer設定ヘルパ関数
function mSetTimer(tm) {
    if(mTimerID) {
        window.clearTimeout(mTimerID);
        mTimerID=0;
    }
    if(tm) mTimerID=window.setTimeout("mRequest('TIMEOUT')", tm);
}
// state内処理ヘルパ関数
function mInStateProc(state, req){
    switch(state){
    case STATE_COLOR_NONE:
        if(req=='CLICK') state=STATE_COLOR_RED;
        break;
    case STATE_COLOR_RED:
        if     (req=='TIMEOUT') state=STATE_COLOR_BLUE;
        else if(req=='CLICK')   state=STATE_COLOR_NONE;
        break;
    case STATE_COLOR_BLUE:
        if     (req=='TIMEOUT') state=STATE_COLOR_GREEN;
        else if(req=='CLICK')   state=STATE_COLOR_NONE;
        break;
    case STATE_COLOR_GREEN:
        if     (req=='TIMEOUT') state=STATE_COLOR_YELLOW;
        else if(req=='CLICK')   state=STATE_COLOR_NONE;
        break;
    case STATE_COLOR_YELLOW:
        if     (req=='TIMEOUT') state=STATE_COLOR_RED;
        else if(req=='CLICK')   state=STATE_COLOR_NONE;
        break;
    }
    return state;
}
// Exit state処理ヘルパ関数
function mExitStateProc(state, req){
    mSetTimer(0);
    switch(state){
    case STATE_COLOR_NONE:   break;
    case STATE_COLOR_RED:    break;
    case STATE_COLOR_BLUE:   break;
    case STATE_COLOR_GREEN:  break;
    case STATE_COLOR_YELLOW: break;
    }
}
// Enter state処理ヘルパ関数
function mEnterStateProc(state, req){
    mUpdateView();
    switch(state){
    case STATE_COLOR_NONE:   mSetTimer(   0);break;
    case STATE_COLOR_RED:    mSetTimer( 500);break;
    case STATE_COLOR_BLUE:   mSetTimer( 830);break;
    case STATE_COLOR_GREEN:  mSetTimer(1160);break;
    case STATE_COLOR_YELLOW: mSetTimer(1500);break;
    }
}
// イベント処理
function mRequest(req) {
    var prev=mState;
    mState=mInStateProc(mState, req);   // state内処理
    if(prev!=mState) {
        mExitStateProc(prev, req);      // stateを抜ける処理
        mEnterStateProc(mState, req);   // stateに入る処理
    }
}
// ビューの更新
function mUpdateView() {
    switch(mState){
    case STATE_COLOR_NONE:
        document.body.style.background='BACKGROUND';
        text.innerHTML="CLICKで開始します。";
        break;
    case STATE_COLOR_RED:
        document.body.style.background='RED';
        text.innerHTML="赤です。CLICKで終了します。";
        break;
    case STATE_COLOR_BLUE:
        document.body.style.background='BLUE';
        text.innerHTML="青です。CLICKで終了します。";
        break;
    case STATE_COLOR_GREEN:
        document.body.style.background='GREEN';
        text.innerHTML="緑です。CLICKで終了します。";
        break;
    case STATE_COLOR_YELLOW:
        document.body.style.background='YELLOW';
        text.innerHTML="黄色です。CLICKで終了します。";
        break;
    }
}
</script>
<hta:application border=dialog borderstyle=normal caption=yes contextmenu=no innerborder=no
 scroll=no minimizebutton=no maximizebutton=no selection=no windowstate=normal icon="" />
</head>
<body onload="mUpdateView()" onclick="mRequest('CLICK')">
<font color=black><b id=text></b></font>
</body>
</html>

HTA: 実行のログを取る

以下のスクリプトは、HTAでログを扱う例です。

// LogWindow.hta

<html>
<head>
<title>LogWindow</title>
<script language=JScript>
window.resizeTo(400,360);
var lDataMax=14;
var lData=new Array(lDataMax);
var lDataW=0;
var i;
for(i=0; i<lDataMax; i++) lData[i]="";
function LogWindow(str)
{
    var i, j, s;
    lData[lDataW++]=str;
    if(lDataW>=lDataMax) lDataW=0;
    s="";
    for(i=0, j=lDataW; i<lDataMax; i++) {
        s+=lData[j++]+"<br>";
        if(j>=lDataMax) j=0;
    }
    txt.innerHTML = s;
}
function B1Click(){LogWindow("ボタン1が押されました。");}
function B2Click(){LogWindow("ボタン2が押されました。");}
function B3Click(){LogWindow("ボタン3が押されました。");}
</script>
<hta:application border=dialog borderstyle=normal caption=yes contextmenu=no innerborder=no
 scroll=no minimizebutton=no maximizebutton=no selection=no windowstate=normal icon="" />
</head>
<body onload="LogWindow('')">
<button onclick="B1Click()"> ボタン1 </button>
&nbsp;&nbsp;
<button onclick="B2Click()"> ボタン2 </button>
&nbsp;&nbsp;
<button onclick="B3Click()"> ボタン3 </button>
<br><br>
<table border=1 bgcolor=lightblue width=100%><tr valign=bottom><td id=txt></td></tr></table>
</body>
</html>

HTA: プログレスバーを実現する

以下のスクリプトは、HTAでプログレスバーを実現する例です。

// Progress.hta

<html>
<head>
<title>ProgressBar</title>
<style type="text/css">
.pBarO  {height:14px;border:1px solid gray;padding:1px;width: 80%}
.pBarOR {height:14px;border:1px solid gray;padding:1px;width: 100%;text-align: right}
.pBarI  {background:#6080f0;height:100%}
</style>
<script language=JScript>
window.resizeTo(240,150);
// Progress Barオブジェクト
function ProgressBar(str){
    this.s=str;
    this.val=0;
    this.get=function(){return this.val;}
    this.set=function(value){this.val=value;}
    this.updt=function(){eval(this.s).style.width=this.val+"%";}
}

var pb1=new ProgressBar('pBar1');
var pb2=new ProgressBar('pBar2');

var TimerID=0;
function TimeOut(){
    var val1, val2;
    val1=pb1.get(); val2=pb2.get();
    val1++;         val2++;
    if(val1>100) val1=0;
    if(val2>100) val2=0;
    pb1.set(val1);  pb2.set(val2);
    pb1.updt();     pb2.updt();
    TimerID=window.setTimeout("TimeOut()", 20);
}
function B1Click(){
    if(TimerID) {
        window.clearTimeout(TimerID);
        TimerID=0;
    }
    else TimerID=window.setTimeout("TimeOut()", 20);
};
function B2Click(){
    pb1.set(0); pb2.set(0);
    pb1.updt(); pb2.updt();
};
</script>
<hta:application border=dialog borderstyle=normal caption=yes contextmenu=no innerborder=no
 scroll=no minimizebutton=no maximizebutton=no selection=no windowstate=normal icon="" />
</head>
<body onload="pb1.updt();pb2.updt()">
<button onclick="B1Click()"> スタート/ストップ </button>
&nbsp;&nbsp;
<button onclick="B2Click()"> リセット </button>
<br><br>
<div class=pBarO><div class=pBarI id=pBar1></div></div>
<br>
<div class=pBarOR><div class=pBarI id=pBar2></div></div>
</body>
</html>

HTA: エレメントの移動

以下のスクリプトは、HTAでエレメントを移動する例です。

// DragMove.hta

<html>
<head>
<title>Drag</title>
<script language=JScript>
var dragElm='';
var dragL, dragT, dragX, dragY;
function dragDown(){
    if(window.event.button!=1) return;
    window.event.cancelBubble=true;
    dragElm=window.event.srcElement;
    dragElm.style.position='relative';
    dragL=dragElm.style.pixelLeft;
    dragT=dragElm.style.pixelTop;
    dragX=window.event.screenX;
    dragY=window.event.screenY;
}
function dragMove(){
    if(window.event.button!=1 || dragElm=='') return;
    window.event.cancelBubble=true;
    dragElm.style.left=(dragL+window.event.screenX-dragX);
    dragElm.style.top =(dragT+window.event.screenY-dragY);
}
function dragUp(){
    dragElm='';
}
</script>
<hta:application border=dialog borderstyle=normal caption=yes contextmenu=no innerborder=no
 scroll=no minimizebutton=no maximizebutton=no selection=no windowstate=normal icon="" />
</head>
<body onmousemove="dragMove()" onmouseup="dragUp()">
<span onmousedown="dragDown()" style="background: yellow">Dragしてください</span>
</body>
</html>

HTA: メニューを実現する

以下のスクリプトは、HTAでメニューを実現する例です。

// Menu.hta

<html>
<head>
<title>Menu</title>
<style type="text/css">
body{margin:0;padding:0}
.menuBar  {background:MENU;color:MENUTEXT;padding:0px 0px 2px 0px}
.menuFlat {font-size:85%;padding:1px 5px 1px 5px;border:1px}
.menuPopup{font-size:85%;padding:1px 4px 1px 4px;border:1px inset}
.menuTable{padding:0;margin:0;border:0 none;background:MENU;color:MENUTEXT}
.menuDrop {display:none;background:MENU;color:MENUTEXT}
.menuDropV{position:absolute;font-size:85%;padding:0px 1px 1px 1px;display:block;
           border:1px outset;line-height:120%;background:MENU;color:MENUTEXT;z-index:9999}
.menuItem {padding: 0px 1.5em 0px 1.5em; width: 100%}
</style>
<script language=JScript>
var menuObj=new ActiveXObject("Scripting.Dictionary")
var menuNow=0;                  // menuがPopup中かどうか
function menuReset(){           // menuのPopupをキャンセル
    var e=new Enumerator(menuObj);
    for (;!e.atEnd();e.moveNext()){
        e.item().className='menuFlat';
        menuObj.Item(e.item()).className='menuDrop';
    }
    menuNow=0;
}
function menuPopup(){           // srcElementと、関連するDrop部をPopupする
    var src=window.event.srcElement;
    var trg=menuObj.Item(src);
    menuReset();
    src.className='menuPopup';
    trg.className='menuDropV';
    trg.style.left=src.offsetLeft;
    trg.style.top =src.offsetTop+src.offsetHeight+1;
    menuNow=src;                // 現在Popup中のmenuを記憶
}
function menuDown(){            // menuが押された
    window.event.cancelBubble=true;
    if(window.event.srcElement.className=='menuPopup') menuReset();
    else menuPopup();
}
function menuClick(){window.event.cancelBubble=true;}   // menuがClickされた
function menuOver(){            // menu領域にポインタが入った
    if(menuNow!=0) menuPopup(); // Popup中なら、srcElementをPopup
}
function menuItemOver(){        // Drop中アイテムのハイライト
    var o=window.event.srcElement.style;
    o.background="HIGHLIGHT";
    o.color="HIGHLIGHTTEXT";
}
function menuItemOut(){         // Drop中アイテムを抜けたら戻す
    window.event.srcElement.style.background="MENU";
    window.event.srcElement.style.color="MENUTEXT";
}
function InitialUpdate(){       // Load後にmenuと、関連するDropを登録
    menuObj.Add(menuFile, dropFile);
    menuObj.Add(menuHelp, dropHelp);
}
function BodyClick(){menuReset();}  // 無関係部分をClickされたらmenuのPopupをキャンセル
function dropExit(){window.close();}
</script>
<hta:application border=dialog borderstyle=normal caption=yes contextmenu=no innerborder=no
 scroll=no minimizebutton=no maximizebutton=no selection=no windowstate=normal icon="" />
</head>
<body onload="InitialUpdate()" onclick="BodyClick()">

<div class=menuBar>
&nbsp;
<span id=menuFile class=menuFlat onmousedown="menuDown()" onmouseover="menuOver()" onclick="menuClick()">ファイル
</span><span id=menuHelp class=menuFlat onmousedown="menuDown()" onmouseover="menuOver()" onclick="menuClick()">ヘルプ</span>

<table class=menuTable><tr><td id=dropFile class=menuDrop>
<div class=menuItem onmouseover="menuItemOver()" onmouseout="menuItemOut()">実行</div>
<div class=menuItem onmouseover="menuItemOver()" onmouseout="menuItemOut()" onclick="dropExit()" onmouseup="dropExit()">終了</div>
</td></tr></table>

<table class=menuTable><tr><td id=dropHelp class=menuDrop>
<div class=menuItem onmouseover="menuItemOver()" onmouseout="menuItemOut()">ヘルプ</div>
<div class=menuItem onmouseover="menuItemOver()" onmouseout="menuItemOut()">バージョン情報</div>
</td></tr></table>
</div>

</body>
</html>

HTA: 設定の保存

以下のスクリプトは、HTAで設定値の保存を実現する例です。設定値はCookieに保存します。Cookieの本体は、Documents and Settings\ユーザ名\Cookiesに保存されます。

// Settings.hta

<html>
<head>
<title>Settings</title>
<script language=JScript>
// --------------- cookie ---------------
var cookiePool=new ActiveXObject("Scripting.Dictionary")
function LoadCookie()               // onloadで呼び出してください
{
    var aCookie = document.cookie.split("; ");
    for (var i=0; i < aCookie.length; i++) {
        var aCrumb = aCookie[i].split("=");
        cookiePool.Add(aCrumb[0], aCrumb[1]);
    }
}
function SetCookie(sName, sValue)   // 設定をcookieに書き込みます
{
    document.cookie = sName + "=" + escape(sValue) + "; expires=Fri, 31 Dec 2021 23:59:59 GMT;";
    if(!cookiePool.Exists(sName)) cookiePool.Add(sName, sValue);
    else cookiePool.Item(sName)=sValue;
}
function GetCookie(sName, defval)   // 値を得ます(無ければdefvalを返します)
{
    if(cookiePool.Exists(sName)) return unescape(cookiePool.Item(sName));
    return defval;
}
// --------------- Drag --------------------
var dragElm='';
var dragL, dragT, dragX, dragY;
function dragDown(){
    if(window.event.button!=1) return;
    window.event.cancelBubble=true;
    dragElm=window.event.srcElement;
    dragElm.style.position='relative';
    dragL=dragElm.style.pixelLeft;
    dragT=dragElm.style.pixelTop;
    dragX=window.event.screenX;
    dragY=window.event.screenY;
}
function dragMove(){
    if(window.event.button!=1 || dragElm=='') return;
    window.event.cancelBubble=true;
    dragElm.style.left=(dragL+window.event.screenX-dragX);
    dragElm.style.top =(dragT+window.event.screenY-dragY);
}
function dragUp(){
    dragElm='';
}
// -------------------------------------
function OnInitial(){
    LoadCookie();
    item1.style.position='relative';        // drag itemはrelative
    var l=GetCookie('item1left', -1);
    var t=GetCookie('item1top', -1);
    if(l!=-1) item1.style.left=l;
    if(t!=-1) item1.style.top=t;
}
function OnClose(){
    SetCookie('item1left', item1.style.pixelLeft);
    SetCookie('item1top',  item1.style.pixelTop);
}
</script>
<hta:application border=dialog borderstyle=normal caption=yes contextmenu=no innerborder=no
 scroll=no minimizebutton=no maximizebutton=no selection=no windowstate=normal icon="" />
</head>
<body onload="OnInitial()" onunload="OnClose()" onmousemove="dragMove()" onmouseup="dragUp()">
<span id=item1 onmousedown="dragDown()" style="background: yellow">Dragしてください</span>
</body>
</html>

WSH: フォルダ内を含むファイル名一覧を作成する

以下のスクリプトは、Drag&Dropされた複数のファイル名を列挙したテキストファイルを作成し、関連づけられたアプリケーションで開きます。「送る」(SendTo)に置いて使用します。フォルダが見つかった場合には、サブディレクトリ内も列挙します。

// FileFolderToText.wsf

<job id="FileFolderToText">
<script language=JScript>
// D&Dされたファイル/フォルダ(およびその中身)を列挙します
var sh=WScript.CreateObject("WScript.Shell");
var fso=new ActiveXObject("Scripting.FileSystemObject");
var i;
var list=new Array();

function GetChildCollection(folderspec) { // 指定フォルダ内のフォルダファイルのリストを返す
    var f=fso.GetFolder(folderspec);
    var col=new Array();
    var fc;

    // フォルダを列挙
    for (fc=new Enumerator(f.SubFolders); !fc.atEnd(); fc.moveNext()) col.push(fc.item().Path);
    // ファイルを列挙
    for (fc=new Enumerator(f.Files);      !fc.atEnd(); fc.moveNext()) col.push(fc.item().Path);

    return col;
}

// D&Dファイル名をlistに追加
for(i=0; i<WScript.Arguments.Length; i++) list.push(WScript.Arguments(i));

for(i=0; i<list.length; i++) {          // フォルダ内を展開しつつ、listを成長させる
    if(fso.FolderExists(list[i])) {
        list=list.slice(0, i+1).concat(GetChildCollection(list[i]),list.slice(i+1, list.length));
    }
}

// listをファイルに出力
var ofilename=sh.ExpandEnvironmentStrings("%TEMP%")+'\\filename.txt';
var ofileobj=fso.CreateTextFile(ofilename, true);
for(i=0; i<list.length; i++) {
    if(fso.FolderExists(list[i])) ofileobj.WriteLine(list[i]+"\\");
    else                          ofileobj.WriteLine(list[i]);
}
ofileobj.Close();
sh.Run('rundll32.exe url.dll,FileProtocolHandler '+ofilename, 5);   // 関連づけアプリを起動
</script>
</job>

WSH: ファイルから正規表現にマッチするタグファイルを出力する

以下のスクリプトは、Drag&Dropされた複数のファイルやフォルダ、および、そのサブディレクトリ内を列挙し、変数reで定義された正規表現にマッチする行を抽出し、タグファイルとして出力します。「送る」(SendTo)に置いて使用します。

// SearchFileFolder.wsf

<job id="SearchFileFolder">
<script language=JScript>
// D&Dされたファイル/フォルダから、ここで定義した正規表現にmatchするタグファイルを出力します。

var re=/<a href/;

var sh=WScript.CreateObject("WScript.Shell");
var fso=new ActiveXObject("Scripting.FileSystemObject");
var i;
var list=new Array();

function GetChildCollection(folderspec) // 指定フォルダ内のフォルダファイルのリストを返す
{
    var f=fso.GetFolder(folderspec);
    var col=new Array();
    var fc;

    // フォルダを列挙
    for (fc=new Enumerator(f.SubFolders); !fc.atEnd(); fc.moveNext()) col.push(fc.item().Path);
    // ファイルを列挙
    for (fc=new Enumerator(f.Files);      !fc.atEnd(); fc.moveNext()) col.push(fc.item().Path);

    return col;
}

// D&Dファイル名をlistに追加
for(i=0; i<WScript.Arguments.Length; i++) list.push(WScript.Arguments(i));

for(i=0; i<list.length; i++) {          // フォルダ内を展開しつつ、listを成長させる
    if(fso.FolderExists(list[i])) {
        list=list.slice(0, i+1).concat(GetChildCollection(list[i]),list.slice(i+1, list.length));
    }
}

// ファイルを開く
var file0=WScript.Arguments(0);
var ofilename=fso.GetParentFolderName(file0)+'\\ext_'+fso.GetFileName(file0);
if(ofilename.slice(-4).toUpperCase()!='.TXT') ofilename+='.txt';
var ofileobj=fso.CreateTextFile(ofilename, true);
var line;
var lc;

for(i=0; i<list.length; i++) {
    lc=1;
    if(!fso.FolderExists(list[i])) {
        var ifileobj=fso.OpenTextFile(list[i], 1);
        while(!ifileobj.AtEndOfStream) {
            line=ifileobj.ReadLine();
            if(re.test(line)) {
                ofileobj.Write(list[i]+'('+lc+'): '+line+'\n');
            }
            lc++;
        }
        ifileobj.Close();
    }
}
ofileobj.Close();
sh.Run('rundll32.exe url.dll,FileProtocolHandler '+ofilename, 5);   // 関連づけアプリを起動
</script>
</job>

WSH: 標準入出力とコマンドラインを利用する

以下のスクリプトは、ipconfigを利用してネットワークアダプタの数を得ます。標準入出力を利用します。その後、コマンドプロンプトのechoを使って、DOS窓にも表示します。

<job id="cmdline">
<script language=JScript>
var sh = WScript.CreateObject("WScript.Shell");
var oExec=sh.Exec('ipconfig /all');
while (oExec.Status == 0) WScript.Sleep(100);

var numAdapters=0;
var str;

while(!oExec.StdOut.AtEndOfStream) {
    str=oExec.StdOut.ReadLine();
    if(str.substr(0, 16)=="Ethernet adapter") numAdapters++;
}

str='アダプタの数は'+numAdapters;
sh.Popup(str);

sh.Run('cmd /K echo '+str, 1);
</script>
</job>

WSH: 音声ファイルのリストをExcelで作成する

以下のスクリプトは、Hey, Scripting Guy! オーディオ ファイルの拡張データを Excel ブックにコピーする方法はありますかを参考に、Drag & Drop対応、サブフォルダの追跡、タイトルへのリンクの付与などの機能を追加した物です。

// AudioList.wsf

<job id="AudioList">
<script language=JScript>
// Excelでオーディオの一覧を作成する
// -----------------------------------------------------------------
// グローバル変数の定義
var sh=WScript.CreateObject("WScript.Shell");
var fs=new ActiveXObject("Scripting.FileSystemObject");
// -----------------------------------------------------------------
// オブジェクトと関数を定義
// Drag&Dropされたファイルをリスト化
function MakeArgList() {
  var l=new Array();
  for(var i=0; i<WScript.Arguments.Length; i++) l.push(WScript.Arguments(i));
  return l;
}

// Platformを調べる(0: 不明, 1: XP, 2: Vista)
function GetPlatform() {
  var oExec=sh.Exec('cmd /C ver');
  while(!oExec.Status) WScript.Sleep(100);
  var str;
  while(!oExec.StdOut.AtEndOfStream) {
    str=oExec.StdOut.ReadLine().toUpperCase();
    if(str.indexOf(" XP")!=-1)        return 1;
    if(str.indexOf("VERSION 6.")!=-1) return 2;
  }
  return 0;
}

// 各列の属性を定義
function propItem(str, num, type) {
  this.Name=str;                                          // 文字列
  this.Num=num;                                           // 対応番号(-1はファイル名、-2はフルパス)
  this.Type=type;                                         // 型(0で標準、1で文字型)
};

// 各桁の属性を決定
function MakeProps() {
  var sys=GetPlatform();
  var pr=new Array();                                     // タイトル行文字列, 番号, 文字列かの配列
  //                      XP, Vista (Shell32.dllのバージョンで異なる事があります)
  var nArtist  =new Array(16, 13);
  var nAlbum   =new Array(17, 14);
  var nTrack   =new Array(19, 26);
  var nTitle   =new Array(10, 21);
  var nDuration=new Array(21, 27);
  var nGenre   =new Array(20, 16);
  var nBps     =new Array(22, 28);
  var nYear    =new Array(18, 15);
  if(!sys || nArtist.length<=sys) return pr;
  --sys;
  pr.push(new propItem("アーティスト",nArtist[sys],   1));
  pr.push(new propItem("アルバム",    nAlbum[sys],    1));
  pr.push(new propItem("#",           nTrack[sys],    0));
  pr.push(new propItem("タイトル",    nTitle[sys],    1));
  pr.push(new propItem("長さ",        nDuration[sys], 0));
  pr.push(new propItem("ジャンル",    nGenre[sys],    1));
  pr.push(new propItem("bps",         nBps[sys],      0));
  pr.push(new propItem("年",          nYear[sys],     0));
  pr.push(new propItem("ファイル名",  -1,             1));
  pr.push(new propItem("パス",        -2,             1));
  return pr;
}

// 表示状態を保持する(Excel)
function DisplayStatus(o) {
  // カラムとシートのインジケータ行の2行分をマイナス
  this.Total=Math.max(Math.floor(o.UsableHeight/o.Cells(1,1).RowHeight)-2, 1);
  this.Min=Math.max(Math.floor(this.Total/4), 1);         // 最低表示行数を計算
  this.Total=Math.max(this.Total, this.Min+1);            // 逆転/一致しない事
  this.Visible=0;                                         // 現在の表示行数
  this.Cur=2;                                             // カレント書き込み行
  // 表示を進めてExcel表示を保証(Excel)
  this.inc=function(o) {
    this.Cur++;
    this.Visible++;
    if(this.Visible>=this.Total) {
      o.Goto(o.Cells(this.Cur-this.Min, 1), true);
      this.Visible=this.Min;
    }
    return this;
  }
}

// 進行状況の表示(Excel, 済み, トータル)
function ExcelStatus(o, treat, total) {
  o.StatusBar="リストアップ中です("+treat+"/"+total+")";
}

// Excelを作成し、シートにヘッダと表示形式を設定(propItemのArray)
function ExcelPrepare(pr) {
  var o=new ActiveXObject("Excel.Application");           // Excelを作成し
  o.Workbooks.Add();                                      // WorkBookを追加
  o.Visible=true;
  for(var i=1; i<=pr.length; i++) {
    if(pr[i-1].Type==1)                                   // Typeが1なら文字列に
      o.Cells(1, i).EntireColumn.NumberFormatLocal="@";
    o.Cells(1, i)=pr[i-1].Name;
  }
  ExcelStatus(o, 0, 0);
  return o;
}

// Excelに書き出す(Excel, porps, Folder, FolderItem, DisplayStatus)
function ExcelWrite(o, pr, of, item, ds) {
  for(var i=1; i<=pr.length; i++) {
    switch(pr[i-1].Num) {
    case -1: o.Cells(ds.Cur, i)=item.Name; break;         // ファイル名
    case -2: o.Cells(ds.Cur, i)=item.Path; break;         // フルパス
    default: o.Cells(ds.Cur, i)=of.GetDetailsOf(item, pr[i-1].Num); break;
    }
  }
  o.ActiveSheet.Hyperlinks.Add(o.Cells(ds.Cur, 4), item.Path);
  return ds.inc(o);
}

// 最後のExcel処理(Excel)
function ExcelLastTreat(o) {
  o.Goto(oExcel.Cells(1, 1), true);                       // 先頭に戻って
  o.Cells.EntireColumn.AutoFit;                           // 桁幅をオートフィット
  o.Cells.Sort(o.Range("A2"), 1,                          // アーティスト/アルバム/トラックでソート
               o.Range("B2"), o.Range("B2"), 1,
               o.Range("C2"), 1,
               1, 1, false, 1, 1, 0, 0, 0);
  o.Cells(1, 1).EntireColumn.AutoFilter;                  // アーティストでオートフィルタ
  o.StatusBar="完了しました。";
}

// ------------------------------------------------------
// メインプログラムの開始
var list=MakeArgList();                                   // 処理対象となるリスト
if(!list.length) {
  sh.Popup("Drag & Dropで使用してください");
  WScript.Quit();
}
var props=MakeProps();
if(!props.length) {
  sh.Popup("非対応のOSです");
  WScript.Quit();
}
var oShell=new ActiveXObject("Shell.Application");
var oExcel=ExcelPrepare(props);                           // Excelを準備
var oFolder;
var oFolderItems;
var j, k;
var fullName;
var curFolder="";                                         // カレントフォルダ名
var parFolder;                                            // 一時的な親フォルダ名
var ds=new DisplayStatus(oExcel);                         // 表示状態を作成

for(j=0; j<list.length; j++) {
  fullName=list[j];
  if(fs.FileExists(fullName)) {                           // ファイルの場合は親フォルダを準備する
    parFolder=fs.GetParentFolderName(fullName);
    if(parFolder!=curFolder) {                            // oFolderのパスが変化したら作成
      oFolder=oShell.Namespace(parFolder);
      curFolder=parFolder;
    }
    ds=ExcelWrite(oExcel, props, oFolder, oFolder.ParseName(fs.GetFileName(fullName)), ds)
    ExcelStatus(oExcel, ds.Cur-2, ds.Cur-2+list.length-j);
  }
  else {                                                  // フォルダの場合
    var curList=new Array();                              // 列挙中フォルダ内のサブフォルダリスト
    oFolder=oShell.Namespace(fullName);
    curFolder=fullName;
    oFolderItems=oFolder.Items();                         // oFolderItemsにコピーしないと遅い
    for(k=0; k<oFolderItems.Count; k++) {
      var item=oFolderItems.Item(k);
      if(item.IsFolder) curList.push(item.Path);          // フォルダはリストに追加
      else {
        ds=ExcelWrite(oExcel, props, oFolder, item, ds);  // ファイルはExcelに書き出す
        ExcelStatus(oExcel, ds.Cur-2, ds.Cur-2+list.length-j+oFolderItems.Count-k);
      }
    }
    // 列挙したサブフォルダを処理リストに挿入
    list=list.slice(0, j+1).concat(curList, list.slice(j+1, list.length));
  }
}
ExcelLastTreat(oExcel);                                   // 最後のExcel処理
</script>
</job>

HTA: ファイル内の文字列を検索する

以下のスクリプトは、ファイルを指定して文字列を検索する例です。

// search.hta

<html>
<head>
<title>search</title>
<script language=JScript>
var fs=new ActiveXObject("Scripting.FileSystemObject");

function do_search() {
  document.all.output.height=document.body.offsetHeight-document.all.output.offsetTop;

  var sstr=document.parm.string1.value;
  var file=document.parm.file1.value;
  if(sstr=="" || file=="") return;

  var reg=RegExp(sstr);
  var ostr="";
  var line;
  var ifile=fs.OpenTextFile(file,1);
  while(line=ifile.ReadLine(), !ifile.AtEndOfStream) {
    if(line.search(reg)!=-1) ostr+=line+"\n";
  }
  ifile.Close();
  parent.output.document.body.innerText=ostr;
}
</script>
<hta:application border=window borderstyle=normal caption=yes innerborder=no
 scroll=no minimizebutton=no maximizebutton=no selection=no windowstate=normal icon="" />
</head>

<body>
<form name=parm>
検索文字列: <input type=text name=string1><br>
検索ファイル: <input type=file name=file1><br>
<input type=button value="実行" onclick="do_search();">
</form>
<iframe id=output width=100%></iframe>
</body>
</html>

HTA: ラジオボタンを使う

以下のスクリプトは、ラジオボタンを使用した選択の例です。

// radio.hta

<html>
<head>
<title>checkbox</title>
<script language=JScript>
function do_check() {
  var l=document.parm.chk.length;       // ラジオボタンの数を取得
  var i, sel=0;
  for(i=0; i<l; i++) {                  // チェックされたボタンを検索
    if(document.parm.chk[i].checked) {
      sel=document.parm.chk[i].value;
      break;
    }
  }
  document.all.output.innerText=sel;
}
</script>
<hta:application border=window borderstyle=normal caption=yes innerborder=no
 scroll=no minimizebutton=no maximizebutton=no selection=no windowstate=normal icon="" />
</head>

<body>

<form name=parm>
<fieldset style="padding: 0.3em">
<legend>選択してください</legend>
<input type=radio accesskey=1 name=chk value=1 checked><u>1</u></input><br>
<input type=radio accesskey=2 name=chk value=2><u>2</u></input><br>
<input type=radio accesskey=4 name=chk value=4><u>4</u></input><br>
<input type=button accesskey=s value="選択(S)" onclick="do_check();"></input>
</fieldset>
</form>

<p id=output></p>

</body>
</html>

HTA: 図形を使う

以下のスクリプトは、VMLを使用して図形を動かす例です。

// vector.hta

<html xmlns:v="urn:schemas-microsoft-com:vml">
<head>
<style>
v\:* { behavior: url(#default#VML); }
</style>
<title>VML</title>
<script language=JScript>
var height=0;
var step;
function do_interval() {
  if(height==0) step=10;
  else if(height==200) step=-10;
  height+=step;
  document.all.ov.style.height=height+"pt";
}
function do_start() {
  window.setInterval("do_interval()", 1000/30);
}
</script>
<hta:application border=window borderstyle=normal caption=yes innerborder=no
 scroll=no minimizebutton=no maximizebutton=no selection=no windowstate=normal icon="" />
</head>
<body onload="do_start()">
<v:oval id=ov style="position:absolute; left:0; top:0; width:100pt; height:0pt" fillcolor="red" />
</body>
</html>

HTA: グレースケールを描く

以下のスクリプトは、VMLを使用してグレースケールを描く例です。リサイズによる再描画もします。

// gray.hta

<html xmlns:v="urn:schemas-microsoft-com:vml">
<head>
<style>
v\:* { behavior: url(#default#VML); }
</style>
<title>Gray Scale</title>
<script language=JScript>
function steps(w, h, ho, n) { // width, height, height offset, number of steps
  var i, c, x, xw;
  var s="", cstr="";
  for(i=0; i<n; i++) {
    c=Math.floor(i*255/(n-1));
    cstr='RGB('+c+','+c+','+c+')';
    x=Math.floor(w*i/n);
    xw=Math.floor(w*(i+1)/n)-x;
    s+='<v:rect style="position:absolute; top:'+ho+'; left:'+x+'; width:'+xw+
       '; height:'+h+';" fillcolor='+cstr+' strokecolor='+cstr+' />';
  }
  return s;
}
function draw() {
  var w=document.body.clientWidth;
  var h=document.body.clientHeight/3;
  var str="";
  str+=steps(w, h, 0  , 256);
  str+=steps(w, h, h  ,  32);
  str+=steps(w, h, h*2,   5);
  document.body.innerHTML=str;
}
window.attachEvent("onresize", draw)
window.attachEvent("onload", draw)
</script>
<hta:application scroll=no windowstate=maximize />
</head>
<body></body>
</html>

WSH: WEBページを作って入力する

以下のスクリプトは、WSHでWEBページを作成する事でユーザの入力を可能とする例です。HTAでは使用できないWScript.Networkなどを使いたい時に利用できます。

// connectNetwork.wsf

<job id="ConnectNetwork">
<script language=JScript>
var isQuited=0;
var server, account, password;
var ie=WScript.CreateObject("InternetExplorer.Application", "IE_");
ie.Visible=1;
ie.MenuBar=ie.ToolBar=ie.AddressBar=0;
ie.Navigate2("about:blank", 2);
ie.Document.write("<html><head><title>NetworkDrive</title></head><body></body></html>");
ie.Document.body.innerHTML=
"server: <input type=text id=server></input><br>"+
"account: <input type=text id=account></input><br>"+
"password: <input type=password id=password></input>";

function IE_OnQuit() {
  isQuited=1;
  server=ie.Document.all.server.value;
  account=ie.Document.all.account.value;
  password=ie.Document.all.password.value;
}

while(!isQuited) {
  WScript.Sleep(300);
}

if(server!="" && account!="" && password!="") {
  WScript.CreateObject("WScript.Network").MapNetworkDrive("", server, 0, account, password);
  WScript.CreateObject("WScript.Shell").Run('rundll32.exe url.dll,FileProtocolHandler "'+server+'"', 5);
}
</script>
</job>

WSH: 音声で通知する

以下のスクリプトは、SAPIを用いて開始終了を音声で通知します。

// speak.wsf

<job id="speak">
<script language=JScript>
  var sp = WScript.CreateObject("SAPI.SpVoice");
  sp.Speak("開始しました");
  sp.Speak("終了しました");
</script>
</job>

WSH: バイナリファイルを扱う

以下のスクリプトは、JScriptを用いてバイナリファイルを扱う例です。 通常JScriptではバイナリファイルを扱うことは出来ませんが、この問題に同じように困っている人が世の中にいて、ADODB.Streamを使って解決している情報をネット上で見つけました。連想配列と関数オブジェクトを使って、再利用しやすくしてみました。

// binfile.wsf

<job id="BinFile">
<script language=JScript>
// BinaryFile:
// >Open(flnm)
// >Close()
// >WriteByte(dat)      1バイト(256の剰余)を書き込みます
// >ReadByte()          -1が返ると読み込み失敗(終了)です
// >EOF()

// コード変換辞書を作成
var BFTbl=new ActiveXObject("Scripting.Dictionary");
BFTbl.Add(0x20ac, 0x80);  BFTbl.Add(0x201a, 0x82);  BFTbl.Add(0x0192, 0x83);
BFTbl.Add(0x201e, 0x84);  BFTbl.Add(0x2026, 0x85);  BFTbl.Add(0x2020, 0x86);
BFTbl.Add(0x2021, 0x87);  BFTbl.Add(0x02c6, 0x88);  BFTbl.Add(0x2030, 0x89);
BFTbl.Add(0x0160, 0x8a);  BFTbl.Add(0x2039, 0x8b);  BFTbl.Add(0x0152, 0x8c);
BFTbl.Add(0x017d, 0x8e);  BFTbl.Add(0x2018, 0x91);  BFTbl.Add(0x2019, 0x92);
BFTbl.Add(0x201c, 0x93);  BFTbl.Add(0x201d, 0x94);  BFTbl.Add(0x2022, 0x95);
BFTbl.Add(0x2013, 0x96);  BFTbl.Add(0x2014, 0x97);  BFTbl.Add(0x02dc, 0x98);
BFTbl.Add(0x2122, 0x99);  BFTbl.Add(0x0161, 0x9a);  BFTbl.Add(0x203a, 0x9b);
BFTbl.Add(0x0153, 0x9c);  BFTbl.Add(0x017e, 0x9e);  BFTbl.Add(0x0178, 0x9f);

function BinaryFile() {
  this.filename="";
  this.isdirty=0;
  this.object=WScript.CreateObject("ADODB.Stream");
  this.object.Type=2;                                 // テキスト
  this.object.charset="iso-8859-1";

  this.Open=function Open(flnm) {
    var fs=new ActiveXObject("Scripting.FileSystemObject");
    this.filename=flnm;
    this.isdirty=0;
    this.object.Open();
    if(fs.FileExists(flnm)) this.object.loadFromFile(flnm);
  }
  this.Close=function Close() {
    if(this.isdirty) {
        this.object.SaveToFile(this.filename, 2);   // 2はoverwrite
    }
    this.object.Close();
  }
  this.WriteByte=function WriteByte(dat) {
    this.object.writeText(String.fromCharCode(dat%256));
    this.isdirty=1;
  }
  this.ReadByte=function ReadByte() {
    if(this.object.EOS) return -1;
    var dat=this.object.readText(1).charCodeAt(0);
    return BFTbl.Exists(dat) ? BFTbl.Item(dat):dat;
  }
  this.EOF=function EOF() {
    return this.object.EOS;
  }
}

var ifile=new BinaryFile();
var ofile=new BinaryFile();
var i;

ofile.Open("d:\\takacy\\binfile.bin");
for(i=0; i<256; i++) {
  ofile.WriteByte(i);
}
ofile.Close();
ifile.Open("d:\\takacy\\binfile.bin");
ofile.Open("d:\\takacy\\binfile-comp.bin");
while(!ifile.EOF()) {
  ofile.WriteByte(255-ifile.ReadByte());
}
ofile.Close();
ifile.Close();
</script>
</job>