【じゃんけんゲームで入門】VBScript

当記事では、自作したじゃんけんゲームを題材にしてVBScriptの文法や使用するときの注意点などを確認していきます。Windows 10 Home 64bit版環境で検証しています。

ソースコードは以下よりご確認ください。

[rps-like/VBScript at master · tomomoss/rps-like](https://github.com/tomomoss/rps-like/tree/master/VBScript)

## じゃんけんゲームとは

じゃんけんゲームはコンソール上で動作するCUIゲームです。

基本的なルールは既存のじゃんけんそのものです。じゃんけんで勝つごとに1点取得し、対戦相手よりも先に5点先取することで勝者となります。

対戦内容はログファイルに書き出して、後から読み返せるようにします。

## VBScriptとは

VBScript（Microsoft Visual Basic Scripting Edtion）はMicrosoftが開発したプログラミング言語です。名前からも分かるようにVB系言語の1つであり、VB・VBAよりも後発でVB.NETよりは古い言語となります。

1996年8月に **ブラウザのクライアント用言語** としてJScript（JavaScriptのMicrosoft拡張版）とともにInternet Explorer 3.0に搭載されました。それから4ヶ月後の1996年12月にはIIS 3.0に実装された **ASP（ASP.NETではない）の標準言語としても採用** され、さらに、Windows 98から搭載が開始されたスクリプト実行環境である **WSH（Windows Script Host）の言語としてJScriptとともに採用** されるなど、様々な場面で使われました。

その文法は、VBScriptの発表時期を考えるとVB4.0が土台になっていると思われますが――VBからいくつかの機能を削った **簡易版VB** といった具合に仕上がっています。

## 開発環境構築手順

VBScriptはWindows系OSであれば標準搭載されていますので開発環境を構築する必要はありません。また、VBScriptはWindows専用言語ですので非Windows系OSでは開発できません。

## 注意したい仕様

基本的な文法などはGitHubに上げているソースコードを読んでいただくか、あるいは自前で調べていただくとして――ここからは、GitHubに上げているソースコードからは読み取れない仕様や初めてVBScriptを触る人に向けての注意事項を列挙します。

### VBScriptの動かし方

当項目では **ブラウザやASPでの動かし方には触れず、WSH上で動作させることに限定** して解説します。

WSH上でVBScriptを動かすには「.vbs」拡張子を付けたソースファイルを、WSHの本体である **wscript.exeかcscript.exeの第1引数として渡すことで実行** されます。どちらの実行ファイルも（WindowsをCドライブにインストールしていた場合）C:\Windows\System32に置いてあります。

当該ディレクトリには標準でPATHが通っていますのでファイル名だけで呼び出すことができます。

```
C:\Users\tomomoss> wscript.exe .\test.vbs
```

```
C:\Users\tomomoss> cscript.exe .\test.vbs
```

wscript.exeとcscript.exeの違いですが、wscript.exeはウィンドウなどを表示して対話的に処理を行うプログラムのための実行環境です。対してcscript.exeはコンソールを表示・操作することができるので自動化を目的としたプログラム向けの実行環境となります。例えば、以下コードをそれぞれのファイルで実行すると挙動が異なります。

```vb
WScript.Echo("Hello world.")
```

対話的に処理を進めるためのwscript.exeだとウィンドウが表示され、処理の自動化等に向いたcscript.exeではコンソールが表示されます（ダブルクリックで立ち上げた場合は一瞬表示されて、すぐ消えてしまいます）。このように作りたいプログラムのカタチに応じて使用する実行環境を使い分ける必要があります。なお、Windowsの標準設定では.vbsファイルの既定のプログラムにはwscript.exeが指定されていることに留意してください。

ところで、上記コードは双方の実行環境に対応していましたが、片方の実行環境でしか動かない処理もあることに注意してください。以下処理はcscript.exe専用です。

```vb
WScript.StdOut.WriteLine("Hello world.")
```

なお、当記事に掲載しているプログラムはcscript.exeで動かすことを想定しています。

### 標準文字コードはShift JIS

WSHの標準文字コードはShift JISです。ソースファイルはShift JISで書く必要がありますし、スクリプトの処理で出力されるファイルもShift JISになっています。

私もよくやらかしたのですが、Shift JIS以外の文字コードで書いたファイルを実行すると以下のようなエラーが表示される場合があります。

```
C:\Users\tomomoss> cscript //Nologo test.vbs
test.vbs(1,1) Microsoft VBScript コンパイル エラー: 終了していない文字列型の定数です。
```

### エントリポイント

VBScriptのエントリポイントはコードの先頭です。元になったVBではMainプロシージャーから処理が開始されますので、VB経験者は間違えないようにしましょう。

### コメント

コメントにはシングルクォート記号を用います。シングルクォート記号から行末までがコメントの範囲となります。

```vb
' ここがコメントです。
```

なお、ブロックコメント（複数行コメント）は採用されていません。

### キーワードの大文字・小文字は区別しない

VBScriptではキーワードの大文字・小文字を区別しません。

### 文字列リテラル

VBScriptでは文字列リテラルを表す記号は **ダブルクォート記号のみ** となっています。他のプログラミング言語ではシングルクォート記号と併用できたりするものですが、VBScriptではシングルクォート記号がコメント用の記号として使われるために、このような仕様になっているようです。

### データ型の指定はできない

本家VBでは、変数宣言時やプロシージャー定義時の引数などにデータ型を指定することができます。

```vb
Sub WriteMessage(strMessage As String)
    ' ※何らかの処理
End Sub
```

ですが、 **VBScriptではできません** 。プロシージャーの引数のデータ型を縛りたいときはTypeName()とガード節を組み合わせることで代用できるでしょう。

```vb
Sub WriteMessage(strMessage)
    If TypeName(strMessage) <> "String" Then
        Call Err.Raise(1, "第1引数はString型を指定してください")
    End If
    ' ※何らかの処理
End Sub
```

### 変数の初期化はできない

変数の宣言と代入を同時に行うことはできません。それぞれが別の命令文となります。

```vb
' 変数の宣言です。
Dim foo

' 代入です。
foo = "Hello world."
```

### 変数のスコープ

変数のスコープは「スクリプトレベル」と「プロシージャレベル」に分けることができます。前者はグローバル変数、後者はプロシージャがスコープとなる変数です。よって、プロシージャレベルの変数はIf文やFor文などのブロック内外でスコープが区切られることがないことに注意してください。

### プロシージャの引数は標準で参照渡し

VBScriptのプロシージャの引数は標準で参照渡しとなっています。

より丁寧に解説すると――引数の前にByRefキーワードを付けると参照渡し、ByValキーワードを付けると値渡しになります。そして、それらキーワードを省略した場合はByRefキーワードが自動的に指定される仕組みなっているというわけです。

```vb
' 参照渡しです。
Sub TestProcedure(ByRef foo)
    ' ※色々な処理
End Sub

' 値渡しです。
Sub TestProcedure(ByVal foo)
    ' ※色々な処理
End Sub

' キーワードを省略すると参照渡しになります。
Sub TestProcedure(foo)
    ' ※色々な処理
End Sub
```

### クラスの取り扱い

VBScriptでのクラスの仕様は面白いことになっています。

まず、クラスを定義するときですが、なんと **コンストラクタに引数を渡すことができません** 。そのため、必要があれば初期化用メソッドをコンストラクタとは別に実装する必要があるでしょう。

また、その他にも制約が多く――メンバ変数に初期値を指定できなかったり、定数化できなかったり、静的（static）変数を定義できなかったり、アクセス修飾子がPublicとPrivateの2種類しかなかったりします。その割にはGetter・Setter代わりのプロパティが実装されていたりもするのが面白いですね。

```vb
Class TestClass

    ' メンバ変数です。
    Private foo

    ' Getter代わりのプロパティです。
    Public Property Get foo
        foo = woo
    End Property

    ' Class_Initializeという名前のメソッドがコンストラクタになります。
    ' 引数は渡せませんので引数欄は空白にしておいてください。
    Private Sub Class_Initialize()
        ' ※色々な処理
    End Sub

    ' メソッドです。
    Public Sub AddAdmiral(strName)
        mstrName = "Green cow"
    End Sub
End Class
```

続けて、インスタンス化するときの操作ですが、こちらは、インスタンスオブジェクトを格納するための変数の宣言とインスタンス化を **別の命令文で行う必要があります** 。

```vb
' まずは変数の宣言です。
Dim objInstance

' それからインスタンス化です。
' 前述のようにコンストラクタ時に引数を渡すことはできません。
Set objInstance = New TestClass
```

### Select文

VBScript文にはSelect文という命令文があります。これは他のプログラミング言語でいうところのswitch文に相当します。ただし、他のプログラミング言語とは異なり、各caseブロックの末尾にbreak文を記述する必要はありません。ブロックの末尾に到達すると **自動的にSelect文から抜けます** 。

```vb
Select Case 条件式
    Case 判定値1
      ' ※色々な処理
    Case 判定値2
      ' ※色々な処理
    Case 判定値n
      ' ※色々な処理
    Case Else
      ' ※色々な処理
End Select
```

### プロシージャの呼び出し方

プロシージャの呼び出し方はちょっと特殊ですので注意してください。

まず、基本的にはCall文で呼び出します。

```vb
Call TestProcedure(123, "Hello world.")
```

変数にプロシージャの戻り値を代入するときや、関数の引数にプロシージャの戻り値を使うときはCall文を使いません。

```vb
Dim foo
foo = Sample(TestProcedure(123, "Hello world."))
```

そして、おそらくこれは糖衣構文の一種だと思うのですが――CALL文を省略してプロシージャを呼び出すことができます。このときは引数を囲む小括弧を **省略しなければなりません** 。

```vb
TestProcedure 123 "Hello world."
```

### 配列の取り扱い

VBScriptで配列を宣言するときは、原則として配列の長さをあらかじめ定義しておく必要があります。このとき、他のプログラミング言語であれば、その配列の「格納できる要素の数」を定義し、それが長さと同義になるでしょう。ただ、VBScriptの場合は配列の「 **最大のインデックス値** 」を指定します。

参照方法は他のプログラミング言語と同様にインデックス値を指定します。

```vb
' 格納できる要素の数が5つの配列を定義します。
Dim array(4)

' 参照方法は普通です。
array(0)
array(1)
```

また、動的配列を使うこともできます。その場合は配列を定義するときに長さを指定せず、後ほどReDim文を使って配列の長さを指定します。

```vb
' 動的配列を定義します。
Dim array()

' 後ほど長さを指定します。
ReDim array(4)
```

また、既に長さが指定されている配列に対して長さの変更を行うこともできます。が、そのときは **格納されているデータが全て消えてしまいます** 。

```vb
Dim array()
ReDim array(4)
array(0) = "第1要素"
array(1) = "第2要素"

' 配列の長さを5から3に変更します。
ReDim array(2)
```

ただし、配列の長さを変更するときにPreserveキーワードを付けると、データを消さずに配列の長さを変えることができます。しかしながら、長さを変更するときに配列の長さを縮めると、 **変更時に切り捨てられたインデックスに格納されていたデータはもちろん消えてしまいます** ので、そこは注意してください。

```vb
Dim array(2)
array(0) = "消えない要素"
array(1) = "消えない要素"
array(2) = "消える要素"

' 長さを3から2に変更します。
ReDim Preserve array(1)
```

## 私が意識していること

VBScriptを使うときに私が意識していることを列挙します。1つ前の「注意したい仕様」を読んでいることを前提とした記述になっています。

### コーディング規約

VBScriptでプログラミングする際、どのようなコーディング規約を採用するかは難しい問題です。一応、Microsoft公式のコーディング規約は存在しますが、ページのレビュー日付が2009年と非常に古いのが気になるところです。

[VBScript のコード記述規則 | Microsoft Docs](https://docs.microsoft.com/ja-jp/previous-versions/windows/scripting/cc392255(v=msdn.10))

上記ページで書かれている内容は、同じVB系言語の最新型であるVB.NETのコーディング規約と矛盾するところがあります。そのためVBScriptのコーディング規約か、VB.NETのコーディング規約か、どちらを採用するべきか悩ましいところです。

今回の試みでは、できるだけVBScriptらしく書くために上記ページの内容を採用しました。今から10年以上前のMicrosoftがどのようなコードを綺麗なコードと考えていたかを体験する良い機会でした。

そういうわけで当記事掲載のプログラムは基本的にMicrosoft公式VBScriptコーディング規約に準拠しています。ただ、当該ページで触れられていないところは、私が過去担当していたVB6.0製業務システムの書き方で補完しています。

### Option Explicitは必ず宣言する

VB系言語の伝統ともいえるOption Explicitは必ず宣言しておきます。

### 原則参照渡し

VBScriptではプロシージャに引数を渡すと標準では参照渡しになります。

個人的には参照渡しというものは嫌いなのですがVBScriptでは標準動作になっておりますので、VBScriptを使うときは従ったほうが良いのかもしれません。

### プロシージャ外の変数の操作

プロシージャのなかから外部の変数を操作するときは、できるだけ直接的に参照・代入するのが良い気がします。

というのも、Microsoft公式のコーディング規約のドキュメントコメントを読んだかぎりでは直接操作することを想定しているようなので、公式の流儀に倣うほうが無難だと考えます。個人的には嫌いな方法ではありますが仕方ありません。

### 識別子

変数以外は原則アッパーキャメルケースで記述し、アンダースコアなどの記号を使用しないようにします。

### CALL文は省略しない

私は、表記の統一度合いを高めるためにCALL文を省略したプロシージャの呼び出し方は使いません。