VBScript作成テクニックメモ

### ■未定義変数の使用を禁止する
スクリプトの１行目にのみ記載することが許される。
これが記載されている時、Dimで変数宣言していない変数がスクリプト内で使われていると、実行時にその変数が宣言されていないことを表すエラーが発生する。
VBScriptにおいては変数を宣言していなくても実はそのまま使えたりするのだが、誤字脱字等の予想外のエラーで何時間も悩みたくなければ、お守りの呪文として１行目に記載しておくのが吉だろう。
(というかプログラミング学習観点でも変数はあらかじめ宣言と初期化しておくべしという考え方が主流なので、おとなしく記載しておこう。)
```
Option Explicit
```

### ■エラーが発生しても無視して処理を続ける
VBScriptのデフォルト設定は、エラーが発生したらその行でスクリプトの実行は強制的に終了する。
スクリプト内で別プログラムを起動していたりすると、そのコントロールが失われてしまうことになるので、こういった場合にあらかじめこれを宣言しておき、エラー発生時には別プログラムの終了処理等を別途してあげてからスクリプトを終了する等の使い方が想定される。
ただしtry & catchのように雑に便利に使える代物ではないので、判定や条件等は自分でよく考えてから実装する必要がある。
```
On Error Resume Next
```

### ■エラーが発生したら処理を中断する
VBScriptのエラー発生時のデフォルト設定はこっち。
とはいえGoTo 0だけでなく、もっと他に便利に使い込むこともできる。
が、私があまり使わないのでその辺は省略。
```
On Error GoTo 0
```

### ■GUI起動された場合はCUI起動し直す
WScript.FullNameはスクリプトをホストしている実行ファイルのフルパスを指す。
要はwscriptで実行されている場合にこれを検知し、同じスクリプトをcscriptで実行しなおすというロジック。
```
If LCase(Split(WScript.FullName, "\")(UBound(Split(WScript.FullName, "\")))) = "wscript.exe" Then
    Dim oWShell
    Set oWShell = WScript.CreateObject("WScript.Shell")
    oWShell.Run "cscript /nologo " & WScript.ScriptFullName, 1, False
    Set oWShell = Nothing
    WScript.Quit
End If
```
##### ※CUI上に書き込んだり、CUI上の入力を読み取るには以下のような実装が可能
この項目はなんでここに書いたのか謎。転記誤りかな？
いずれ相応しい記載箇所が見つかれば項目を移動することにする。
```
WScript.StdOut.Write "書き込む内容" & vbCr   '←今の行頭にカーソルを移動する
WScript.StdOut.Write "書き込む内容" & vbCrLf '←次の行頭にカーソルを移動する
WScript.StdOut.WriteLine "書き込む内容"      '←次の行頭にカーソルを移動する
変数 = WScript.StdIn.ReadLine
```
##### 【応用】スクリプトの最後等に「Exit」と打ち込まないと終了させてもらえない仕組みを実装する
```
Dim sInput
Do Until False
    WScript.StdOut.Write Now() & " | プログラムが完了しました。Exitコマンドを入力してください..." & vbCrLf
    sInput = WScript.StdIn.ReadLine
    If LCase(Trim(sInput)) = "exit" Then
        WScript.Echo vbCrLf & "終了コマンドが入力されました。 """ & sInput & """" & vbCrLf
        WScript.Quit
    End If
Loop
```

### ■スクリプトを終了する
その名の通り。
If文とかで処理分岐して終了させたり、スクリプトの最後に明示的にここで終了する旨を表すのに使う。
```
WScript.Quit
```

### ■スクリプト起動時の引数を取得する
cArguments(n)は指定した引数のn+1個目(０始まり)を表す。
cArguments.Countで引数の数を数えることも可能
```
Dim cArguments
Set cArguments = WScript.Arguments
```
```
Dim sArg1, sArg2, sArg3
sArg1 = cArguments(0)
sArg2 = cArguments(1)
sArg3 = cArguments(2)
```

### ■IF文を扱う
言語によっては喧嘩しがちな記号どこに入れる問題とかは無いっぽいので平和。
あとよくElseIfの書き方をド忘れする。
```
If str = "TEST" Then
    msgbox "strはTESTです。"
ElseIf str = "TEMP" Then
    msgbox "strはTEMPです。"
Else
    msgbox "strはTESTでもTEMPでもありません。"
End If
```

