【テクニカル・上級編】【中級者向け】図面内に追加された「ユーザー定義記号(Sketch Block / 独自シンボル)」の配置と一括削除 – SolidWorks VBA解析バイブル

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

【極限の知見】SolidWorks 図面における Sketch Block の制御:一括配置と安全な一括削除のアーキテクチャ

多くのエンジニアは、SolidWorks APIを「操作を自動化するためのツール」と考えている。しかし、エンタープライズレベルの設計環境において、APIは「設計資産を統制するためのインフラ」でなければならない。

特に図面(Drawing)における「ユーザー定義記号(Sketch Block)」の扱いは、その典型だ。社内標準の注意記号、品質管理マーク、あるいは特殊な検図印。これらをプログラムで正確な座標に配置し、かつ、旧規格となった古いシンボルを、図面データの整合性を破壊せずに一括削除する。この一見単純なタスクの裏には、COMオブジェクトのライフサイクル管理、コレクションの反復におけるインデックスの動的変化、そしてメモリリークの回避という、シニアエンジニアが直面する深淵な問題が潜んでいる。

本稿では、単なる「動くコード」ではなく、大規模な図面データに対しても堅牢に動作する、プロフェッショナルな実装手法を詳解する。

1. Sketch Block の正体:階層構造の理解

まず、誤解を解いておかなければならない。SolidWorksの図面における「Sketch Block」は、単なる図形ではない。それは `SketchBlockInstance` というオブジェクトとして、特定の `Sheet` または `View` のコンテキストに紐付けられた、厳格な階層構造を持つエンティティである。

初心者が陥る最大の罠は、「図面全体からブロックを探そうとして、全エンティティを全スキャンする」という、計算量 $O(n)$ の非効率なアプローチだ。大規模なアセンブリ図面において、これはアプリケーションのフリーズを招く。

効率的な制御のためには、以下の階層を意識せよ。
1. DrawingDoc (Root)
2. Sheet (コンテキスト)
3. View (注釈のコンテキスト)
4. SketchBlockInstance (実体)

2. 破壊的削除における「インデックスの罠」

「特定の名前を持つブロックをすべて削除する」という要件に対し、多くの者が書くコードは以下の通りだ。

' --- 典型的な失敗例 ---
For i = 1 To blockCount
    If blocks(i).Name = "Old_Symbol" Then
        blocks(i).Delete
    End If
Next i

これは致命的なバグを内包している。`Delete` メソッドを呼び出した瞬間、コレクション内の要素が詰められ、インデックスが繰り上がる。その結果、削除対象の次の要素がスキップされるという現象が発生する。

これを回避するプロフェッショナルな戦略は二つある。
1. 逆順ループ (Reverse Loop): インデックスを末尾からデクリメントしていく。
2. コレクションへの集約 (Collection Gathering): 削除対象のオブジェクト参照のみを一旦別の `Collection` オブジェクトに格納し、後から一括処理する。

後者の方が、メモリ管理の観点から予測可能性が高い。

3. 実装:極限の堅牢性を備えた制御モジュール

以下に、配置と削除の双方をカバーする、実戦的なクラスモジュール級のロジックを示す。

Option Explicit

' ==============================================================================
' Module: SymbolManager
' Description: 図面内におけるSketch Blockの精密な配置および一括管理
' ==============================================================================

