【入門編】【上級プロフェッショナル向け】FSOファイル操作の「ロールバック(取り消し)機能」を備えた擬似トランザクションマネージャー – VBScript (Visual Basic Scripting Edition)解析バイブル

スポンサーリンク
スポンサーリンク

止まらない自動化は、恐怖を招く。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の基本はバッチリですよ。自信を持って、次の自動化に挑んでください。応援しています!

タイトルとURLをコピーしました