### ■FOR TO NEXT文を扱う
インデックスは1ずつ増加する。
2ずつ増加とか1ずつ減少とか色々できたはずだけど、可読性悪いしあんまり使わない。
そういう場合は後述のDo While LoopやDo Until Loopを使って処理部分で明示的に計算させるべし。
```
Dim i
For i = 1 To 5
    msgbox i '1～5までの数字が順次表示される
Next
```

### ■FOR EACH IN NEXT文を扱う
コレクションの中から1つずつ順に取り出して処理する方式。
コレクションの中身を一巡するまでループしてくれるので、終了条件にインデックスを計算して～というような面倒な処理が不要なのが良い。
```
Dim cNumber '0～9までの数字が格納されたコレクション
For Each n In cNumber
    msgbox n '0～9までの数字が順次表示される
Next
```

### ■DO WHILE LOOP文を扱う
条件を満たしている間はループ処理を実行する。(つまり条件部分がTRUEなら無限ループする。)
Do While Trueとして意図的に無限ループさせることも可能だが、その場合は処理部分のどこかにIf文で条件を指定し、Exit Doでループを抜けるように実装してあげるのが良い。
```
Dim i : i = 0
Do While i < 10
    i = i + 1
    msgbox i '1～10までの数字が順次表示される
Loop
```

### ■DO UNTIL LOOP文を扱う
条件を満たすまでの間はループ処理を実行する(つまり条件部分がFALSEなら無限ループする。)
Do Whileと同様にDo Until Falseとして意図的に無限ループさせることも可能であり、Exit Doでループから抜けられるのも同じ。
```
Dim i : i = 5
Do Until i = 0
    i = i - 1
    msgbox i '4～0までの数字が順次表示される
Loop
```

### ■1次元配列を扱う
インデックスは０始まり。
```
Dim Ary("aaa","bbb","ccc")

msgbox Ary(0) '"aaa"が表示される
msgbox Ary(1) '"bbb"が表示される
msgbox Ary(2) '"ccc"が表示される
```
##### 【応用】配列の大きさを求める
枠の大きさであって中身の有無は問わない。
For To Nextを使って配列の中身を順に処理する場合等の最大ループ回数としてよく使う。
やっぱりインデックスは０始まりなので、直観と１ズレることに注意。
```
msgbox UBound(Ary) '2が表示される
```

### ■2次元配列を扱う
もちろん各次元のインデックスは０始まり。
```
Dim Ary(9,4) '10行5列

Ary(0,0)="あ"
Ary(0,1)="い"
Ary(0,2)="う"
Ary(0,3)="え"
Ary(0,4)="お"
Ary(1,0)="か"
Ary(1,1)="き"
～～～etc..
```
##### 【応用】配列の大きさを求める
枠の大きさであって中身の有無は問わない。
第2引数で対象とする次元を指定する。
ここでもやっぱりインデックスは０始まりなので、直観と１ズレることに注意。
```
msgbox UBound(Ary,1) '9が表示される
msgbox UBound(Ary,2) '4が表示される
```

### ■FileSystemObjectを扱う
ファイルやフォルダ等、文字通りファイルシステムを扱う際に宣言しておき、用意されたメソッドやプロパティを使う。
```
Dim fso
Set fso = CreateObject("Scripting.FileSystemObject")
～～～色々な処理～～～
Set fso = Nothing '最後にちゃんと片付ける
```
##### 【応用】フォルダの存在有無を判定する
変数に格納したり、If文の条件式によく使う。
フルパス指定の末尾は「\」があってもなくても構わない。
```
fso.FolderExists("判定したいフォルダのフルパス")
'　⇒True ：存在する
'　⇒False：存在しない
```

##### 【応用】ファイルの存在有無を判定する
変数に格納したり、If文の条件式によく使う。
```
fso.FileExists("判定したいファイルのフルパス")
'　⇒True ：存在する
'　⇒False：存在しない
```

##### 【応用】フォルダオブジェクトを取得する
NameプロパティやPathプロパティ、SubFolderプロパティ等を使って、そのフォルダオブジェクトの情報を引き出すのに使う。
もちろんDeleteメソッド等で操作することも可能。
```
Dim oFolder
Set oFolder = fso.GetFolder("取得するフォルダのフルパス")
```

