序論:文字列という名の「静かなる毒」を排除せよ
Visio VBAの世界において、初心者が最初に覚える呪文は `Shape.CellsU(“PinX”).Formula = “10mm”` だ。
しかし、数千、数万のシェイプを動的に生成・制御するエンタープライズレベルの自動化システムにおいて、この「文字列指定(CellsU)」はパフォーマンスを蝕む「静かなる毒」となる。
COMオートメーションの境界を越える際、文字列のパース、ハッシュテーブルのルックアップ、そして内部的な名前解決のオーバーヘッドは無視できない。大規模な図面生成において、処理時間の8割がこの「名前解決」に費やされている事実は、あまり知られていない。
真のアーキテクトは、ShapeSheetの構造を数値として捉える。
本稿では、`CellsRowIndex` と `CellsColumnIndex` を駆使し、`CellsSRC` (Section, Row, Column) プロパティによる極限の高速アクセスを実現する技法を伝授する。これは、レガシーシステムの延命から、最新のデジタルツイン基盤構築まで、すべてのVisio開発者が到達すべき「終着駅」である。
—
1. 概念の転換:CellsUからCellsSRCへ
VisioのShapeSheetは、本質的には多次元配列の集合体だ。
`CellsU` は人間にとっての利便性(エイリアス)を提供するが、内部エンジンが求めているのは常に Section (セクション), Row (行), Column (列) の3つの整数インデックスである。
なぜ CellsSRC なのか?
1. パースの回避: 文字列 “PinX” を内部定数 `visColumnXFormPinX` に変換するコストをゼロにする。
2. ループの効率化: セクションや行をループで回す際、文字列結合(”Prop.” & i)という最悪のパフォーマンス低下要因を排除できる。
3. 型安全性の確保: 整数定数(Enum)を使用することで、スペルミスによるランタイムエラーをコンパイル時に抑制できる。
—
2. 実践:インデックスの事前解決(Pre-fetching)
カスタムプロパティ(Shape Data)やユーザー定義セル(User-defined Cells)にアクセスする場合、行インデックスは可変だ。これをループの中で毎回 `CellsU` で取得するのは愚策である。
`CellsRowIndex` と `CellsColumnIndex` を使い、初期化フェーズでインデックスをキャッシュするのがプロの流儀だ。
高速アクセス・エンジンの実装例
以下のコードは、大量のシェイプに対してShape Dataを書き込む際の、最も洗練されたアプローチの一つである。
Option Explicit
‘ Visio Type Library Constants を参照していることを前提とする
‘ 大量処理時のパフォーマンスを極限まで高めるためのクラス構造例
Public Sub HighSpeedShapeSheetUpdate()
Dim vsoApp As Visio.Application
Set vsoApp = Visio.Application
‘ 高速化の鉄則:描画とイベントを一時停止
vsoApp.ScreenUpdating = 0
vsoApp.EventsEnabled = 0
Dim vsoPage As Visio.Page
Set vsoPage = vsoApp.ActivePage
Dim vsoShape As Visio.Shape
Dim i As Long
‘ 事前にインデックスを解決しておく
‘ 毎回 “Prop.Cost” を探すのではなく、一度だけインデックスを取得する
‘ ここでは代表的なシェイプからインデックスをサンプリングする
Dim targetRowIndex As Integer
Dim targetColIndex As Integer
‘ サンプルシェイプでインデックスを特定(存在しない場合はエラーハンドリングが必要)
On Error Resume Next
‘ セクション: visSectionProp (Shape Data)
‘ 行の名前: “Cost”
‘ 列: 通常のプロパティ値は visCustPropsValue (0)
Set vsoShape = vsoPage.Shapes(1)
targetRowIndex = vsoShape.CellsRowIndex(“Prop.Cost”)
targetColIndex = visCustPropsValue
On Error GoTo 0
‘ メインループ:SRC指定による高速書き込み
For Each vsoShape In vsoPage.Shapes
‘ CellsSRC(Section, Row, Column)
‘ 文字列パースが発生しないため、CellsUより数倍高速に動作する
vsoShape.CellsSRC(visSectionProp, targetRowIndex, targetColIndex).FormulaU = “1500”
Next vsoShape
‘ 状態の復元
vsoApp.ScreenUpdating = 1
vsoApp.EventsEnabled = 1
MsgBox “処理完了”, vbInformation
End Sub
—
3. 極限の最適化:SetFormulas によるバッチ処理
`CellsSRC` よりもさらに高速な手法が存在する。それが `SetFormulas` (または `SetResults`) だ。
これは、1つのシェイプ内の複数のセル、あるいは複数のシェイプのセルに対して、一度のCOMコールで値を流し込む手法である。COMのラウンドトリップを最小化することこそが、Windows APIレベルでの最適化に匹敵する効果をもたらす。
SetFormulas の実装パターン
Public Sub UltraFastBatchUpdate(vsoShape As Visio.Shape)
‘ SRCの配列を準備する (Section, Row, Column)
‘ 3つの要素で1つのセルを指定する
Dim srcStream() As Integer
ReDim srcStream(0 To (3 3) – 1) ‘ 3つのセルを更新する場合
‘ 1つ目のセル: PinX
srcStream(0) = visSectionObject: srcStream(1) = visRowXFormOut: srcStream(2) = visXFormPinX
‘ 2つ目のセル: PinY
srcStream(3) = visSectionObject: srcStream(4) = visRowXFormOut: srcStream(5) = visXFormPinY
‘ 3つ目のセル: Width
srcStream(6) = visSectionObject: srcStream(7) = visRowXFormOut: srcStream(8) = visXFormWidth
‘ 設定する数式の配列
Dim formulas(0 To 2) As Variant
formulas(0) = “100mm”
formulas(1) = “100mm”
formulas(2) = “50mm”
‘ 一括実行。個別に CellsSRC を叩くよりも圧倒的に速い
vsoShape.SetFormulas srcStream, formulas, 0
End Sub
—
4. アーキテクトの視点:メモリ管理とライフサイクル
VBAは参照カウント方式のメモリ管理を行っているが、Visioのような巨大なオブジェクトモデルを扱う場合、明示的な解放(`Set object = Nothing`)を怠ると、予期せぬメモリリークや、アプリケーション終了時のプロセス残留を招く。
特にループ内での `Shape` オブジェクトの取得は、一時的なラッパーオブジェクトを大量に生成する。
大規模処理では、一定数(例えば1000シェイプごと)に `DoEvents` を挟み、OS側に制御を戻すことで、メッセージキューの滞留を防ぎ、システムの安定性を確保するのがベテランの作法だ。
—
5. 結論:技術の真髄は「見えないコスト」の削減にあり
`CellsU` から `CellsSRC` への移行は、単なる書き換えではない。それは、高レベル抽象化の裏に隠された「計算コスト」を意識し、ハードウェアに近いレイヤーで最適化を行うという、エンジニアとしての矜持である。
1. 文字列を排除せよ: `CellsSRC` を標準とせよ。
2. インデックスをキャッシュせよ: `CellsRowIndex` はループの外で解決せよ。
3. 境界を越える回数を減らせ: `SetFormulas` による一括制御を検討せよ。
この3原則を遵守するだけで、あなたの構築するVisioシステムは、他を圧倒するパフォーマンスと堅牢性を手に入れるだろう。レガシーなVBAという環境にあって、モダンな最適化思想を貫くこと。それこそが、我々チーフアーキテクトに課せられた使命である。
