Windows Scripting Host 6

Excel

今回はExcelを操作する方法についてみていきます。 これは、VBAをふだんから使っている人にとっては非常に簡単です。
動作確認を行っているExcelはいわゆるExcel95です。

Excelを操作する

最初に、ExcelのApplicationオブジェクトにあたるオブジェクトを得ます。
    var objXL = WScript.CreateObject("Excel.Application");
それから、とりあえずExcelを見えるようにしておきます。
    objXL.Visible = true;
そのあとは、VBAでApplicationとしているところ、 またはそれが省略されているところでは Applicationオブジェクトを補っておきます。例えば、
    objXL.Cells(1, 1).Value = 1;
だいたいこれくらい分かっておけば書けるでしょう。
下の例は、モンテカルロ法で円周率を求めるプログラムです。 座標値が0〜1を取る正方形の中に疑似乱数で点を打ち、 その点が中心が原点、半径1の円内に入るかどうかを調べます。 1、2列目がx、yの値、3列目が円の中に入っていれば1、 入っていなければ0になります。 この3列目の平均を取って4倍した値が概算の円周率です。
scripts/montecarlo.js
-----------------------------------------------------------
var i, pi, rmax;
var xlMedium, xlNone, xlThin, xlAutomatic, xlXYScatter;
var xlCircle, xlValue, xlCategory;

pi = 3.141592653589793;
ExcelConstants();

var objXL = WScript.CreateObject("Excel.Application");

objXL.Visible = true;
objXL.WorkBooks.Add();                  //新規ブック

// 1/4円を描くためのデータ 4列目がx、5列目がy
//本当はワークシート関数を使ったほうが速い
for(i = 0; i <= 50; i++) {
    objXL.Cells(i + 1, 4).Value = Math.sin(pi * i / 100);
    objXL.Cells(i + 1, 5).Value = Math.cos(pi * i / 100);
}

//座標値が0〜1の値を取る2次元の点が
//半径1の円の中にいくつ入ったかで円周率を求める
rmax = 1000;
with(objXL) {
    Range(Cells(1, 3), Cells(rmax, 3)).FormulaR1C1
                            = "=if(rc1*rc1+rc2*rc2<1,1,0)";
    Range(Cells(1, 1), Cells(rmax, 2)).FormulaR1C1
                            = "=rand()";
}
objXL.Cells(1, 7).FormulaR1C1                   //結果
                = "=average(r1c3:r" + rmax + "c3)*4";
objXL.Cells(1, 7).Borders.Weight = xlMedium;

//疑似乱数によって生成された点と1/4円をグラフとして描く
with(objXL) {
    ActiveSheet.ChartObjects.Add(60, 30, 330, 300).Select
    //以下略
}

//Excelの定数
function ExcelConstants() {
    xlMedium = -4138;
    //以下略
}
気をつけなければならないのは、Rangeメソッドです。
よく分からないのですが、次のような動作になります。
    //エラー
    objXL.Range(Cells(1, 1), Cells(3, 1)).Value = 3;
    
    //正常動作
    with(objXL) {
        Range(Cells(1, 1), Cells(3, 1)).Value = 3;
    }
名前付き引数は使えません。
例えば、次は行と列を入れ替えるExcelのマクロです。
Sub transpose()
    Cells(1, 1).Value = 1
    Cells(2, 1).Value = 2
    Range("A1:A2").Copy
    Range("C1").PasteSpecial Transpose:=True
    Application.CutCopyMode = False
End Sub
PasteSpecialメソッドはTransposeという名前付き引数しか指定していませんが、 WSHではきちんと引数を指定しなければなりません。
scripts/transpose.js
---------------------------------------------------------
var objXL = WScript.CreateObject("Excel.Application");
objXL.Visible = true;
objXL.WorkBooks.Add();

xlAll = -4104; xlNone = -4142;

with(objXL) {
    Cells(1, 1).Value = 1
    Cells(2, 1).Value = 2
    Range("A1:A2").Copy
    Range("C1").PasteSpecial(xlAll, xlNone, false, true);
    CutCopyMode = false
}
xlAllなどのExcelの定数は自分で値を代入する必要があるようです。 マクロを走らせるなどして定数の値を確かめましょう。

WSH2.0ではタイプライブラリを参照することにより、 定数をそのまま使うことができます(9/6/99)。

ファイルと連携する