##### 【応用】ファイルオブジェクトを取得する
フォルダオブジェクトとほぼ同様。
```
Dim oFile
Set oFile = fso.GetFile("取得するファイルのフルパス")
```

##### 【応用】テキストファイルを開く
読み込みモードは省略可能であり、これを指定しない場合は○○専用として用途を限定せず、読み込むことも書き込むことも可能な状態で開くことができる。
しかし意図しないファイルの誤操作を防ぐためにも、後続処理の用途によって読み込みモードを使い分けると吉。
```
Dim oFile
Set oFile = fso.OpenTextFile("ファイルのフルパス", "読み込みモード")
'読み込みモード：ForReading   1 ファイルを読み取り専用として開きます。このファイルには書き込むことができません。 
'　　　　　　　　ForWriting   2 ファイルを書き込み専用として開きます。 
'　　　　　　　　ForAppending 8 ファイルを開き、ファイルの最後に追加して書き込みます。 
```

##### 【応用】ファイルのデータを読み込む
ReadLineやReadAllで読み込む際、初期割り当てされたメモリ容量を超えてあまりに大きなデータを読み込もうとすると、割り当てメモリを追加確保しては読み込み再開、しかしまだ足りないのでまた追加確保して…というようにオーバーヘッドに時間と取られることもある。
こういった場合は十分に大きな文字数をReadで指定することで、最初から大きな割り当てメモリを確保した上で読み込み処理を実行することもできる。
ただしこの話はそもそも大容量のデータをVBScriptで扱おうとするなという話であり、普通にVBScriptを使っている分には早々直面するようなものではないので小話程度に受け取られたし。
```
oFile.Read(読み込む文字数)
oFile.ReadLine
oFile.ReadAll
```

##### 【応用】ファイルにデータを書き込む　※対象ファイルは書き込み権限でOpenしておくこと
こちらはあまり補記するようなこともないかな。
```
oFile.Write "書き込む文字列"または、書き込む文字列の格納された変数
oFile.WriteLine "書き込む文字列"または、書き込む文字列の格納された変数
```

### ■ADODB.Streamを扱う
FileSystemObjectでは文字コードUTF-8のファイルを扱えないので、代わりにこちらを使用する。
```
Dim input
Set input = CreateObject("ADODB.Stream")

Set input = Nothing
input.Type = 2 '*** 1:バイナリ, 2:テキスト
input.Charset = "UTF-8"
input.Open
```

##### 【応用】特定のファイルからTypeで指定した形式のデータを読み込む
```
input.LoadFromFile "読み込むファイルのパス"
Dim sText
sText = input.ReadText(-1) '*** -1:全行読み込み, -2:１行読み込み
```
または
```
Do Until input.EOS
    sText = input.ReadText(-2) '*** -1:全行読み込み, -2:１行読み込み
    (読み込んだ変数に対する任意の処理)
Loop
```
※EOSとはEnd Of Streamを意味し、EOF(End Of File)と同じような使い方ができる。

##### 【応用】読み込んだデータをファイルに吐き出す【その１】
上記inputと同様にしてoutputを定義、設定(Openまで)し、以下を記述する。
```
output.WriteText sText, 1 '*** 0:Write, 1:WriteLine
output.SaveToFile "書き出しファイルのパス", 2 '*** 1:上書きを許可しない, 2:上書きを許可する
```
##### 【応用】読み込んだデータをファイルに吐き出す【その２】
FileSystemObjectで作成した書き込みファイルに、変数に格納したデータを書き込む。
```
oFile.Write sText
oFile.WriteLine sText
```

