【テクニカル・上級編】【上級】バージョン管理システム(Git等)と連携したステンシル配布の自動化 – Visio VBA解析バイブル

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

信頼のアーキテクチャ:Git連携によるステンシル配布の自動化とオブジェクト制御の極致

図面は、単なる描画結果ではない。それはシステムの設計思想を具現化した「データ」であり、その構成要素であるステンシル(Stencil)は、組織における設計言語の「スキーマ」である。

多くの現場では、このスキーマの更新が属人的なファイル配布や、不完全な共有フォルダ運用に依存している。その結果、エンジニア間で「シェイプのプロパティが異なる」「最新のロジックが反映されていない」といった、低レベルながらも致命的な不整合が多発する。

我々アーキテクトの責務は、この「設計言語の不整合」を技術的に排除することにある。本稿では、Gitを信頼の単一ソース(Single Source of Truth)とし、Visio VBAと外部プロセスを高度に連携させることで、ステンシルのライフサイクルを完全に自動化する実装手法を詳解する。

1. システムアーキテクチャの設計思想

VBA単体でGit操作を完結させようとするのは、設計思想として誤りだ。VBAはあくまで「ホストアプリケーション(Visio)の制御」に専念すべきであり、バージョン管理の複雑なロジックは、Git CLIやPythonといった、その領域に特化したツールに委ねるべきである。

推奨されるワークフロー

1. Central Repository: Git LFS(Large File Storage)を用いたバイナリ管理。
2. Local Agent (Sync Engine): Git CLIを実行し、ローカルのステンシルディレクトリを最新状態に同期する。
3. Visio Integration (VBA): 起動時または定期的に、ローカルファイルのハッシュ値またはバージョンファイルを検証し、必要に応じてSync Engineを呼び出し、ステンシルを再ロードする。

2. Windows APIによるファイル占有の検知

ステンシルを更新しようとする際、最大の障壁となるのは「ファイルロック」である。Visioがステンシルを開いている状態でGitによるファイル上書き(`git pull`)を試みると、アクセス拒否エラーが発生し、同期プロセスは崩壊する。

これをエレガントに回避するため、Windows APIを用いてファイルが他のプロセスによってロックされているかを事前に判定する。

' --- Module: Win32Api ---
Option Explicit

' ファイルのアクセス権限をチェックするためのAPI宣言
Private Declare PtrSafe Function CreateFile Lib "kernel32" Alias "CreateFileA" ( _
    ByVal lpFileName As String, _
    ByVal dwDesiredAccess As Long, _
    ByVal dwShareMode As Long, _
    ByVal lpSecurityAttributes As LongPtr, _
    ByVal dwCreationDisposition As Long, _
    ByVal dwFlagsAndAttributes As Long, _
    ByVal hTemplateFile As LongPtr) As LongPtr

Private Declare PtrSafe Function CloseHandle Lib "kernel32" (ByVal hObject As LongPtr) As Long

Private Const GENERIC_READ As Long = &H80000000
Private Const FILE_SHARE_READ As Long = &H1
Private Const OPEN_EXISTING As Long = 3
Private Const INVALID_HANDLE_VALUE As LongPtr = -1

''' <summary>
''' 指定したファイルが他のプロセスによってロックされているかを確認する
''' </summary>
Public Function IsFileLocked(ByVal filePath As String) As Boolean
    Dim hFile As LongPtr
    
    ' 読み取り専用モードでオープンを試みる
    hFile = CreateFile(filePath, GENERIC_READ, 0, 0, OPEN_EXISTING, 0, 0)
    
    If hFile = INVALID_HANDLE_VALUE Then
        ' オープンに失敗した場合は、何らかの理由(ロック含む)でアクセス不能
        IsFileLocked = True
    Else
        ' 成功した場合はハンドルを閉じて解放
        CloseHandle hFile
        IsFileLocked = False
    End If
End Function

3. 実装:同期エンジンとVBAのブリッジ

次に、外部のGitプロセスを呼び出し、同期を完結させるコアロジックを実装する。ここで重要なのは、`WScript.Shell` を利用した非同期実行の制御と、実行結果の待機である。

' --- Module: StencilManager ---
Option Explicit

Private Const STENCIL_PATH As String = "C:\Work\Project\Stencils\"
Private Const GIT_EXE As String = "git.exe"

''' <summary>
''' ステンシルの同期プロセスを開始する
''' </summary>
Public Sub SynchronizeStencils()
    Dim shell As Object
    Set shell = CreateObject("WScript.Shell")
    
    Dim cmd As String
    Dim waitOnReturn As Boolean: waitOnReturn = True
    Dim windowStyle As Integer: windowStyle = 1 ' 黒いコンソールを表示
    
    ' 1. まず、現在のディレクトリへ移動し、git pullを実行するコマンドを構築
    ' ※ 実際の実装では、作業ディレクトリを明示的に指定すべき
    cmd = "cmd /c cd /d " & STENCIL_PATH & " && " & GIT_EXE & " pull"
    
    Debug.Print "Executing: " & cmd
    
    ' 2. ファイルロックのチェック(重要:同期前に既存のステンシルを閉じる必要がある)
    ' ここでは簡略化しているが、本来は開いているDocumentオブジェクトをすべて走査すべき
    
    On Error GoTo ErrorHandler
    
    ' 3. Git Pullの実行
    Dim resultCode As Integer
    resultCode = shell.Run(cmd, windowStyle, waitOnReturn)
    
    If resultCode <> 0 Then
        Err.Raise vbObjectError + 513, "SyncEngine", "Git pull failed with code: " & resultCode
    End If
    
    MsgBox "Stencil synchronization completed successfully.", vbInformation
    Exit Sub

