【実務・中級編】Shape.ForeignTypeによる挿入オブジェクト判定:OLEオブジェクト・画像・図形の識別処理 – Visio VBA解析バイブル

スポンサーリンク

諸君、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という広大な迷宮を攻略するための第一歩だ。諸君のツールが、単なる自動化を超え、堅牢なデータパイプラインの一部となることを期待する。

「コードの向こう側に、常にデータのライフサイクルを意識せよ。」
これが私からの最後のアドバイスだ。

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