【テクニカル・上級編】【中級】マスターシェイプの更新チェックと最新版への置き換えロジック – Visio VBA解析バイブル

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

マスターシェイプの「魂」を継承せよ:Visioにおける大規模図面更新のアーキテクチャ

図面管理の現場において、ステンシル(Stencil)の進化は避けられない。数年前に定義された「サーバー」のマスターシェイプと、最新のコンプライアンスに基づいた「サーバー」では、描画レイヤーも、保持すべきシェイプデータ(Shape Data)の定義も異なる。

多くのエンジニアは、古いシェイプを見つけると「手動で削除して、新しいマスターから引き直す」という、あまりに低レイヤーで、かつリスクの高い作業を選択する。しかし、数千個のシェイプが散りばめられた大規模なネットワーク構成図やシステムアーキテクチャ図において、そのような作業は非効率の極みであり、さらに致命的な欠陥を招く。「接続関係(Connectors)の断絶」と「ユーザー定義データの消失」だ。

本稿では、単なる「置換」ではない。既存シェイプが保持する「状態(State)」を、新しいマスターへと「移行(Migration)」させるための、極限の更新ロジックについて解説する。

1. 識別戦略:バージョン情報の「真実の源泉(Source of Truth)」

まず、どのシェイプが「古い」のかを判定するロジックが必要だ。マスターの名称(Name)に頼るのは素人の仕事だ。マスターの名前は、組織の命名規則変更によって容易に変遷する。

プロフェッショナルな設計においては、マスターシェイプ自体に「スキーマバージョン」を管理するShape Data(ユーザー定義セル)を埋め込んでおく。

バージョン判定の設計指針

  • メタデータの埋め込み: マスター作成時に `Version` という名前の `ShapeData` セルを定義し、`1.0.0` のようなセマンティックバージョニングを付与する。
  • 判定ロジック: 既存シェイプが持つ `Version` セルの値と、現在のステンシルが規定する `MinimumRequiredVersion` を比較する。

2. 置換のアルゴリズム:破壊的置換から「状態の継承」へ

単純に `Shape.Delete` して新しいマスターを `Drop` する手法は、そのシェイプに接続されていたコネクタを「迷子」にし、図面の整合性を破壊する。

我々が実装すべきは、「新しいシェイプを生成し、古いシェイプの属性を転写した後、古いシェイプを破棄する」というトランザクション的なプロセスである。

継承すべきプロパティの優先順位

1. ShapeData (User-defined cells): IPアドレス、ホスト名、ラック番号などの重要データ。
2. Text: ユーザーが書き加えた注釈。
3. Geometry (Transform): 位置(X, Y)、回転、スケール。
4. Connections: これが最難関だ。既存の `Connect` インデックスを追跡し、新しいシェイプへ再接続する必要がある。

3. 実装:極限のマスター更新エンジン

以下に、メモリ管理とオブジェクトのライフサイクルを意識した、実戦的なVBAコードの雛形を示す。ここでは、最も重要かつ難易度の高い「ShapeDataの移行」と「位置の継承」に焦点を当てる。

Option Explicit

' ==============================================================================
' Module: MasterMigrationEngine
' Description: 既存シェイプの属性を新しいマスターへ継承・置換する
' Author: Legendary Chief Architect
' ==============================================================================

Public Sub UpdateAllShapesInDocument(ByVal targetStencilName As String, ByVal minRequiredVersion As String)
    Dim vsoPage As Visio.Page
    Dim vsoShape As Visio.Shape
    Dim vsoNewMaster As Visio.Master
    Dim vsoStencil As Visio.Document
    Dim i As Long
    
    ' 高速化のための環境設定
    On Error GoTo ErrorHandler
    Application.ScreenUpdating = False
    Application.DebugPrint "Migration Started..."

    ' 1. ステンシルのロード(メモリ効率を考慮し、参照を最小限にする)
    Set vsoStencil = Application.Documents.OpenEx(targetStencilName, visOpenDocked)
    If vsoStencil Is Nothing Then
        Err.Raise 513, , "Target stencil not found: " & targetStencilName
    End If

    ' 2. 全ページを走査
    For Each vsoPage In Application.ActiveDocument.Pages
        DebugPrint "Processing Page: " & vsoPage.Name
        
        ' シェイプの削除によるインデックスの狂いを防ぐため、後ろからループ
        For i = vsoPage.Shapes.Count To 1 Step -1
            Set vsoShape = vsoPage.Shapes.Item(i)
            
            ' 3. バージョンチェック
            If IsOutdated(vsoShape, minRequiredVersion) Then
                DebugPrint "Updating Shape: " & vsoShape.Name & " (ID: " & vsoShape.ID & ")"
                
                ' マスターの特定と置換実行
                ReplaceShapeWithNewMaster vsoPage, vsoShape, vsoStencil
            End If
        Next i
    Next vsoPage

CleanUp:
    Application.ScreenUpdating = True
    ' COMオブジェクトの明示的な解放(メモリリーク防止)
    Set vsoStencil = Nothing
    DebugPrint "Migration Completed Successfully."
    Exit Sub

ErrorHandler:
    MsgBox "Critical Error during migration: " & Err.Description, vbCritical
    Resume CleanUp
End Sub