上のような使い方はまるで実用的ではありません。 実際にはファイルと連携するのがよいでしょう。

下は1日の残業時間が書かれたテキストファイルを読み込んで、 Excelに書き出すスクリプトです。

G:\残業の下に毎朝、前日の残業時間が書かれたテキストファイルが いつも置かれているとします。

残業980703.txt
---------------------------------
さくら,知世,小狼,千春,奈緒子,利佳
0,0,2,0.5,1,0.5
C:\残業の下に毎月の残業時間がまとめられたブックがあるとします。

さくら知世小狼 千春奈緒子利佳
7/1/98110 1.512
7/2/980.50.50 0.512

そして、タスクスケジューラで毎朝10時にスクリプトが動くなどと設定しておけば、 自動的にテキストファイルを読み込んで、 その月のブックのその日の行に各人の残業時間が書かれます。 その月のブックがなければ前月分などから新しく作ります。
Excelはウィンドウを見せないようにしていて、 スクリプトが終わる前にExcelを終了するようにしているので、 画面上には何も現れず、HDDが静かなマシンであれば 動作していることに気づかないかもしれません。

scripts/zangyou.js
------------------------------------------------------------
var fs, source_fld, excel_fld, stream, i, str;
var xlToRight = -4161;
fs = WScript.CreateObject("Scripting.FileSystemObject");
source_fld = fs.GetFolder("g:\\残業\\");

//テキストファイルから各人の残業時間を読み取る
var fc = new Enumerator(source_fld.Files);
var arrData = new Array();
var nData = 0;
for(; !fc.atEnd(); fc.moveNext()) {
    //ファイル名がパターンにマッチするテキストファイルを読む
    if(/残業[0-9]+\.txt/.test(fc.item().Name)) {
        arrData[nData] = new Data();
        //ファイル名から日付を得る
        arrData[nData].setDate(fc.item().Name);
        stream = fs.OpenTextFile(fc.item());
        //1行目は名前の配列
        str = stream.ReadLine();
        arrName = str.split(",");
        //2行目は残業時間の配列
        str = stream.ReadLine();
        arrHours = str.split(",");
        stream.Close();
        for(i = 0; i < arrName.length; i++)
            arrData[nData].setData(arrName[i], arrHours[i]);
        nData++;
    }
}

//データをブックに書きこむ
var objXL = WScript.CreateObject("Excel.Application");
//objXL.Visible = true;             ウィンドウを見せない
excel_fld = fs.GetFolder("c:\\残業");
for(i = 0; i < nData; i++)
    with(objXL) {
        OpenMonthBook(arrData[i].d);
        r = arrData[i].d.getDate() + 1;     //書きこむ行
        str = arrData[i].d.toLocaleString();
        Cells(r, 1).Value = str.substring(0, 10);   //日付
        cmax = Cells(1, 2).End(xlToRight).Column;
        for(c = 2; c <= cmax; c++) {
            str = Cells(1, c).Text          //名前
            if(arrData[i].IsExist(str))
                Cells(r, c).Value =
                            arrData[i].getZangyou(str);
        }
        ActiveWorkbook.Save();
    }
objXL.Quit();

//その月に対応するブックを開く
function OpenMonthBook(d) {
    //省略
}

//1日のデータを表すオブジェクトの定義
function Data() {
    this.d;
    this.dic = WScript.CreateObject("Scripting.Dictionary");
    this.setDate = setDate;
    this.setData = setData;
    this.IsExist = IsExist;
    this.getZangyou = getZangyou;
}

//以下略
ウィンドウ系プロパティ

Excelだけ使っているとあまり使うことはないかもしれませんが、 WSHではウィンドウのサイズ、スタイルを変えるプロパティが有用です(2/21/99)。
scripts/excelwnd.js
------------------------------------------------------
var objXL = WScript.CreateObject("Excel.Application");
objXL.Visible = true;

//Excelの定数
var xlNormal = -4143;
var xlMaximized = -4137;
var xlMinimized = -4140;

objXL.Left = 0;                     //左端の座標
objXL.Top = 0;                      //上端の座標
objXL.Width = 600;                  //幅
objXL.Height = 500;                 //高さ
objXL.WindowState = xlMaximized;    //最大化
objXL.WindowState = xlNormal;       //元の大きさに
objXL.WindowState = xlMinimized;    //最小化

first, prev, next, noframe, exit
Written 7/4/98