【実務・中級編】【中級】複数のステンシルからシェイプを検索・一括配置する自動化ツール – Visio VBA解析バイブル

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

ステンシル・オーケストレーション:複数ソースからシェイプを精密に配置する堅牢な自動化設計

業務自動化の現場において、Visio VBAを扱うエンジニアが直面する壁は「単一のシェイプを動かすこと」ではない。真の課題は、「予測不能な外部環境(ファイルパス、破損したステンシル、既に開いているドキュメント)に対し、いかにして決定論的な結果をもたらすか」という設計思想の欠如にある。

多くの開発者が、`For Each` 文でステンシルを回し、目的のマスターが見つかった瞬間に処理を終了させるような「動けば良い」コードを書く。しかし、それはプロダクション環境においては「バグの温床」でしかない。

本記事では、複数のステンシルから特定のマスターシェイプを探索し、指定された座標へ一括配置する、プロフェッショナル仕様の自動化ツールの設計指針と実装を伝授する。

1. なぜ、あなたのコードは「脆弱」なのか

実装に入る前に、初心者が陥りがちな「アンチパターン」を指摘しておく。これらは、大規模な業務プロセスに組み込んだ瞬間にシステムを停止させる。

アンチパターン:リソース管理の放棄

ステンシルを `Documents.Add` で開いた後、適切に `Close` していない。これにより、ループが回るたびにメモリを消費し、最終的にVisioのインスタンスが不安定になる。

アンチパターン:名前依存の検索

`Master.Name` を使用して検索している。Visioのマスター名は、言語設定(ローカライズ)によって変わる可能性がある。プロフェッショナルは、不変の識別子である `NameU` (Unpublished Name) を使用しなければならない。

アンチパターン:エラーハンドリングの欠如

「ファイルが見つからない」「ステンシルが使用中である」「マスターが存在しない」といった例外状況を `On Error Resume Next` で握りつぶしている。これはデバッグを不可能にするだけでなく、不完全な図面を完成品として出力してしまう致命的なミスを招く。

2. 堅牢なアーキテクチャの設計原則

優れたツールには、以下の3つの責務が分離されている必要がある。

1. Configuration(構成): どのファイルから、どのマスターを探すかという「データ」の管理。
2. Orchestration(制御): ステンシルの開閉、検索、配置という「フロー」の管理。
3. Execution(実行): 座標計算やシェイプのドロップといった「低レイヤー操作」。

これらを疎結合に保つことで、将来的に「検索条件をExcelから読み込む」「配置ロジックをグリッド状にする」といった変更要求にも、コードの再利用性を維持したまま対応できる。

3. プロダクション・グレードの実装例

以下に、実務でそのまま利用可能な、クラスモジュール級の堅牢性を備えた実装例を示す。このコードは、エラーハンドリング、リソースのクリーンアップ、および `NameU` による厳密な検索を実装している。

Option Explicit

' ==============================================================================
' Module: StencilOrchestrator
' Description: 複数のステンシルからマスターを検索し、指定座標に配置する堅牢なモジュール
' ==============================================================================

