階層を掌握せよ:図面内BOMの構造解析と高精度Excelインベントリ構築への極致
多くのエンジニアは、SolidWorks VBAを「単なる作業の自動化ツール」と考えている。しかし、我々プロフェッショナルにとって、それは「設計データの整合性を担保し、製造プロセスへと繋ぐデータパイプライン」の構築を意味する。
特に、図面(Drawing)内に配置された部品表(BOM)の抽出において、多くの開発者が陥る罠がある。それは、単に「テーブルの文字列を読み取る」という表層的なアプローチだ。マルチレベル(階層構造)を持つ複雑なアセンブリにおいて、単なるセル走査では、部品の親子関係、インデントによる階層表現、そしてデータの欠落を正しく処理することはできない。
本稿では、SolidWorks APIの深層に触れ、メモリ効率を極限まで高め、かつExcelへの出力速度を劇的に向上させる「プロフェッショナル仕様のBOM抽出アルゴリズム」を解説する。
1. アーキテクチャの設計思想:なぜ「セル走査」では足りないのか
図面上のBOMは、`TableAnnotation` オブジェクトとして存在する。しかし、我々が真に操作すべきは、その背後にある `BomTableAnnotation` だ。
一般的な初級者が書くコードは、`TableAnnotation.Text(row, col)` をループで回し、そのままExcelのセルに書き込む。これは、大規模なアセンブリ(数千パーツ)において、ExcelのCOMインターフェースへの過剰な呼び出しを引き起こし、処理速度を致命的に低下させる。
プロフェッショナルが採用すべき戦略:
1. メモリ内バッファリング: SolidWorksからデータを抽出する際は、直接Excelに書き込まず、一度「2次元のVariant配列」に全てのデータを格納する。
2. 階層ロジックの分離: BOMの「Level」列を解析し、インデント構造を論理的に再構築するロジックを、UI(Excel)の操作から完全に切り離す。
3. リソースの厳格な管理: `Set = Nothing` による明示的な解放はもちろん、COMオブジェクトのライフサイクルを意識し、メモリリークを徹底的に排除する。
2. 実装:高精度マルチレベルBOM抽出エンジン
以下に、図面内のBOMを解析し、階層情報を維持したままExcelへ高速転送する、実戦投入レベルのコードを示す。
Option Explicit
' ==============================================================================
' Module: BOM_Extractor_Pro
' Description: 図面内のBOMを解析し、階層構造を維持してExcelへ高速出力する
' Author: Legendary Chief Architect
' ==============================================================================
Sub ExportMultiLevelBOM()
Dim swApp As SldWorks.SldWorks
Dim swModel As SldWorks.ModelDoc2
Dim swDraw As SldWorks.DrawingDoc
Dim swSheet As SldWorks.Sheet
Dim swView As SldWorks.View
Dim swTable As SldWorks.TableAnnotation
Dim swBomTable As SldWorks.BomTableAnnotation
Dim xlApp As Object ' Excel.Application
Dim xlWB As Object ' Excel.Workbook
Dim xlWS As Object ' Excel.Worksheet
' 高速化のための変数
Dim rowCount As Long, colCount As Long
Dim r As Long, c As Long
Dim dataBuffer() As Variant ' メモリ内バッファ(2次元配列)
On Error GoTo ErrorHandler
' 1. SolidWorksへの接続とドキュメントの取得
Set swApp = Application.SldWorks
Set swModel = swApp.ActiveDoc
If swModel Is Nothing Then
MsgBox "SolidWorksドキュメントが開かれていません。", vbCritical
Exit Sub
End If
If swModel.GetType <> swDocDRAWING Then
MsgBox "図面ファイルを開いてください。", vbCritical
Exit Sub
End If
Set swDraw = swModel
' 2. 図面内の最初のBOMテーブルを特定
' ※実務では、特定の名前のビューやテーブルを検索するロジックに拡張すること
Set swView = swDraw.GetFirstView ' FirstViewはシート自体を指す
Set swView = swView.GetNextView ' 次のビュー(図面ビュー)へ
Do While Not swView Is Nothing
Set swTable = swView.GetFirstTableAnnotation
Do While Not swTable Is Nothing
' BomTableAnnotationかどうかを判定
If swTable.Type = swTableAnnotation_Bom Then
Set swBomTable = swTable
Exit Do
End If
Set swTable = swTable.GetNext
Loop
If Not swBomTable Is Nothing Then Exit Do
Set swView = swView.GetNext
Loop
If swBomTable Is Nothing Then
MsgBox "図面内にBOMが見つかりませんでした。", vbExclamation
Exit Sub
End If
' 3. データの解析とバッファリング
' テーブルのサイズを取得
rowCount = swBomTable.RowCount
colCount = swBomTable.ColumnCount
' 配列の再定義 (行, 列)
ReDim dataBuffer(1 To rowCount, 1 To colCount)
' API呼び出し回数を最小化するため、一度に全データを配列へ格納
For r = 1 To rowCount
For c = 1 To colCount
' 文字列として取得
dataBuffer(r, c) = swBomTable.Text(r - 1, c - 1)
Next c
Next r
' 4. Excelへの高速出力 (COMのオーバーヘッドを回避)
Set xlApp = CreateObject("Excel.Application")
xlApp.Visible = True
Set xlWB = xlApp.Workbooks.Add
Set xlWS = xlWB.Sheets(1)
' 配列を一度にセル範囲へ流し込む(これが最速のテクニック)
' Range(Cells, Cells) への一括代入は、セルごとの書き込みより数百倍高速
xlWS.Range(xlWS.Cells(1, 1), xlWS.Cells(rowCount, colCount)).Value = dataBuffer
' 5. 階層構造の視覚的整形 (Post-Processing)
' Level列(仮に1列目とする)に基づいてインデントを適用
Call ApplyHierarchyFormatting(xlWS, rowCount, colCount)
MsgBox "BOMの抽出が完了しました。", vbInformation
CleanUp:
' 6. メモリの明示的解放 (重要)
Set swBomTable = Nothing
Set swTable = Nothing
Set swView = Nothing
Set swDraw = Nothing
Set swModel = Nothing
Set swApp = Nothing
Set xlWS = Nothing
Set xlWB = Nothing
Set xlApp = Nothing
Exit Sub
ErrorHandler:
MsgBox "致命的なエラーが発生しました: " & Err.Description, vbCritical
Resume CleanUp
End Sub
' ------------------------------------------------------------------------------
' Sub: ApplyHierarchyFormatting
' Description: Excel上のLevel列に基づき、インデントと書式を制御する
' ------------------------------------------------------------------------------
Private Sub ApplyHierarchyFormatting(ws As Object, rows As Long, cols As Long)
Dim r As Long
Dim levelVal As Integer
Dim indentSize As Integer
' 画面更新を停止して処理を加速
ws.Application.ScreenUpdating = False
For r = 2 To rows ' ヘッダーを除外
' Level列が1列目にあると仮定
On Error Resume Next
levelVal = CInt(ws.Cells(r, 1).Value)
On Error GoTo 0
If levelVal > 0 Then
' レベルに応じたインデント設定
indentSize = (levelVal - 1) * 2
ws.Cells(r, 2).IndentLevel = indentSize ' 部品名列(2列目)にインデント
' レベルに応じた色の変化(オプション)
If levelVal = 1 Then
ws.Rows(r).Font.Bold = True
End If
End If
Next r
ws.Application.ScreenUpdating = True
End Sub
3. 技術的深掘り:伝説的アーキテクトの知見
3.1. Variant配列による「一括書き込み」の魔力
コード内で `xlWS.Range(…).Value = dataBuffer` と記述している箇所に注目してほしい。これは単なるショートカットではない。
VBAにおいて、Excelのセル一つひとつに対して `Cells(r, c).Value = …` とアクセスすることは、プロセス間通信(IPC)を何度も発生させることを意味する。数千行のデータがある場合、この通信コストだけで数分を浪費する。
`Variant` 配列(メモリ上の連続した領域)を `Range` オブジェクトに一括で流し込む手法は、IPCの回数を「1回」に集約する。これが、プロフェッショナルとアマチュアを分かつ境界線だ。
3.2. オブジェクトのライフサイクルとメモリ管理
SolidWorksのような巨大なCOMサーバーを操作する場合、メモリ管理を怠ると、VBAエディタがフリーズしたり、SolidWorks自体がクラッシュしたりする。
`Set swBomTable = Nothing` のように、使用したオブジェクトを明示的に破棄することは、ガベージコレクション(GC)を待つのではなく、開発者がリソースの解放タイミングを完全に支配することを意味する。特にループ内でオブジェクトを生成する場合は、この原則を鉄の掟として守るべきである。
3.3. レガシー環境への配慮と拡張性
本コードでは `CreateObject(“Excel.Application”)` を使用している。これは、既にExcelが開いている環境でも、あるいは開いていない環境でも動作する堅牢な設計だ。
また、実務においては「Level列」が何列目にあるかは設計者によって異なる。これを定数(`Const COL_LEVEL As Integer = 1`)として定義し、外部設定ファイルや図面プロパティから動的に取得できるように拡張するのが、真のシステム管理者としての振る舞いである。
結論
BOM抽出の自動化は、単なる「作業の削減」ではない。それは、設計データの構造をデジタルツインとして正確に抽出し、後続の工程(ERP、MES、在庫管理)へと流し込むための「データの儀式」である。
APIの挙動を理解し、メモリとパフォーマンスを掌握したとき、君の書くコードは単なるスクリプトを超え、企業の基幹を支えるアーキテクチャへと昇華する。