''' <summary>
''' 指定したシート上の指定座標に、標準シンボルを配置する
''' </summary>
''' <param name="swDraw">SolidWorks Drawing Document</param>
''' <param name="blockName">配置するブロックの定義名</param>
''' <param name="posX">X座標 (m)</param>
''' <param name="posY">Y座標 (m)</param>
Public Sub InsertStandardSymbol(ByRef swDraw As SldWorks.DrawingDoc, _
                                ByVal blockName As String, _
                                ByVal posX As Double, _
                                ByVal posY As Double)
    
    Dim swSheet As SldWorks.Sheet
    Dim swModel As SldWorks.ModelDoc2
    Dim swSketchBlock As SldWorks.SketchBlock
    Dim swBlockInst As SldWorks.SketchBlockInstance
    Dim swAnnotation As SldWorks.Annotation
    
    Set swModel = swDraw
    Set swSheet = swDraw.GetCurrentSheet
    
    ' 1. ブロック定義の取得 (既存の定義から検索)
    ' ※実際の実装では、ライブラリファイルから読み込む処理が必要
    Set swSketchBlock = swModel.GetSketchBlock(blockName)
    
    If swSketchBlock Is Nothing Then
        Err.Raise vbObjectError + 513, "SymbolManager", "指定されたブロック定義が見つかりません: " & blockName
        Exit Sub
    End If
    
    ' 2. 配置処理
    ' InsertSketchBlockは、現在のシートのコンテキストにインスタンスを作成する
    Set swBlockInst = swModel.InsertSketchBlock(swSketchBlock, posX, posY, 0)
    
    If swBlockInst Is Nothing Then
        Debug.Print "配置失敗: " & blockName
        Exit Sub
    End If
    
    ' 3. オブジェクトの明示的解放 (メモリ最適化)
    ' COMオブジェクトの参照カウントを適切に管理する
    Set swBlockInst = Nothing
    Set swSketchBlock = Nothing
    Set swSheet = Nothing
    Set swModel = Nothing
End Sub

''' <summary>
''' 特定の名称を持つ古いブロックを、図面全体から安全に一括削除する
''' </summary>
''' <param name="swDraw">SolidWorks Drawing Document</param>
''' <param name="targetName">削除対象のブロック名(部分一致)</param>
Public Sub PurgeOldSymbols(ByRef swDraw As SldWorks.DrawingDoc, ByVal targetName As String)
    
    Dim swModel As SldWorks.ModelDoc2
    Dim swView As SldWorks.View
    Dim swBlockInst As SldWorks.SketchBlockInstance
    Dim delCollection As New Collection
    Dim i As Long
    
    Set swModel = swDraw
    
    ' --- Step 1: 削除対象の収集 (Scanning Phase) ---
    ' 全ビューを走査し、条件に合致するインスタンスをCollectionに集める
    ' これにより、削除中のインデックス変動問題を完全に回避する
    Set swView = swModel.GetFirstView ' Sheet自体が最初のビュー
    
    Do While Not swView Is Nothing
        ' ビュー内のすべてのエンティティを走査
        ' Note: 実際にはView.GetFirstSketchBlock を使用するのが高速
        Set swBlockInst = swView.GetFirstSketchBlock
        
        Do While Not swBlockInst Is Nothing
            ' ブロック名の判定 (大文字小文字を区別しない)
            If InStr(1, swBlockInst.Name, targetName, vbTextCompare) > 0 Then
                delCollection.Add swBlockInst
            End If
            Set swBlockInst = swBlockInst.GetNext
        Loop
        
        Set swView = swView.GetNextView
    Loop
    
    ' --- Step 2: 実行フェーズ (Execution Phase) ---
    If delCollection.Count = 0 Then
        Debug.Print "削除対象のシンボルは見つかりませんでした。"
        Exit Sub
    End If
    
    ' Undoを一つのトランザクションとして扱うため、画面更新を一時停止
    swModel.ActiveView.EnableGraphicsUpdate = False
    
    On Error Resume Next ' 削除中の予期せぬエラーでプロセスを止めないための防御
    For i = 1 To delCollection.Count
        Set swBlockInst = delCollection(i)
        If Not swBlockInst Is Nothing Then
            swBlockInst.Select2 False, 0
            swModel.EditDelete
        End If
        ' インスタンスへの参照を切り離す
        Set swBlockInst = Nothing
    Next i
    On Error GoTo 0
    
    ' 3. 描画の再開とクリーンアップ
    swModel.ActiveView.EnableGraphicsUpdate = True
    swModel.ForceRebuild3 True
    
    Debug.Print delCollection.Count & " 個のシンボルを削除しました。"
    
    ' 参照の完全な破棄
    Set delCollection = Nothing
    Set swModel = Nothing
End Sub

4. アーキテクトの視点:パフォーマンスとメモリ管理の極意

このコードを実運用に投入する際、シニアエンジニアが意識すべき「極限の知見」を3点付け加える。

I. COM参照の明示的解放(Explicit Release)

VBAはガベージコレクション(GC)を持っているように見えるが、それはあくまでVBA内部の挙動だ。SolidWorksのような巨大なCOMサーバーを操作する場合、`Set obj = Nothing` を怠ると、SolidWorksのプロセスがメモリ上に残り続け、あるいはメモリリークを引き起こして、数時間後の「謎のクラッシュ」を招く。特にループ内でオブジェクトを生成する場合は、必ずスコープの終わりに明示的な解放を行え。

II. Windows API による UI フリーズの抑制

大規模な削除処理を行う際、SolidWorksのGUIは応答なし(Not Responding)状態になる。これを防ぐには、`swModel.ActiveView.EnableGraphicsUpdate = False` を活用するのは当然として、もしより高度なシステム連携(C#等からの呼び出し)を行うのであれば、`SendMessage` API を用いて、処理中に「処理中…」というメッセージをOSのメッセージキューに流し込み、ユーザーのストレスを軽減する設計が必要だ。

III. トランザクションの整合性

`EditDelete` を繰り返すと、Undoスタックが膨大な量になり、メモリを圧迫する。大量の削除を行う場合は、処理の前後で `swModel.Extension.SelectByID2` 等を用いて選択状態を適切に管理し、必要であれば、処理後に一度 `ForceRebuild3` を呼び出して、内部のグラフ構造(Feature Tree)を再構築させることが、データ破損を防ぐための鉄則である。

結論

図面自動化の真髄は、「動くものを作る」ことではない。「どのような異常系においても、設計データの整合性を保ち、システムを安定して稼働させ続けること」にある。

今回提示した「Collectionへの集約による削除手法」と「厳格な参照管理」は、そのための最小限かつ強力な武器となるだろう。これらを使いこなし、単なるスクリプターから、真のCADオートメーション・アーキテクトへと昇華してほしい。

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