### ■WindowsScriptShellを扱う
外部コマンドをVBScript内で実行したい時に使うものくらいに覚えておけば良い。
```
Dim oWShell
Set oWShell = CreateObject("WScript.Shell")
```
主に使うのはRunメソッド。
第１引数に実行する外部コマンドを指定する。(PATHが通っていればフルパスで指定する必要はない。)
第２引数は外部コマンド実行時のウインドウの状態を指定するものだが、正直使わない。
最低限、０で非表示にできること、５でアクティブウインドウとして表示できることくらいを覚えておけば良いのでは？(一応詳細は後述しておく。)
第３引数の方が重要で、起動した外部コマンドの終了を待機する(True)のか、別線として起動したらほったらかしにする(False)のかを選択できる。
デフォルトはFalseなので、意図しない外部コマンドの実行順序混線なんかが稀によくある。(ちゃんとTrueで制御しよう。)
```
oWShell.Run "notepad"
oWShell.Run "notepad",5,True
```
```
Set oWShell = Nothing '最後にちゃんと片付ける
```
| 第2引数 | ウインドウのスタイル |
|:-:|:--|
| 0 | ウィンドウを非表示にし、別のウィンドウをアクティブにします。 |
| 1 | ウィンドウをアクティブにして表示します。ウィンドウが最小化または最大化されている場合は、元のサイズと位置に戻ります。アプリケーションでウィンドウを最初に表示するときには、このフラグを指定してください。 |
| 2 | ウィンドウをアクティブにし、最小化ウィンドウとして表示します。 |
| 3 | ウィンドウをアクティブにし、最大化ウィンドウとして表示します。 |
| 4 | ウィンドウを最新のサイズと位置で表示します。アクティブなウィンドウは切り替わりません。 |
| 5 | ウィンドウをアクティブにし、現在のサイズと位置で表示します。 |
| 6 | 指定したウィンドウを最小化し、Z オーダー上で次に上位となるウィンドウをアクティブにします。 |
| 7 | ウィンドウを最小化ウィンドウとして表示します。アクティブなウィンドウは切り替わりません。 |
| 8 | ウィンドウを現在の状態で表示します。アクティブなウィンドウは切り替わりません。 |
| 9 | ウィンドウをアクティブにして表示します。ウィンドウが最小化または最大化されている場合は、元のサイズと位置に戻ります。アプリケーションで最小化ウィンドウを復元するときには、このフラグを指定してください。 |
| 10 | アプリケーションを起動したプログラムの状態に基づいて、表示状態を設定します。 |

### ■スクリプトのカレントディレクトリを取得する
求め方の性質上、末尾は「\」となる。
WScript.ScriptFullNameは実行中スクリプトのフルパス、
Wscript.ScriptNameは実行中スクリプトの名前となる。
```
Dim sCrDir
sCrDir = Left(WScript.ScriptFullName, Len(Wscript.ScriptFullName) - Len(Wscript.ScriptName))
```

### ■ログファイルを作成する
```
Dim oLog
Set oLog = fso.CreateTextFile(sCrDir & "LogFile.log")
```
※書き込むメソッドは以下のような実装が可能
```
oLog.Write "書きたい内容"
oLog.WriteLine "書きたい内容"
oLog.Close 'ただのテキストファイルではなぜかSaveは不要だが、Closeは最低限しておこう
Set oLog = Nothing '最後にちゃんと片付ける
```

### ■MS Office Excelを扱う
もう読んで字の如く。あとはお好きにこねてくれ。
```
Dim AppExcel
Set AppExcel = CreateObject("Excel.Application")

Dim oWorkBook
Set oWorkBook = AppExcel.Workbooks.Open("開きたいExcelワークブックファイルのフルパス",,True)

Dim oWorkSheet
Set oWorkSheet = oWorkBook.WorkSheets("開きたいSheet名")
Set oWorkSheet = oWorkBook.WorkSheets(開きたいSheetのn番目数)

oWorkBook.Close(False)
AppExcel.Quit
Set AppExcel = Nothing
```

### ■MS Office Wordを扱う
Excelに同じ。
```
Dim AppWord
Set AppWord = CreateObject("Word.Application")

Dim oDocument
Set oDocument = AppWord.Documents.Open("開きたいWordドキュメントファイルのフルパス",,True)

oDocument.Close(False)
AppWord.Quit
Set AppWord = Nothing
```

### ■MS Office PowerPointを扱う
Excelに同じ。
```
Dim AppPowerPoint
Set AppPowerPoint = CreateObject("PowerPoint.Application")

Dim oPresentation
Set oPresentation = AppPowerPoint.Presentations.Open("開きたいPowerPointプレゼンテーションファイルのフルパス",,True)

oPresentation.Close(False)
AppPowerPoint.Quit
Set AppPowerPoint = Nothing
```