【実務・中級編】【中級】ステンシルファイルを読み取り専用で開き、競合を防ぐ運用設計 – Visio VBA解析バイブル

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

ステンシル競合を根絶せよ:Visio VBAにおける「読み取り専用」運用の極限設計

Visioでの業務自動化ツールを開発している際、最もエンジニアを絶望させるのは「個人の環境では動くが、共有サーバーに配置した途端にファイルロックで落ちる」という現象だ。

特にステンシル(`.vssx`)の管理は、多くの開発者が軽視しがちな急所である。単純に `Documents.Open` で開けばいいと考えているなら、それは「いつ爆発してもおかしくない時限爆弾」をユーザーに配布しているに等しい。

なぜなら、Visioのステンシルは内部的に「ドキュメント」として扱われるため、不用意なオープンは書き込み権限の要求や、一時ファイルの作成によるファイルロックを引き起こすからだ。複数ユーザーが同時にツールを起動した瞬間、誰かが「編集モード」で開いたステンシルが、他の全ユーザーのプロセスを止める。

今回は、プロの現場で必須となる「読み取り専用でのステンシル展開と、メモリリークのないライフサイクル管理」について、その設計思想と実装を伝授する。

1. 致命的な設計ミス:なぜ `Documents.Open` では不十分なのか

多くの入門書やネット上のサンプルコードでは、以下のように記述されている。

' --- 素人設計の例:危険なコード ---
Set stn = Documents.Open("C:\Stencils\MyShapes.vssx")

この書き方には、プロダクション環境における3つの致命的な欠陥がある。

1. 排他制御の欠如: ユーザーが誤ってステンシルを直接開いて編集していた場合、VBA側でランタイムエラーが発生し、ツールがクラッシュする。
2. 意図しない保存要求: 実行中にシェイプのプロパティを書き換えた場合、終了時に「保存しますか?」というダイアログが表示され、自動化処理がストップする。
3. リソースの残留: `Close` 処理を適切に記述していない場合、バックグラウンドでステンシルが開きっぱなしになり、メモリを食いつぶす。

プロの設計では、`Documents.OpenEx` を使い、明示的に「読み取り専用」かつ「非表示」でロードさせる。

2. 堅牢なステンシル管理のアーキテクチャ

我々が目指すのは、「ツール起動時に静かに読み込み、必要なマスターをドロップし、用が済んだら痕跡を残さず消える」というステートレスに近い運用だ。

設計の要諦

  • ReadOnly = True: ファイルロックを完全に回避する。
  • ReadOnlyを強制するフラグ管理: `OpenEx` の引数で読み取り専用を確定させる。
  • 参照の局所化: ステンシルオブジェクトをグローバル変数にせず、必要なスコープ内でのみ保持し、確実に `Close` する。

3. プロダクションコード:`StencilManager` 実装例

以下に、実務でそのまま利用可能な、堅牢なステンシル読み込みロジックを提示する。このコードは、ファイル存在チェックから、読み取り専用オープン、そしてエラーハンドリングまでを完結させている。

Option Explicit

