【テクニカル・上級編】【中級者向け】図面内の部品表(BOM)から指定したカスタムプロパティ列を抽出してCSV自動エクスポート – SolidWorks VBA解析バイブル

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

SolidWorks VBA Masterclass: 高信頼・高パフォーマンスなBOMデータ抽出エンジンを構築する

業務自動化の現場において、「動くコード」を書くことは容易だ。しかし、数千枚の図面をバッチ処理し、ERP(基幹業務システム)へデータを流し込むような、エンタープライズ・レベルの「システム」を構築する場合、話は全く別物になる。

多くの開発者が陥る罠がある。それは、APIの参照方法の脆弱性と、COMオブジェクトのライフサイクル管理の欠如だ。これらは、単一の実行では表面化しない。しかし、長時間稼働する自動化プロセスや、大量のファイルをループ処理する環境下では、メモリリークによる「SolidWorksのハングアップ」や、テンプレート変更による「インデックスずれによるデータ破損」という形で、致命的なコストとなって跳ね返ってくる。

本稿では、図面内のBOM(部品表)から特定のカスタムプロパティを抽出し、CSVとして出力する、極めて堅牢な実装手法について解説する。単なるコードの提示ではない。プロフェッショナルが守るべき「設計思想」を伝授する。

1. 脆弱な実装を排除する:インデックス指定の罠

一般的な実装では、BOMの列を「1列目、2列目……」と、そのインデックス番号(Integer)で指定する。これは、設計変更やテンプレートの微修正に対して極めて脆弱だ。

真に堅牢なアーキテクチャでは、「ヘッダー文字列による動的なカラム・マッピング」を採用すべきである。列の並び順が変わろうとも、システムは目的のデータ(「材質」や「重量」など)を正確に捕捉し続けなければならない。

2. メモリ管理の極意:COMオブジェクトの解放

SolidWorks VBAは、背後で巨大なCOM(Component Object Model)インターフェースを操作している。ループ内で `swTableAnnotation` や `swView` といったオブジェクトを生成し、明示的に解放(`Set obj = Nothing`)せずに放置すれば、メモリ使用量は指数関数的に増大する。

特に、図面を開き、BOMを読み、閉じる……というサイクルを繰り返すバッチ処理において、オブジェクトの解放漏れは、OSのページファイルへの過度なスワップを招き、最終的にプロセスを強制終了させる。

3. 実装例:堅牢なBOMエクスポート・エンジン

以下に、シニアエンジニアが実装すべきコードの骨格を示す。このコードは、`Microsoft Scripting Runtime`(FileSystemObject)への参照設定を前提としている。

Option Explicit

' ==============================================================================
' Module: BOMExtractor
' Description: 図面内のBOMから特定カラムを抽出し、CSV出力する高信頼性モジュール
' Author: Chief Architect
' ==============================================================================

' 列名をキーとしてインデックスを保持するためのDictionary
Private m_ColumnMap As Object

Sub Main()
    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 csvPath As String
    Dim targetColumns As Variant
    Dim i As Integer
    
    ' 初期設定
    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
    csvPath = swModel.GetPathName & "\BOM_Export_" & Format(Now, "yyyymmdd_hhmmss") & ".csv"
    
    ' 抽出したいカスタムプロパティのカラム名を定義
    ' ここに定義した文字列がBOMのヘッダーと完全一致する必要がある
    targetColumns = Array("部品番号", "材質", "重量", "仕入先")
    
    ' 1. BOMの走査とデータ抽出
    ' ここでは、図面内の最初のBOMテーブルを対象とするロジックを実装
    ' 実際の実装では、複数のViewや複数のBOMを走査するロジックへ拡張可能
    
    On Error GoTo ErrorHandler
    
    ' 最初のビュー(図面シートそのもの)から開始
    Set swView = swDraw.GetFirstView
    
    Do While Not swView Is Nothing
        ' Viewが持つAnnotationを走査
        Dim swAnn As SldWorks.Annotation
        Set swAnn = swView.GetFirstAnnotation
        
        Do While Not swAnn Is Nothing
            ' TableAnnotation(BOM等)であるかを確認
            If swAnn.GetType = swAnnotationType_e.swTableAnnotation Then
                Set swTable = swAnn.GetTableAnnotation
                
                ' BOMテーブルが見つかった場合、抽出処理を実行
                If ProcessBOMTable(swTable, targetColumns, csvPath) Then
                    MsgBox "エクスポート完了: " & csvPath, vbInformation
                    GoTo CleanUp
                End If
            End If
            Set swAnn = swAnn.GetNextAnnotation
        Loop
        Set swView = swView.GetNextView
    Loop

    MsgBox "対象のBOMテーブルが見つかりませんでした。", vbExclamation

