スクリプト万歳

使ってますか? Windows Scripting Host



初音 玲 HATSUNE, Akira



Visual Basic Script Edition(以下VBS)と聞いて、まず思い浮かぶのが、Active Server Page(ASP)などのサーバーサイドスクリプトを記述することや、IEに読み込ませて動作させるクライアントサイドスクリプトを記述することだろう。しかし、これから注目されるのは、VBSでWindows Scripting Host(以下WSH)のスクリプトが記述できることだと思う(表1)。
表1:ホストアプリケーションごとの
VBSのバージョン
ホストアプリケーション VBS
IE 3.0 1.0
IIS 3.0 2.0
IE 4.0 3.0
IIS 4.0 3.0
WSH 1.0 3.0
Outlook 98 3.0
VS 6.0 4.0
IE 5.0 5.0
IIS 5.0 5.0
WSH 5.0 5.0

Windows Scripting Hostとは?

 WSHは、Windows 98では標準でサポートされている機能だ。Windows NT 4.0ではOption Packを導入することでサポートされ、また、マイクロソフトの「WindowsScriptTechnologies」(http://www.microsoft.com/japan/developer/scripting/)からダウンロードすることもできる。
 WSHは、ひとことで言えば、高機能なBatファイル実行環境だ。Windowsに元々あったBatファイル実行環境は、Autoexec.batに代表されるように、コマンドを次々に実行していくくらいにしか使えない逐次実行用の環境でしかなかった。
 WSHは、Batファイルに対応するスクリプトファイルの解釈に、Internet Explorer(以下IE)で培ったスクリプトエンジンの仕組みを流用している。事実、Visual Basic ScriptやJScriptの解釈部分は、IEと共通のDLLにより実現されている。そのため、IEのバージョンが上がれば、WSHのスクリプト解釈部分のバージョンも上がる。よって、執筆時点での最新バージョンは、バージョン5.0だ。

小見出しなぜ、WSHか?

 WSHを使わなくても、htmlファイル中にVBSを埋め込みIEで実行することが可能だ。しかし、あえてWSHを使う理由は、IEよりもメモリの負担が少なく動作も軽いからだ。最終的なアウトプットをhtmlではなく別の形態にしたいのならば、IEを使う必要はない。

小見出しWSHの使い方

 WSHは、スクリプトファイルをドロップしたときにDOSプロンプトが起動されるシェルベースのCscript.exeと、ウィンドウベースのWscript.exeの2つの形態で提供されている(図1)。
 Dropするスクリプトファイルの拡張子がjsならばJscriptで記述され、vbsのものがVBSで記述されたWSHだ。ドロップする以外にも、エクスプローラからスクリプトファイルをダブルクリックしたり、コマンドラインからスクリプトファイル名を指定して起動することもできる。多分、一番正当な使い方は、コマンドラインからの起動だろう。
 たとえば、リスト1をコマンドラインから起動するには、

cscript arg.vbs パラメータ

のように入力すればよい。

図1:WSHの構造
図1:WSHの構造

リスト1:パラメータを受けとる形のWSHのサンプル
Option Explicit
Dim objArgs
Dim iintLoop

Set objArgs = WScript.Arguments
For iintLoop = 0 to objArgs.Count - 1
        WScript.Echo objArgs(iintLoop)
Next

小見出し変数型宣言の注意点

 VBSの特徴のひとつとして、変数型がいわゆるバリアント型しか存在しないことがあげられるだろう。そのため、インタープリタという点を差し引いても必ずしも処理効率がよいとはいえない。まあ、WSHの使い方の真髄がちょっとした定型手続きを記述することだとすれば、変数型などを導入して、敷居を高くする必要はないだろう。要は、マイクロソフトにがんばってもらって、バリアント型変数でも十分な処理速度を確保できるようになればよいのだ。
 しかしながら、型宣言がないとしても、スクリプトが作った本人以外に使われる可能性がわずかでもあるのならば、先頭にDim文を使って変数宣言を行なっておくのが良いだろう。もちろんOption Explicitを先頭に記述して、変数宣言を必須とする仕組みを有効にしておくのも忘れてはいけない。Option Explicitを記述しておけば、変数宣言していない変数に出会ったところで実行時エラーになる(リスト2、図2)。
 Visual Studio 6.0をインストールしていれば、Microsoft Development Environmentでデバッグも可能だ(図3)。

リスト2:Option Explicitを記述して、変数の宣言を必須とする
Option Explicit
Dim objArgs
' Dim iintLoop

Set objArgs = WScript.Arguments
For iintLoop = 0 to objArgs.Count - 1
        WScript.Echo objArgs(iintLoop)
Next

図2 :実行時エラーのダイアログ
図2 :実行時エラーのダイアログ

図3:デバッグ中
図3:デバッグ中

小見出し変数のスコープの注意点

 WSHでは、Visual Basicでいうところのmainプロシージャという考え方はない。スクリプトの先頭部分がグローバル変数領域であり、その直後からのコードが順に実行される。
 この構造は、Visual Basicに慣れているほど、なんとなく気持ち悪いと思える構造だろう。その気持ち悪さを解消するために、あえてmainプロシージャ風のコーディングをするならば、

Dim gintA
Dim gintB
main
Sub Main
    WScript.echo "main"
End Sub

という感じになる。これならば、まだなんとなく納得のゆく形であると思う(付録CD-ROM中SCOPE.VBS)。

小見出しWSHのオブジェクト

 WSHは、VBSの実行環境だけではなく、Windowsを操作するためのActiveXコンポーネントを提供している(表2)。
 いろいろ興味深いオブジェクトやプロパティがあるが、今回注目するのは、CreateObjectメソッドの存在である。このメソッドが存在するということは、ActiveXコンポーネントが使えることを意味する。ExcelやWordだけではなく、Visual Basicで作成したActiveX DLLやActiveX EXEも例外ではない。そして、そのなかには、Oracle Objects for OLEももちろん含まれる。

表2:WScript/CScriptのオブジェクト
WScriptオブジェクト
プロパティ Application Idispatchインターフェイスを返す
Arguments コマンドライン引数の集合であるArgumentsコレクションオブジェクトを返す
FileName Wscript.exeのファイル名を絶対パスで返す
Interactive Wscriptが対話モードかバッチモードのいずれで動作しているかを返す
Name Wscriptの登録名を返す
Path Wscript.exeへの絶対パスを返す
ScriptFullName スクリプトファイル名を絶対パスで返す
ScriptName スクリプトファイル名を返す
Version WSHのバージョンを返す
メソッド CreateObject ActiveXコンポーネントを作成する
Echo ウィンドウまたはDOSプロンプトに文字列を表示する
Quit 指定したエラーコードでスクリプトを終了する
WshArgumentsオブジェクト Wscript.Argumentsプロパティを経由して参照する
プロパティ Item n番目のコマンドライン引数を返す
Count コマンドライン引数の個数を返す

Oracle Objects for OLEを使う

 Oracle Objects for OLEを使って何ができるのかと言えば、Oracleからデータを取得したり更新したりすることがあげられる。そして、Oracle Objects for OLEとWSHを使えば、特別な開発環境をもたなくても、Oracleからデータを取得してExcelに貼り付けるといったことまで可能だ。

小見出し事前の準備

 データベース別名、ユーザーIDおよびパスワードなどを用意して、SQL*Plusなどで接続ができるかを確認する。接続が確認できないときは、Net8 Easy Configrationなどを使って、正しく設定をする。

小見出しサンプルの実行

 データベース別名を「vbm」、ユーザーIDを「scott」、パスワードを「tiger」と定義したとすれば、リスト3を実行するには、DOSプロンプトから、

cscript oo4o.vbs vbm scott/tiger

と入力する。今回のサンプルでは、動作状況がわかりやすいようにechoメソッドでその時々の処理を出力しているので、現在どこの処理を行なっているかが、DOSプロンプト上に表示される。

リスト3:oo4oを使ってOracleから取得したデータをExcelのワークシートに出力
    Dim objSess               ' Oracle Objects for OLEオブジェクト
    Dim objDb                 ' OraDatabase
    Dim objDs                 ' OraDynaset
    Dim objCol                ' OraFields
    Dim strError
    Dim ilngLoop
    Dim strRange
    Dim objXL                 ' Excelオブジェクト
    Dim objArgs               ' コマンドライン引数

    On Error Resume Next

    Set objArgs = WScript.Arguments

' (1)ActiveX DLLであるoo4oを起動します
    WScript.echo "oo4oを起動します。"
    Set objSess = WScript.CreateObject("OracleInProcServer.XOraSession")
    If Err>0 Then
       MsgBox "OracleInProcServer Create Error. Err=" & Err
    End If
' (2)SQL*Net接続文字列を指定して、Oracleと接続します
    WScript.echo "Oracleと接続します。"
    Set objDb = objSess.OpenDatabase(objArgs(0), objArgs(1), CLng(0))
    Call subErrDisp
' (3)SQL文を指定してレコードを取得します
    WScript.echo "SELECT文を実行します。"
    Set objDs = objDb.DbCreateDynaset("select * from emp", CLng(12))
' (4)Excelと接続します
    WScript.echo "Excelと接続します。"
    Set objXL = WScript.CreateObject("Excel.Application")
    objXL.Workbooks.Add
' (5)タイトルを設定します
    WScript.echo "タイトルを設定します。"
    Set objCol = objDs.Fields
    For ilngLoop = 1 To objCol.Count
        objXL.Cells(1, ilngLoop).Value = objCol(ilngLoop - 1).Name
    Next
' (6)CopyToClipboardを使って、シートにデータを配置します
    WScript.echo "データを設定します。"
    objDs.CopyToClipboard -1
    objXL.Range("A2").Select
    objXL.ActiveSheet.Paste
' (7)レコードセットを開放します
    Set objDs = Nothing
' (8)Oracleとの接続を開放します
    Set objDb = Nothing
' (9)oo4oとの接続を開放します
    Set objSess = Nothing

exitOpen:
    On Error Resume Next
    Set objDs = Nothing
    Set objDb = Nothing
    Set objSess = Nothing
    WScript.echo "Excelを表示します。"
    objXL.Visible = True

Sub subErrDisp()
    Dim strErrText         ' エラー文字列
    If objSess.LastServerErr=0 Then
       If objDs.LastServerErr=0 Then
          If Err>0 Then
             strErrText = Error
          End If
       Else
          strErrText = objDs.LastServerErrText
          objDs.LastServerErrReset
       End If
    Else
       strErrText = objSess.LastServerErrText
       objSess.LastServerErrReset
    End If
' エラーがあればそれを表示する
    If strErrText<>"" Then
       WScript.echo strErrText
       WScript.Quit
    End If
End Sub

小見出しウィンドウベースのサンプル

 コマンドラインベースのサンプルと同等の動作をするウィンドウベースのサンプルが、リスト4だ。コマンドライン引数の代わりにInputBoxを使って、データベース別名と接続文字列を取得している。また、echoメソッドの代わりにMsgBoxを使っている。エクスプローラからダブルクリックでスクリプトを実行したいときなどは、このようなプログラミングを行なえばよいだろう。

リスト4:ウィンドウベースで動作するサンプル
    Dim objSess               ' Oracle Objects for OLEオブジェクト
    Dim objDb                 ' OraDatabase
    Dim objDs                 ' OraDynaset
    Dim objCol                ' OraFields
    Dim strError
    Dim ilngLoop
    Dim strRange
    Dim objXL                 ' Excelオブジェクト
    Dim strHost               ' データベース別名
    Dim strConn               ' 接続文字列

    On Error Resume Next

    strHost = InputBox("データベース別名",,"")
    strConn = InputBox("接続文字列",,"")

' (1)ActiveXDLLであるoo4oを起動します
    MsgBox "oo4oを起動します。",vbOkOnly+vbExclamatin
    Set objSess = WScript.CreateObject("OracleInProcServer.XOraSession")
    If Err>0 Then
       MsgBox "OracleInProcServer Create Error. Err=" & Err
    End If
' (2)SQL*Net接続文字列を指定して、Oracleと接続します
    MsgBox "Oracleと接続します。",vbOkOnly+vbExclamatin
    Set objDb = objSess.OpenDatabase(strHost, strConn, CLng(0))
    Call subErrDisp
' (3)SQL文を指定してレコードを取得します
    MsgBox "SELECT文を実行します。",vbOkOnly+vbExclamatin
    Set objDs = objDb.DbCreateDynaset("select * from emp", CLng(12))
' (4)Excelと接続します
    MsgBox "Excelと接続します。",vbOkOnly+vbExclamatin
    Set objXL = WScript.CreateObject("Excel.Application")
    objXL.Workbooks.Add
    objXL.Visible = True
' (5)タイトルを設定します
    MsgBox "タイトルを設定します。",vbOkOnly+vbExclamatin
    Set objCol = objDs.Fields
    For ilngLoop = 1 To objCol.Count
        objXL.Cells(1, ilngLoop).Value = objCol(ilngLoop - 1).Name
    Next
' (6)CopyToClipboardを使って、シートにデータを配置します
    MsgBox "データを設定します。",vbOkOnly+vbExclamatin
    objDs.CopyToClipboard -1
    objXL.Range("A2").Select
    objXL.ActiveSheet.Paste
' (7)レコードセットを開放します
    MsgBox "終了処理をします。",vbOkOnly+vbExclamatin
    Set objDs = Nothing
' (8)Oracleとの接続を開放します
    Set objDb = Nothing
' (9)oo4oとの接続を開放します
    Set objSess = Nothing

exitOpen:
    On Error Resume Next
    Set objDs = Nothing
    Set objDb = Nothing
    Set objSess = Nothing

Sub subErrDisp()
    Dim strErrText         ' エラー文字列
    If objSess.LastServerErr=0 Then
       If objDs.LastServerErr=0 Then
          If Err>0 Then
             strErrText = Error
          End If
       Else
          strErrText = objDs.LastServerErrText
          objDs.LastServerErrReset
       End If
    Else
       strErrText = objSess.LastServerErrText
       objSess.LastServerErrReset
    End If
'エラーがあればそれを表示する
    If strErrText<>"" Then
       MsgBox strErrText,vbOkOnly+vbExclamatin
       WScript.Quit
    End If
End Sub

VBS5.0の新機能
SQLライクに文字列をコードとして扱う

 VBS 5.0では、EVAL関数とExecuteステートメントが追加されている。この2つの機能追加は、VBSに新たな可能性を付加したといえる。
 まずは、EVAL関数から見てみよう(サンプル5、付録CD-ROM中EVAL.VBS)。

Dim intA
Dim intB
intB = 10
intA = EVAL("5 + 9 - intB")
WScript.echo intA

を実行すれば、「4」という解答が表示される。
 これは、文字列を式の右辺としてコーディングしたときと同じ動作をしているということだ。見慣れた形で書き直せば、

Dim intA
Dim intB
intB = 10
intA = 5 + 10 - intB
WScript.echo intA

ということになる。このサンプルでは、EVAL関数の引数として文字列定数を指定しているが、スクリプト中で文字列を組み立てて、それを引数として渡してもよい。つまり、動的に式の右辺を生成して、その計算結果を表示することができるのだ。
 Executeステートメントはさらにすごい。

Dim intA
Dim intB
Dim strProc

intB = 10
strProc = "Sub Proc: intA = 5 + 9 - intB: End Sub"
execute strProc
Proc
WScript.echo intA

を実行すると(付録CD-ROM中exec.vbs)、サンプル5と同様に「4」という答えを得ることができる(図4)。ここで特筆すべきは、Procサブルーチンの内容を文字列として扱えるということだ。あたかもSQL文をプログラム中で組み立ててRDBMSに問い合わせを行なうように、VBS中でプログラムを組み立てて、それを評価してしまうことが可能なのだ。
 これは、LISPやPrologといった人工知能向け言語(とは最近は言わないのだろうか?)で実現されていた言語仕様だ。このような強力な仕様が搭載されてきたことで、VBSは新たな局面を迎えたのかもしれない。

図4:Executeステートメントとその実行例
図4:Executeステートメントとその実行例


VB Magazine ライブラリ| Visual Basic WorkGroup
int21 ホームページ| PCDN ホームページ

PCDN
Copyright (c) 1998 int21 CorporationAll Rights Reserved.
For questions or comments, please send mail to: pcdn@int21.co.jp