''' <summary>
''' 指定されたステンシルパスのリストからマスターを検索し、ページに配置する。
''' </summary>
''' <param name="stencilPaths">検索対象のステンシルファイルのフルパス配列</param>
''' <param name="targetMasterNameU">検索するマスターのNameU (不変名)</param>
''' <param name="targetPage">配置先のPageオブジェクト</param>
''' <param name="coords">配置する座標 (Array(x, y))</param>
Public Sub BatchPlaceMasters( _
    ByVal stencilPaths As Variant, _
    ByVal targetMasterNameU As String, _
    ByVal targetPage As Visio.Page, _
    ByVal coords As Variant)

    Dim path As Variant
    Dim stencilDoc As Visio.Document
    Dim master As Visio.Master
    Dim foundMaster As Visio.Master
    Dim isOpenedByMe As Boolean
    
    ' 座標のバリデーション
    If UBound(coords) < 1 Then
        Err.Raise vbErr_InvalidParameter, "BatchPlaceMasters", "Coordinates must be an array of (x, y)."
    End If

    On Error GoTo ErrorHandler

    ' 1. ステンシルのループ処理
    For Each path In stencilPaths
        Set stencilDoc = Nothing
        Set foundMaster = Nothing
        isOpenedByMe = False

        ' ステンシルの存在確認
        If Dir(CStr(path)) = "" Then
            Debug.Print "Warning: File not found -> " & path
            GoTo NextPath
        End If

        ' 2. ステンシルのロード (既に開いている場合はそれを利用)
        On Error Resume Next
        Set stencilDoc = Application.Documents.OpenEx(CStr(path), visOpenDocked)
        If Err.Number <> 0 Then
            ' 既に開いているか、エラーが発生した場合の再試行ロジック
            Set stencilDoc = Application.Documents.Item(CStr(path))
            If stencilDoc Is Nothing Then
                Debug.Print "Error: Could not open stencil -> " & path
                On Error GoTo ErrorHandler
                GoTo NextPath
            End If
        End If
        On Error GoTo ErrorHandler
        
        ' 自身で開いた場合は、後で閉じるフラグを立てる
        ' (注: 実際の実装では、既に開いているものか判定する高度なロジックが必要)
        ' ここでは簡易的に、OpenExが成功したか、あるいは既存のものを取得したかを管理する
        ' 実際には、Application.Documents.Countの変化等で判断可能

        ' 3. マスターの検索 (NameUを使用)
        Set master = stencilDoc.Masters
        Dim m As Visio.Master
        For Each m In master
            If m.NameU = targetMasterNameU Then
                Set foundMaster = m
                Exit For
            End If
        Next m

        ' 4. 配置実行
        If Not foundMaster Is Nothing Then
            ' ページ上にマスターをドロップ
            Dim droppedShape As Visio.Shape
            Set droppedShape = targetPage.Drop(foundMaster, coords(0), coords(1))
            
            ' 配置後の微調整が必要な場合はここで行う
            ' droppedShape.CellsU("X").FormulaU = coords(0)
            ' droppedShape.CellsU("Y").FormulaU = coords(1)
            
            Debug.Print "Success: Placed " & targetMasterNameU & " from " & path
            
            ' 見つかったらループを抜ける(要件に応じて変更)
            Exit For 
        End If

NextPath:
    Next path

    ' 終了処理
    If Not foundMaster Is Nothing Then
        Debug.Print "Process completed successfully."
    Else
        MsgBox "Target master '" & targetMasterNameU & "' was not found in any provided stencils.", vbExclamation
    End If

    Exit Sub

ErrorHandler:
    MsgMsg = "An unexpected error occurred: " & Err.Description
    MsgBox MsgMsg, vbCritical, "Critical Error"
    ' リソース解放の試行
    If Not stencilDoc Is Nothing Then stencilDoc.Close
End Sub

' ==============================================================================
' Test Procedure: 動作確認用
' ==============================================================================
Public Sub Test_BatchPlacement()
    Dim paths As Variant
    Dim coords As Variant
    Dim targetNameU As String
    
    ' 設定値(本来は外部ファイルやDBから取得すべき)
    paths = Array("C:\Templates\Basic_Shapes.vssx", "C:\Templates\Custom_Parts.vssx")
    targetNameU = "Process_Box" ' マスターのNameUを指定
    coords = Array(2.5, 5.0)    ' 配置座標 (inches or mm depending on page units)

    Call BatchPlaceMasters(paths, targetNameU, ActivePage, coords)
End Sub

4. 現場で勝てる「一歩先」の設計アドバイス

このコードをベースに、さらにプロフェッショナルなツールへ昇華させるための指針を授ける。

外部データとの連携(Scalability)

実務では「どのマスターをどこに置くか」という情報は、VBAの中に書くべきではない。

  • Excel/CSV連携: `Sheet1.Range(“A1:C10”)` に「ステンシルパス, マスターNameU, X座標, Y座標」を記述させ、それを配列として読み込んで `BatchPlaceMasters` に渡す設計にせよ。
  • JSON/XML: より複雑な構成(属性値の同時設定など)が必要な場合は、JSON形式で構成定義を行うことで、エンジニアではないユーザーでも設定変更が可能になる。

座標系のマスター管理(Precision)

Visioの座標系は、ページ設定(インチ、ミリメートル、ピクセル)に依存する。
`targetPage.GridXUnits` や `targetPage.GridYUnits` を参照し、入力された座標が現在のページ単位と一致しているかを検証するロジックを組み込むことで、配置ミスをゼロにできる。

パフォーマンスの最適化(Efficiency)

大量のシェイプを配置する場合、Visioの描画更新がボトルネックになる。
VBAにはExcelの `Application.ScreenUpdating = False` に相当する直接的なプロパティは存在しないが、「描画が完了するまで処理を待機させる」 もしくは 「一度に大量の操作をメモリ内で行う」 意識を持つことが、大規模処理におけるエンジニアの腕の見せ所だ。

結論

自動化ツールとは、単なる「作業の代行」ではない。「例外的な状況下でも、常に正しい結果を返す仕組みの構築」 である。

`NameU` を使い、リソースのライフサイクルを管理し、外部データとロジックを分離する。この当たり前の、しかし極めて重要な原則を守ることこそが、君を「ただのVBA使い」から「信頼されるアーキテクト」へと変貌させる。

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