CleanUp:
    ' 【重要】オブジェクトの明示的な解放
    ' これを行わないことが、大規模システムにおけるメモリリークの主因となる
    Set swTable = Nothing
    Set swAnn = Nothing
    Set swView = Nothing
    Set swDraw = Nothing
    Set swModel = Nothing
    Set swApp = Nothing
    Exit Sub

ErrorHandler:
    MsgBox "致命的なエラーが発生しました: " & Err.Description, vbCritical
    Resume CleanUp
End Sub

''' <summary>
''' BOMテーブルを解析し、指定された列のデータをCSVに書き出す
''' </summary>
Private Function ProcessBOMTable(swTable As SldWorks.TableAnnotation, targetCols As Variant, filePath As String) As Boolean
    Dim colMap As Object
    Set colMap = CreateObject("Scripting.Dictionary")
    
    Dim i As Integer, j As Integer
    Dim colCount As Integer
    Dim rowCount As Long
    Dim headerText As String
    Dim fso As Object
    Dim ts As Object
    Dim lineData As String
    
    colCount = swTable.ColumnCount
    rowCount = swTable.RowCount
    
    ' --- STEP 1: カラム・マッピング (Robust Column Identification) ---
    ' ヘッダー行を走査し、ターゲットとなる列のインデックスを特定する
    For j = 0 To colCount - 1
        headerText = Trim(swTable.Text(0, j)) ' 0行目がヘッダーと仮定
        For i = LBound(targetCols) To UBound(targetCols)
            If headerText = targetCols(i) Then
                colMap.Add targetCols(i), j
            End If
        Next i
    Next j
    
    ' 必要な列が一つも見つからない場合は終了
    If colMap.Count = 0 Then
        ProcessBOMTable = False
        Exit Function
    End If
    
    ' --- STEP 2: データ抽出とCSV書き出し ---
    Set fso = CreateObject("Scripting.FileSystemObject")
    Set ts = fso.CreateTextFile(filePath, True, True) ' Unicode対応
    
    ' ヘッダー行の書き込み
    lineData = ""
    For i = LBound(targetCols) To UBound(targetCols)
        If colMap.Exists(targetCols(i)) Then
            lineData = lineData & targetCols(i) & ","
        End If
    Next i
    ts.WriteLine Left(lineData, Len(lineData) - 1) ' 末尾のカンマを除去
    
    ' データ行の書き込み (1行目はヘッダーなので 1 から開始)
    For i = 1 To rowCount - 1
        lineData = ""
        For j = LBound(targetCols) To UBound(targetCols)
            If colMap.Exists(targetCols(j)) Then
                ' セル値の取得。ダブルクォーテーションで囲むことでカンマ混入を防ぐ
                lineData = lineData & """" & swTable.Text(i, colMap(targetCols(j))) & ""","
            End If
        Next j
        ts.WriteLine Left(lineData, Len(lineData) - 1)
    Next i
    
    ts.Close
    
    ' 後処理
    Set ts = Nothing
    Set fso = Nothing
    Set colMap = Nothing
    
    ProcessBOMTable = True
End Function

4. アーキテクトの視点:運用における検討事項

このコードを実務に投入する際、以下の3点を考慮せよ。

A. 文字コードとシステム間連携

上記のコードでは `FileSystemObject` を使用し、Unicode (`True` 引数) でファイルを作成している。これは、日本語の「材質」や特殊記号を含む「仕入先名」が文字化けすることを防ぐためだ。しかし、連携先のレガシーなシステムが Shift-JIS しか受け付けない場合は、ADODB.Stream を使用してエンコーディングを明示的に制御する実装に切り替える必要がある。

B. エラー・レジリエンス(回復力)

実環境では、ファイルが他のプロセス(Excel等)によって開かれていて書き込みに失敗するケースが多発する。`On Error GoTo` によるエラーハンドリングに加え、書き込み前に `fso.FileExists` および、ファイルロックの有無を確認するロジックを組み込むことが、システムの信頼性を担保する。

C. パフォーマンスの最適化

数千枚の図面を処理する場合、`swApp.ScreenUpdating = False` は当然として、SolidWorksの「再構築(Rebuild)」を最小限に抑える設計が求められる。本コードのように、既存のテーブル情報を読み取る(Read-only)だけであれば、再構築のコストは発生しないが、もしプロパティを書き換えるような処理を加える場合は、計算負荷に細心の注意を払え。

結論

自動化エンジニアの価値は、単に「作業を減らす」ことではなく、「ミスが起こり得ない仕組みを作る」ことにある。インデックスに依存しない動的なロジック、厳格なメモリ管理、そして文字コードまで考慮したI/O設計。これらを備えて初めて、あなたの書いたコードは「ツール」から「システム」へと昇華する。

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