止まらない自動化は、恐怖を招く。VBScriptで実装する「擬似トランザクション」の極意
こんにちは。現場の設計図を書き、自動化の荒波を乗り越えてきたチーフアーキテクトです。
マクロの記録や、ネットで見つけた断片的なコードを組み合わせて「動いた!」と喜ぶ。そのステップは素晴らしい第一歩です。しかし、あなたが「プロフェッショナル」へと脱皮しようとする時、必ず直面する壁があります。
それは、「途中でエラーが起きたら、中途半端に書き換わったファイルはどうなるのか?」という問題です。
例えば、10個のファイルを移動させる処理を書いていたとしましょう。5個目でネットワークエラーが発生して止まったら? 残りの5個は元の場所にあり、最初の5個は移動済み……。この「不整合な状態」を放置することは、業務システムにおいては致命的なリスクになります。
今日は、そんなリスクを回避するための高度な設計思想、「ロールバック(取り消し)機能」を備えた擬似トランザクションマネージャーの実装方法を伝授します。
1. 「トランザクション」という考え方
データベースの世界では、「すべて成功するか、さもなくば、何もなかったことにするか(All or Nothing)」という原則があります。これを「トランザクション」と呼びます。
VBScriptの標準的なファイル操作(FSO: FileSystemObject)には、この機能は備わっていません。`fso.DeleteFile` を実行した瞬間、ファイルは消え去ります。やり直し(ロールバック)は不可能です。
では、どうするか?
私たちは「操作の履歴を記録しておき、エラーが起きたら履歴を逆順に辿って、元の状態に復元する」という仕組みを自前で作るのです。
ロールバックの設計図
1. 記録 (Log): ファイルを動かしたり消したりする前に、「何をしたか」をスタック(積み重ね)に保存する。
2. 実行 (Execute): 実際のファイル操作を行う。
3. 失敗 (Rollback): エラーが発生したら、スタックを「最後に行った操作」から順に遡り、逆の操作(移動したなら戻す、作ったなら消す)を実行する。
4. 完了 (Commit): すべて成功したら、履歴を破棄する。
2. 実装:TransactionManagerクラス
それでは、実際に使えるコードを見ていきましょう。ここでは、初心者の方でも構造が理解しやすいよう、クラス形式でまとめました。
' =================================================================
' Class: TransactionManager
' Purpose: ファイル操作の履歴を管理し、エラー時にロールバックを行う
' =================================================================
Class TransactionManager
Private m_fso
Private m_undoStack ' 操作履歴を格納するコレクション
' 操作の種類を定義する定数
Private Const OP_CREATE = 1
Private Const OP_MOVE = 2
Private Const Op_DELETE = 3 ' 今回は「一時退避」による擬似削除
Private Sub Class_Initialize()
Set m_fso = CreateObject("Scripting.FileSystemObject")
Set m_undoStack = CreateObject("Scripting.Collection")
End Sub
' --- 公開メソッド ---
' ファイルを作成する
Public Function CreateTextFile(path, content)
On Error Resume Next
Dim ts
Set ts = m_fso.CreateTextFile(path, True)
If Err.Number = 0 Then
ts.WriteLine(content)
ts.Close
' 履歴に追加: 「作成した」ので、ロールバック時は「削除」する
m_undoStack.Add Array(OP_CREATE, path, "")
CreateTextFile = True
Else
CreateTextFile = False
End If
On Error GoTo 0
End Function
' ファイルを移動する
Public Sub MoveFile(srcPath, destPath)
On Error Resume Next
m_fso.MoveFile srcPath, destPath
If Err.Number = 0 Then
' 履歴に追加: 「移動した」ので、ロールバック時は「元の場所へ戻す」
m_undoStack.Add Array(OP_MOVE, destPath, srcPath)
Else
Err.Raise Err.Number, "TransactionManager.MoveFile", "移動に失敗しました: " & srcPath
End If
On Error GoTo 0
End Sub
' ファイルを削除する (※重要: 本当に消さずに一時フォルダへ退避させる)
Public Sub DeleteFile(path)
On Error Resume Next
Dim tempPath
tempPath = path & ".bak_" & Timer ' 一時的な名前で退避
m_fso.MoveFile path, tempPath
If Err.Number = 0 Then
' 履歴に追加: 「退避した」ので、ロールバック時は「元の場所へ戻す」
m_undoStack.Add Array(Op_DELETE, path, tempPath)
Else
Err.Raise Err.Number, "TransactionManager.DeleteFile", "削除(退避)に失敗しました: " & path
End If
On Error GoTo 0
End Sub
' ロールバックを実行する
Public Sub Rollback()
WScript.Echo "--- エラー検知: ロールバックを開始します ---"
Dim i, action, p1, p2
' スタックを後ろ(最後に行った操作)から順に処理
For i = m_undoStack.Count To 1 Step -1
action = m_undoStack(i)(0)
p1 = m_undoStack(i)(1)
p2 = m_undoStack(i)(2)
Select Case action
Case OP_CREATE
' 作成したファイルは削除して元に戻す
If m_fso.FileExists(p1) Then m_fso.DeleteFile(p1)
Case OP_MOVE
' 移動したファイルは元の場所へ戻す (p1=移動先, p2=元)
If m_fso.FileExists(p1) Then m_fso.MoveFile p1, p2
Case Op_DELETE
' 退避させたファイルを元の場所へ戻す (p1=元, p2=退避先)
If m_fso.FileExists(p2) Then m_fso.MoveFile p2, p1
End Select
Next
m_undoStack.RemoveAll
WScript.Echo "--- ロールバック完了 ---"
End Sub
' 全て成功した時に履歴をクリアする
Public Sub Commit()
m_undoStack.RemoveAll
WScript.Echo "--- 全ての処理が成功しました (Commit) ---"
End Sub
End Class
3. 実際に使ってみる(テストシナリオ)
このクラスをどう使うのか。エラーが起きることを前提とした、プロフェッショナルなテストコードを書いてみましょう。
' =================================================================
' メイン処理
' =================================================================
Dim tm
Set tm = New TransactionManager
On Error Resume Next ' エラーハンドリング開始
WScript.Echo "処理を開始します..."
' 1. ファイル作成
tm.CreateTextFile "test1.txt", "Hello World"
' 2. ファイル移動
tm.MoveFile "test1.txt", "test1_moved.txt"
' 3. 【わざとエラーを起こす】存在しないファイルを削除しようとする
WScript.Echo "意図的なエラーを発生させます..."
tm.DeleteFile "non_existent_file.txt"
' エラーチェック
If Err.Number <> 0 Then
WScript.Echo "エラー発生: " & Err.Description
tm.Rollback ' ここで魔法が起きる!
Else
tm.Commit
End If
If Err.Number <> 0 Then
Err.Clear
End If
Set tm = Nothing
WScript.Echo "プログラムを終了します。"
4. エンジニアの視点:ここが設計のキモだ!
このコードには、単なる「動くコード」を超えた、いくつかの重要な設計思想が隠されています。
① 「削除」は「移動」である
ここが最大のポイントです。
`fso.DeleteFile` を直接使ってしまうと、ロールバック時に復元できません。だから、このクラスでは「削除とは、ファイルを一時的な名前(.bak)に変えて、別の場所に隠しておくことである」と再定義しています。これこそが、オブジェクトのライフサイクルを制御するプロの技です。
② スタック(LIFO: Last-In, First-Out)の利用
ロールバックは必ず「最後に行った操作から逆順に」行わなければなりません。
1. ファイルを作る
2. ファイルを移動する
という順で行った場合、元に戻すときは
1. ファイルを戻す
2. ファイルを消す
という順序です。これを実現するために、`Collection` オブジェクトをスタックとして利用しています。
③ `On Error Resume Next` の使いどころ
VBScriptにおけるエラーハンドリングは非常に強力ですが、使い所を間違えると「エラーを無視して突き進む、制御不能なプログラム」になります。
このクラスでは、「個別の操作でエラーが起きたら、即座にエラー番号を発生させて(Err.Raise)、呼び出し元に通知する」という設計にしています。これにより、呼び出し元は「あ、失敗したからロールバックしよう」という判断ができるのです。
最後に:ここをクリアすれば、あなたはもう初級者ではない
いかがでしたか?
「ただファイルを操作する」のと、「操作の失敗を想定して、元の状態を保証する仕組みを作る」のとでは、コードの品質が天と地ほど違います。
この「擬似トランザクション」の考え方は、ファイル操作だけでなく、Excel操作や、設定ファイルの書き換えなど、あらゆる自動化に応用できます。
「もし、ここで失敗したらどうなるだろう?」
常にこの問いを自分に投げかけることが、伝説的なエンジニアへの第一歩です。
ここをクリアすれば、VBScriptの基本はバッチリですよ。自信を持って、次の自動化に挑んでください。応援しています!
