【実務・中級編】【初心者向け】ページ削除前に「確認ダイアログ」を表示し、誤操作によるデータ損失を防止する安全策 – Visio VBA解析バイブル

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

MsgBoxの先へ:データ消失を防ぐ「防御的設計」としてのページ削除ロジック

業務自動化の現場において、最も恐ろしいのは「バグ」ではない。「ユーザーによる、意図しない破壊的操作」だ。

Visioを用いた大規模な図面管理ツールを構築している際、スクリプトの不備やユーザーの誤操作によって、設計の根幹を成す「インデックスページ」や「マスター図面」が削除されてしまう。これは単なるミスではなく、業務プロセスの停止、ひいてはデータの完全性の喪失を意味する。

多くの開発者は、`MsgBox` を使って「削除しますか?」と聞くだけのコードを書く。しかし、プロフェッショナルなアーキテクトは、それを「確認」とは呼ばない。それは単なる「警告」に過ぎず、防御策としては極めて脆弱だ。

本稿では、Visio VBAにおけるページ削除処理を、単なるスクリプトから「堅牢なシステム」へと昇華させるための設計思想と実装パターンを伝授する。

1. なぜ、あなたの「確認ダイアログ」は役に立たないのか

初心者が書く典型的なコードは、以下の通りだ。

' --- 典型的な「脆弱な」実装例 ---
Sub NaiveDeletePage()
    Dim pgName As String
    pgName = InputBox("削除するページ名を入力してください")
    
    ' 単なる確認。これでは「守るべきページ」の概念がない
    If MsgBox(pgName & " を削除してもよろしいですか?", vbYesNo) = vbYes Then
        ActiveDocument.Pages(pgName).Delete
    End If
End Sub

このコードには、設計上の致命的な欠陥が3つある。

1. ポリシーの欠如: 「絶対に消してはいけないページ(Cover, Index等)」というビジネスルールがコードに組み込まれていない。
2. エラーハンドリングの不在: 指定したページが存在しない場合、あるいはページがロックされている場合に、ランタイムエラーでアプリケーションがクラッシュする。
3. 状態の不整合: 削除に失敗した場合のロールバックや、ユーザーへの適切なフィードバックが考慮されていない。

設計者として、我々は「ユーザーがYesを押すこと」を前提にしてはならない。「ユーザーが何をすべきではないか」をシステム側で強制することこそが、真の自動化エンジニアの責務である。

2. アーキテクチャ設計:防御的削除パターン

堅牢なツールを作るためには、削除ロジックを「UI層(メッセージ表示)」と「ビジネスロジック層(削除可否判定)」に分離すべきだ。

設計の3原則

1. Blacklist Management (保護リストの管理): 削除を禁止するページ名を定義する。
2. Existence Verification (存在確認): オブジェクトのライフサイクルを考慮し、対象が確実に存在することを確認する。
3. Exception Safety (例外安全性): 予期せぬエラー(ページロック、参照エラー等)を捕捉し、システムを正常な状態に保つ。

3. プロダクションクオリティの実装例

以下に、そのまま業務ツールに組み込めるレベルの、保守性と堅牢性を備えたコードを示す。

Option Explicit