' ------------------------------------------------------------------------------
' 関数: IsOutdated
' 目的: ShapeDataのVersionセルを確認し、更新が必要か判定する
' ------------------------------------------------------------------------------
Private Function IsOutdated(ByVal vsoShape As Visio.Shape, ByVal minVersion As String) As Boolean
    Dim vsoCell As Visio.Cell
    On Error Resume Next
    ' ShapeData内の"Version"セルを参照
    Set vsoCell = vsoShape.CellsU("Prop.Version")
    On Error GoTo 0
    
    If vsoCell Is Nothing Then
        ' バージョン情報を持っていないシェイプは「古い」とみなす(保守的アプローチ)
        IsOutdated = True
    Else
        ' 簡易的な文字列比較(本来はセマンティックバージョニングの数値比較を推奨)
        If vsoCell.ResultIU < VersionToInteger(minVersion) Then
            IsOutdated = True
        Else
            IsOutdated = False
        End If
    End If
End Function

' ------------------------------------------------------------------------------
' 関数: ReplaceShapeWithNewMaster
' 目的: 新しいマスターをドロップし、属性を転写して旧シェイプを消去する
' ------------------------------------------------------------------------------
Private Sub ReplaceShapeWithNewMaster(ByVal vsoPage As Visio.Page, _
                                     ByVal oldShape As Visio.Shape, _
                                     ByVal vsoStencil As Visio.Document)
    Dim vsoNewMaster As Visio.Master
    Dim newShape As Visio.Shape
    Dim vsoCell As Visio.Cell
    Dim i As Integer
    
    ' 新しいマスターの取得(名称は要件に合わせて調整)
    ' ここでは、ステンシル内の特定のマスターを探すロジックとする
    Set vsoNewMaster = vsoStencil.Masters.Item("New_Standard_Server")
    
    ' 1. 新しいシェイプを既存の位置にドロップ
    ' oldShape.X, oldShape.Y を使用して座標を完全に一致させる
    Set newShape = vsoPage.Drop(vsoNewMaster, oldShape.X, oldShape.Y)
    
    ' 2. 座標・回転の精密な同期
    newShape.CellsU("PinX").FormulaU = oldShape.CellsU("PinX").FormulaU
    newShape.CellsU("PinY").FormulaU = oldShape.CellsU("PinY").FormulaU
    newShape.CellsU("Angle").FormulaU = oldShape.CellsU("Angle").FormulaU
    
    ' 3. テキストの継承
    newShape.Text = oldShape.Text
    
    ' 4. ShapeData (User-defined cells) の転送
    ' 全てのPropセルを走査し、新しいマスターに存在する項目のみ転送する
    For i = 0 To oldShape.CellCount - 1
        Set vsoCell = oldShape.CellsU(i)
        ' Propで始まるセル(ShapeData)のみを対象とする
        If Left(vsoCell.NameU, 5) = "Prop." Then
            On Error Resume Next
            ' 新しいシェイプに同じ名前のセルが存在する場合のみ値をコピー
            newShape.CellsU(vsoCell.NameU).FormulaU = vsoCell.FormulaU
            On Error GoTo 0
        End If
    Next i
    
    ' 5. 旧シェイプの破棄
    ' ※ 本来はここでコネクタの再接続ロジック(newShape.GlueTo)を挟む必要がある
    oldShape.Delete
    
    ' メモリ管理
    Set newShape = Nothing
    Set oldShape = Nothing
End Sub

' 補助関数: バージョン文字列を比較可能な整数に変換(簡易実装)
Private Function VersionToInteger(ByVal v As String) As Long
    ' "1.2.0" -> 10200 等に変換するロジックを実装
    ' 本来は正規表現やSplitを使用
    VersionToInteger = CLng(Replace(v, ".", ""))
End Function

4. アーキテクトの視点:メモリ管理とパフォーマンスの極意

このコードを商用環境、特に大規模な図面(数千ページ、数万シェイプ)に適用する場合、以下の3点に留意せよ。

Windows APIとCOMのライフサイクル

VBAはCOM(Component Object Model)の上で動いている。`Set vsoShape = Nothing` を明示的に行わない場合、Visioのプロセスメモリ内に参照カウントが残り続け、長時間稼働する自動化システムでは、メモリ不足による `Out of Memory` エラーを引き起こす。特にループ内で大量のオブジェクトを生成・破棄する場合は、極めて慎重に管理せよ。

トランザクションの整合性

図面の更新中にエラーが発生した場合、図面は「半分だけ新しいマスター、半分だけ古いマスター」という、カオスな状態に陥る。
実運用では、更新処理を開始する前に `ActiveDocument.SaveCopyAs` を用いてスナップショットを作成するか、エラーハンドリング内で「ロールバック(Undo)」を実装する、あるいは「更新ログ」を外部ファイルに出力して、失敗したシェイプを特定できるように設計するのがプロの仕事だ。

スケーラビリティへの配慮

`Application.ScreenUpdating = False` は必須だが、それだけでは不十分だ。数万個のシェイプを処理する場合、OSのメッセージキューが詰まることがある。適宜 `DoEvents` を挟む検討をせよ。ただし、`DoEvents` はコンテキストスイッチのコストを伴うため、頻度には細心の注意を払え。

結言

マスターシェイプの更新は、単なる「見た目の変更」ではない。それは、図面という名の「構造化データ」のスキーマ・マイグレーションである。
エンジニアが提供すべきは、使い勝手の良いツールではない。「データの整合性を担保し、物理的な接続を維持し、不変の属性を次世代へ受け渡す」という、極めて強固なアーキテクチャなのだ。

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