ErrorHandler:
    MsgBox "Critical Error during synchronization: " & Err.Description, vbCritical
End Sub

4. 極限のメモリ管理:オブジェクトの明示的解放

ステンシルを更新した後、古いステンシルを閉じ、新しいステンシルを読み込む。このプロセスにおいて、最も多くのエンジニアが陥る罠が、COMオブジェクトの参照カウントの残留によるメモリリークである。

Visioの`Document`オブジェクト(ステンシル)は、単に`Close`を呼ぶだけでは不十分な場合がある。特に、マスターシェイプの操作や複雑なプロパティ設定を行っている場合、参照がメモリ内に残り続け、Visioのプロセスが肥大化し、最終的にクラッシュを招く。

以下に、安全な「リロード」のパターンを示す。

''' <summary>
''' ステンシルを安全にリロードする
''' </summary>
Public Sub ReloadStencil(ByVal stencilName As String)
    Dim vsoStencil As Visio.Document
    Dim docObj As Visio.Document
    Dim found As Boolean: found = False
    
    ' 1. 現在開いているステンシルを特定し、閉じる
    For Each docObj In Visio.Documents
        ' ステンシルファイル(.vssx)かどうかを判定
        If InStr(1, docObj.Name, ".vssx", vbTextCompare) > 0 Then
            If docObj.Name = stencilName Then
                Set vsoStencil = docObj
                found = True
                Exit For
            End If
        End If
    Next docObj
    
    ' 2. 既存のステンシルがある場合は、明示的に閉じる
    If found Then
        vsoStencil.Close
        ' --- 伝説的アーキテクトの知見 ---
        ' オブジェクトをNothingに設定し、参照カウントを確実に減らす
        Set vsoStencil = Nothing 
        ' COMの解放を促すため、極めて短い待機を入れることも検討すべき(レガシー環境対策)
        DoEvents 
    End If
    
    ' 3. 新しいステンシルをロード
    On Error Resume Next
    Set vsoStencil = Visio.Documents.OpenEx(STENCIL_PATH & stencilName, visOpenDocked)
    On Error GoTo 0
    
    If vsoStencil Is Nothing Then
        Err.Raise vbObjectError + 514, "ReloadStencil", "Failed to open stencil: " & stencilName
    End If
    
    Set vsoStencil = Nothing
    Debug.Print "Stencil " & stencilName & " reloaded successfully."
End Sub

アーキテクトのメモ:なぜ `Set = Nothing` なのか

VBAは内部的にCOM(Component Object Model)を利用している。`Set vsoStencil = docObj` とした瞬間、そのオブジェクトへの参照カウントがインクリメントされる。`docObj.Close` を実行しても、変数 `vsoStencil` がそのオブジェクトを指し続けている限り、メモリ上の実体は破棄されない。
大規模なシステムにおいて、この「幽霊オブジェクト」が蓄積されると、Visioは「メモリ不足」を吐いて沈黙する。「オブジェクトのライフサイクルは、スコープではなく、明示的な破棄によって制御せよ」。これが鉄則だ。

5. 結論:堅牢な自動化に向けて

ステンシルの自動同期は、単なる「便利機能」ではない。それは、大規模な設計プロジェクトにおいて「全員が同じ定義に基づいて思考していること」を保証するためのインフラストラクチャである。

1. Git LFSによるバイナリの整合性担保。
2. Windows APIによるファイルロックの事前検知。
3. WScript.Shellによる外部プロセスとの疎結合な連携。
4. 厳格な参照管理によるメモリリークの徹底排除。

これらを組み合わせることで、初めて「レガシーなツール」であるVisioを、現代的なDevOpsのサイクルへと組み込むことが可能になる。技術の真髄は、華やかな新機能の導入ではなく、こうした地味で、しかし逃れられない「不整合」を排除する執念にある。

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