''' <summary>
''' ページ削除の安全な実行を行うメインプロシージャ
''' </summary>
Public Sub SafeDeletePageWorkflow()
    Dim targetPageName As String
    Dim targetPage As Visio.Page
    
    ' 1. ユーザーからの入力を取得
    targetPageName = InputBox("削除するページ名を入力してください:", "ページ削除確認")
    
    ' キャンセルまたは空入力のハンドリング
    If targetPageName = "" Then Exit Sub
    
    On Error GoTo ErrorHandler
    
    ' 2. オブジェクトの取得と存在確認
    ' ActiveDocument.Pages(name) は存在しない場合にエラーを投げるため、慎重に扱う
    Set targetPage = GetPageByName(targetPageName)
    
    If targetPage Is Nothing Then
        MsgBox "エラー: 指定されたページ '" & targetPageName & "' は見つかりませんでした。", vbCritical, "対象不在"
        Exit Sub
    End If
    
    ' 3. ビジネスルールに基づく削除可否判定(ガードレール)
    If IsProtectedPage(targetPage) Then
        MsgBox "拒否: '" & targetPageName & "' はシステム保護対象ページです。" & vbCrLf & _
               "このページを削除することはできません。", vbExclamation, "保護されたページ"
        Exit Sub
    End If
    
    ' 4. 最終確認ダイアログ
    If MsgBox("警告: ページ '" & targetPageName & "' を完全に削除します。" & vbCrLf & _
              "この操作は取り消せません。よろしいですか?", vbYesNo + vbWarning, "最終確認") = vbNo Then
        Exit Sub
    End If
    
    ' 5. 実行
    targetPage.Delete
    MsgBox "ページ '" & targetPageName & "' を正常に削除しました。", vbInformation, "完了"

CleanExit:
    Exit Sub

ErrorHandler:
    ' 予期せぬエラー(ページがロックされている、メモリ不足等)をキャッチ
    MsgBox "致命的なエラーが発生しました。" & vbCrLf & _
           "Error No: " & Err.Number & vbCrLf & _
           "Description: " & Err.Description, vbCritical, "システムエラー"
    Resume CleanExit
End Sub

''' <summary>
''' ページ名からPageオブジェクトを取得する(エラー回避型)
''' </summary>
Private Function GetPageByName(ByVal pName As String) As Visio.Page
    Dim pg As Visio.Page
    On Error Resume Next
    Set pg = ActiveDocument.Pages(pName)
    On Error GoTo 0
    Set GetPageByName = pg
End Function

''' <summary>
''' 削除禁止ページ(ブラックリスト)の判定ロジック
''' </summary>
Private Function IsProtectedPage(ByRef pPage As Visio.Page) As Boolean
    ' 本来はここを外部ファイル(ExcelやConfigファイル)から読み込むのが理想的
    ' Hardcodingは保守性を下げるが、まずはロジックの分離を示す
    Select Case pPage.Name
        Case "Cover", "Index", "Master_Template", "Sheet1"
            IsProtectedPage = True
        Case Else
            IsProtectedPage = False
    End Select
End Function

4. さらに高度な設計へのステップアップ

実務で「伝説的なツール」を目指すなら、以下の要素を検討せよ。

A. 設定の外部化(Database/File Integration)

上記のコードでは、保護対象のページ名を `Select Case` でハードコードしている。これは小規模なら良いが、大規模プロジェクトでは悪手だ。

  • Excel連携: 運用担当者がExcelシートに「保護ページリスト」を記載し、VBAが起動時にそれを読み込む。
  • Configファイル: JSONやXML形式で設定を保持する。

これにより、「プログラムを書き換えずに、運用ルールを変更できる」という、エンジニアリングの極致とも言える柔軟性が手に入る。

B. 削除前の「スナップショット」概念

VisioにはUndo(元に戻す)機能があるが、VBAによる操作はUndoスタックを汚染したり、複雑な操作の後に機能しなかったりすることがある。
極めて重要なデータを取り扱う場合、削除を実行する直前に、`ActiveDocument.SaveCopyAs` を用いて、現在の状態を一時ファイルとしてバックアップするロジックを組み込むのが、真のプロフェッショナルだ。

C. オブジェクトの依存関係のチェック

もし、削除しようとしているページ内の図形が、他のページから「参照(Link)」されている場合、単なる削除はデータの不整合を招く。
`Shape.Stencil` や `Shape.ConnectionPoints` を走査し、依存関係がある場合は「削除の前にリンクを解除してください」というメッセージを出す、あるいは自動でリンクを解除するロジックこそが、アーキテクトの腕の見せ所である。

結論

「動けばいい」コードを書く者は、単なる作業員だ。「壊れない」コードを書く者が、エンジニアである。

`MsgBox` は、ユーザーとの対話の手段であって、システムの安全性を担保する手段ではない。ビジネスルールをコードに落とし込み、例外を予測し、エラーを制御する。この一連の「防御的設計」こそが、あなたの構築する自動化ツールを、現場で信頼される「製品」へと変えるのだ。

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