諸君、Visioを「ただの作図ソフト」だと思っていないか?
― Shape.ForeignType が分かつ、プロと素人の境界線
開発プロジェクトの現場において、Visio VBAは「最もカオスになりやすい領域」の一つだ。
理由は単純。Visioのドキュメント(.vsdx)は、純粋なベクター図形、外部から貼り付けられたビットマップ、そしてExcelやWordといった「異物」であるOLEオブジェクトが混在する、いわばデータのるつぼだからだ。
素人の書くコードは、すべてのShapeを同じように扱い、OLEオブジェクトに対してベクター図形専用のプロパティを叩き、実行時エラーで轟沈する。あるいは、何千ものシェイプを全件走査して、型判定に無駄なCPUサイクルを浪費する。
本稿では、Visio VBAにおけるオブジェクト識別の「急所」である`Shape.ForeignType`を中心に、堅牢かつ高速な自動化ツールを構築するための極限の知見を伝授する。
—
1. なぜ Shape.Type だけでは不十分なのか
Visioの`Shape.Type`プロパティは、そのシェイプが「グループ(visTypeGroup)」か「単体図形(visTypeShape)」かといった、構造的な分類しか教えてくれない。
問題は、画像やOLEオブジェクトも、Visioの内部的には「`visTypeForeign`(4)」という一つの型にまとめられてしまう点にある。
- ビットマップ画像(PNG/JPG) も `visTypeForeign`
- Excelの埋め込み表 も `visTypeForeign`
- AutoCADの図面データ も `visTypeForeign`
これらを一括りに「外部オブジェクト」として扱うのは、あまりに乱暴だ。Excel OLEであれば、VBAからその中のセルデータにアクセスできるが、ビットマップに対してそれを試みれば、コードは無残にエラーを吐く。
ここで登場するのが、`Shape.ForeignType` プロパティだ。
—
2. ForeignType の正体と識別ロジック
`Shape.ForeignType` は、そのシェイプが「どのような種類の外部データを持っているか」を示すビットフラグを返す。我々が実務で注視すべき定数は以下の3つだ。
1. `visTypeIsEmbedded` (1): ExcelやWordなどのOLE埋め込みオブジェクト。
2. `visTypeIsControl` (2): ActiveXコントロール(ボタンやテキストボックスなど)。
3. `visTypeIsBitmap` (4): ラスタ画像(PNG, JPG, BMPなど)。
設計上の注意:
これらの値は「ビットフラグ」である。つまり、理論上は組み合わさる可能性がある。判定には `AND` 演算を用いるのが、エンタープライズ級のコードにおける作法だ。
—
3. 実践:プロダクション・グレードの識別コード
以下に、ページ内の全シェイプをスキャンし、種類ごとに異なる処理(ログ出力、データ抽出、スタイル変更など)を振り分ける、保守性の高いコード例を示す。
‘
‘ @Description: ページ内のシェイプをForeignTypeに基づき高精度に識別・分類する
‘ @Author: Chief Architect
‘
Public Sub ClassifyAllShapes()
Dim vsoPage As Visio.Page
Dim vsoShape As Visio.Shape
Dim foreignKind As Integer
Set vsoPage = ActivePage
‘ パフォーマンス最適化:画面更新を停止
On Error GoTo ErrorHandler
Application.ScreenUpdating = False
For Each vsoShape In vsoPage.Shapes
‘ 1. まずは基本型をチェック
Select Case vsoShape.Type
Case visTypeGroup
Debug.Print “Group Shape: ” & vsoShape.Name
‘ 必要に応じて再帰的に内部を探索するロジックをここに置く
Case visTypeShape
Debug.Print “Standard Vector Shape: ” & vsoShape.Name
Case visTypeForeign
‘ 2. ここが本題:外部オブジェクトの詳細判定
foreignKind = vsoShape.ForeignType
If (foreignKind And visTypeIsEmbedded) <> 0 Then
HandleOLEObject vsoShape
ElseIf (foreignKind And visTypeIsBitmap) <> 0 Then
HandleBitmapObject vsoShape
ElseIf (foreignKind And visTypeIsControl) <> 0 Then
Debug.Print “ActiveX Control: ” & vsoShape.Name
End If
Case Else
Debug.Print “Other Type: ” & vsoShape.Name
End Select
Next vsoShape
CleanUp:
Application.ScreenUpdating = True
Exit Sub
ErrorHandler:
Debug.Print “Error: ” & Err.Description
Resume CleanUp
End Sub
‘ — OLEオブジェクト(Excel等)の処理 —
Private Sub HandleOLEObject(ByRef shp As Visio.Shape)
‘ 埋め込みオブジェクトのプログラムIDを取得(例: Excel.Sheet.12)
‘ これにより、DB連携時にどのパーサーを使うべきか判断できる
Debug.Print “[OLE] ” & shp.Name & ” (ProgID: ” & shp.ProgID & “)”
‘ ヒント: shp.Object で対象アプリケーションのインスタンスを操作可能だが、
‘ OLEサーバーの起動は極めて重いため、必要な時以外は触れないのが鉄則。
End Sub
‘ — ビットマップ画像の処理 —
Private Sub HandleBitmapObject(ByRef shp As Visio.Shape)
‘ 画像のサイズや解像度チェック、または特定レイヤーへの移動など
Debug.Print “[Bitmap] ” & shp.Name
End Sub
—
4. データベース・ファイル連携における「プロの視点」
実務で「特定の種類のオブジェクトだけを抽出したい」という要求の裏には、必ずデータの整合性という課題が隠れている。
OLEオブジェクト(Excel)を扱う際の罠
`shp.Object` を介してExcelのセルを読み取る場合、ユーザーの環境にそのバージョンのExcelがインストールされている必要がある。また、大量のOLEオブジェクトをループ内で連続して開くと、メモリリークやGhostプロセス(残存するExcel.exe)の原因となる。
対策: 大量処理が必要な場合は、一旦 `shp.Export` で一時画像として書き出すか、あるいは「どうしてもデータが必要な最小限のシェイプ」に絞り込んで処理せよ。
ファイルパスの正規化
`visTypeIsBitmap` のシェイプが「リンク貼り付け」されている場合、元のファイルパスが失われている(あるいは絶対パスで固定されている)ことが多い。ツールを配布する際は、リンク切れを想定したエラーハンドリングが必須だ。
ShapeSheetとの併用
`ForeignType` で識別したあと、さらに詳細な条件(例:特定のユーザー定義セルを持っているか)を `shp.CellExistsU(“User.MyCategory”, 0)` で確認することで、誤判定をゼロに近づけることができる。
—
5. 結論:美しさは「判定の厳密さ」に宿る
「動けばいい」コードと「業務を支える」コードの差は、例外的なデータ(予期せぬ画像の貼り付け、他部署が作ったOLE入りの図面)をいかにエレガントにいなすかにある。
`Shape.ForeignType` を使いこなし、オブジェクトの出自を正確に把握することは、Visio VBAという広大な迷宮を攻略するための第一歩だ。諸君のツールが、単なる自動化を超え、堅牢なデータパイプラインの一部となることを期待する。
「コードの向こう側に、常にデータのライフサイクルを意識せよ。」
これが私からの最後のアドバイスだ。