''' <summary>
''' 指定したステンシルからマスターシェイプを安全にページへドロップする
''' </summary>
''' <param name="stencilPath">ステンシルファイルのフルパス</param>
''' <param name="masterName">読み込むマスターシェイプの名前</param>
Public Sub SafeDropMaster(ByVal stencilPath As String, ByVal masterName As String)
    Dim docStencil As Visio.Document
    Dim shpMaster As Visio.Master
    Dim shpInstance As Visio.Shape
    
    On Error GoTo ErrorHandler

    ' 1. ファイルの存在確認(ネットワークパス等の遅延や欠落への対策)
    If Dir(stencilPath) = "" Then
        Err.Raise vbObjectError + 513, "SafeDropMaster", "ステンシルファイルが見つかりません: " & stencilPath
    End If

    ' 2. 【極限の知見】OpenExによる読み取り専用オープン
    ' 第2引数に読み取り専用を指定し、ユーザーへのダイアログ表示を完全に封殺する
    ' VisOpenReadonly = 1 (定数)
    Set docStencil = Documents.OpenEx(stencilPath, VisOpenReadonly)

    ' 3. マスターの抽出
    On Error Resume Next
    Set shpMaster = docStencil.Masters(masterName)
    On Error GoTo ErrorHandler

    If shpMaster Is Nothing Then
        Err.Raise vbObjectError + 514, "SafeDropMaster", "指定されたマスター '" & masterName & "' がステンシル内に存在しません。"
    End If

    ' 4. アクティブページへのドロップ
    ' 座標(0,0)に配置し、その後で位置調整を行うのが定石
    Set shpInstance = ActivePage.Drop(shpMaster, 0, 0)
    
    ' ここで shpInstance に対して個別の設定(名前変更や位置調整)を行う
    shpInstance.Text = "自動配置済み: " & masterName

CleanUp:
    ' 5. 【重要】ステンシルのクローズ
    ' 読み取り専用で開いていても、明示的に閉じなければVisioプロセスに残り続ける
    ' 保存せずに閉じる(visCloseNoSave)を徹底する
    If Not docStencil Is Nothing Then
        docStencil.Close visCloseNoSave
        Set docStencil = Nothing
    End If
    Exit Sub

ErrorHandler:
    MsgBox "【システムエラー】" & vbCrLf & Err.Description, vbCritical, "Stencil Load Error"
    Resume CleanUp
End Sub

''' <summary>
''' ツール実行メインルーチン
''' </summary>
Public Sub Main()
    ' 設定ファイルや定数からパスを管理することを強く推奨する
    Const STENCIL_PATH As String = "S:\Common\StandardShapes.vssx"
    
    ' 複数のマスターを順次読み込む例
    Call SafeDropMaster(STENCIL_PATH, "Server_Icon")
    Call SafeDropMaster(STENCIL_PATH, "DB_Icon")
    Call SafeDropMaster(STENCIL_PATH, "Firewall_Icon")
    
    MsgBox "全てのコンポーネントを安全に読み込みました。", vbInformation
End Sub

4. チーフアーキテクトが教える「さらに上の運用」

上記のコードで動作は安定するが、さらに大規模なツールや、数千個のシェイプを扱う場合は以下の最適化を検討せよ。

① オープン回数の最小化(キャッシュ戦略)

上記の `SafeDropMaster` は、呼び出すたびにステンシルを開閉している。これは堅牢だが、マスター数が数十個になるとディスクI/Oがボトルネックとなり、動作がもっさりする。
その場合は、「セッション開始時に一度だけ読み取り専用で開き、全てのドロップが終わった後に一括して閉じる」というラッパー関数を設計せよ。

② ネットワーク遅延への耐性

ステンシルを共有サーバー(`\\Server\Share\…`)に置いている場合、ネットワークの一時的な瞬断で `Documents.OpenEx` がフリーズすることがある。
極限まで安定性を高めるなら、「ツール起動時に `FileSystemObject` でステンシルをローカルの `%TEMP%` フォルダにコピーし、ローカルパスから読み込む」という手法を採る。これにより、ネットワーク起因のクラッシュを物理的に排除できる。

③ マスター名への依存を排除する

`docStencil.Masters(“MasterName”)` という指定は、誰かがステンシル内のシェイプ名を変更した瞬間に壊れる。
保守性を高めるなら、ステンシル側のマスターに「UserDefinedProperty(ユーザー定義プロパティ)」を仕込み、IDなどの不変な値で検索して取得するロジックを組み込むべきだ。

結論

Visio VBAにおいて、ステンシル管理を適当にする者は、必ず「原因不明のファイルロック」という泥沼にハマる。

1. `OpenEx` で `VisOpenReadonly` を指定せよ。
2. `visCloseNoSave` で確実にリソースを解放せよ。
3. ネットワークパスへの直接依存を避け、エラーハンドリングを完備せよ。

この3点を徹底するだけで、あなたのツールは「たまに動くマクロ」から「信頼される業務システム」へと昇華する。コードの美しさとは、機能の多さではなく、いかに「想定外の事態で死なないか」にあることを忘れるな。

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