【テクニカル・上級編】【初心者向け】図面シート上の「承認欄」「改訂日」などのテキストノートを一括書き換えるVBA – SolidWorks VBA解析バイブル

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

図面データ管理の規律:Drawing Noteの一括制御における設計思想と実装

「初心者向け」というラベルが貼られたタスクほど、プロフェッショナルを油断させ、そして陥落させるものはない。

図面シート上の「承認欄」や「改訂日」を書き換える。一見すれば、数行のループと数個のAPI呼び出しで済む単純作業に見えるだろう。しかし、数百、数千の図面ファイルを対象とした「システム」として構築する場合、話は全く別だ。

メモリリークによるCADのクラッシュ、不適切なオブジェクト参照によるリソースの枯渇、そしてエラーハンドリングの欠如による処理の中断。これらは、単なる「コードの書き方」の問題ではなく、オブジェクトのライフサイクルを制御できていない設計の敗北である。

本稿では、単なるスクリプトの提供ではなく、堅牢な自動化エンジンを構築するためのアーキテクチャを解説する。

1. 凡庸な実装が陥る「3つの罠」

自動化を実装する際、多くのエンジニアが以下の罠に足を取られる。

罠その1:`SelectByID2` への過度な依存

多くのリファレンスでは、特定のノートを選択するために `SelectByID2` を使用するよう指示している。しかし、これは「ユーザーの操作を模倣している」に過ぎない。UIの状態に依存するこの手法は、実行速度を著しく低下させ、バックグラウンド処理において予期せぬ挙動を引き起こす。プロフェッショナルは、モデルのツリー(Annotationコレクション)を直接走査すべきだ。

罠その2:COMオブジェクトの解放漏れ

SolidWorksはCOM(Component Object Model)ベースのアプリケーションである。VBAの変数スコープが外れただけでメモリが解放されると信じているなら、それは幻想だ。明示的な `Set obj = Nothing` は、単なるマナーではなく、長時間稼働するシステムにおける「生存戦略」である。

罠その3:エラーハンドリングの不在

ファイルが読み取り専用である、ネットワークパスが切断された、図面内に該当するノートが存在しない……。これらの例外事象を無視するコードは、自動化ツールではなく「時限爆弾」である。

2. アーキテクチャ設計:堅牢な更新エンジンの構成

我々が構築すべきは、以下の3層構造を持つエンジンである。

1. Iteration Layer (反復層): 物理的なファイルシステムを走査し、対象となる図面ファイルを特定する。
2. Logic Layer (論理層): 図面を開き、注釈(Annotation)の中から特定のパターンを持つノートを検索・特定する。
3. Lifecycle Layer (制御層): メモリを管理し、エラー発生時にリソースを安全に破棄して次のファイルへ移行する。

3. 極限の実装例:Drawing Note Batch Updater

以下に、これらの思想を具現化したコードを示す。ここでは、`FileSystemObject` を用いた効率的なファイル走査と、注釈コレクションを直接操作する低レイヤーなアプローチを採用している。

'**
' Module: DrawingAutomationEngine
' Description: 高信頼性図面ノート一括更新エンジン
' Author: Chief Architect
'**

Option Explicit

' Windows APIの呼び出しが必要な場合はここに宣言(例: ユーザー入力を制御する場合など)
' Declare PtrSafe Function ...

Sub Main()
    ' ターゲットディレクトリの設定(実際にはダイアログ等から取得すべき)
    Dim targetFolder As String
    targetFolder = "C:\Project\Drawings\" 
    
    ' 更新するデータ(本来はDBやCSVから取得する)
    Dim updateMap As Object
    Set updateMap = CreateObject("Scripting.Dictionary")
    updateMap.Add "REV_DATE", "2023-10-27"
    updateMap.Add "APPROVED_BY", "Lead Engineer"

    ' エンジンの起動
    Call ExecuteBatchUpdate(targetFolder, updateMap)
End Sub

''' <summary>
''' 図面群に対して一括処理を実行するメインエンジン
''' </summary>
Sub ExecuteBatchUpdate(folderPath As String, dataMap As Object)
    Dim swApp As SldWorks.SldWorks
    Dim fso As Object
    Dim folder As Object
    Dim file As Object
    Dim swModel As SldWorks.ModelDoc2
    Dim swDraw As SldWorks.DrawingDoc
    Dim errorCount As Long
    Dim successCount As Long

    On Error GoTo ErrorHandler

    Set swApp = Application.SldWorks
    Set fso = CreateObject("Scripting.FileSystemObject")
    
    If Not fso.FolderExists(folderPath) Then
        MsgBox "Target folder not found.", vbCritical
        Exit Sub
    End If

    Set folder = fso.GetFolder(folderPath)

    ' 高速化のための設定: 画面更新を抑制(バックグラウンド処理の鉄則)
    ' swApp.Visible = False ' 必要に応じて検討

    For Each file In folder.Files
        ' 図面ファイル(.SLDDRW)のみを対象とする
        If LCase(fso.GetExtensionName(file.Path)) = "slddrw" Then
            
            ' --- Lifecycle Management: File Open ---
            Set swModel = swApp.OpenDoc6(file.Path, swDocDRAWING, swOpenDocOptions_Silent, "", 0, 0)
            
            If Not swModel Is Nothing Then
                Set swDraw = swModel
                
                ' ノート更新ロジックの実行
                If UpdateDrawingNotes(swDraw, dataMap) Then
                    ' 変更を保存
                    swModel.Save3 swSaveAsOptions_Silent, 0, 0
                    successCount = successCount + 1
                Else
                    errorCount = errorCount + 1
                End If
                
                ' --- Lifecycle Management: Explicit Release ---
                swApp.CloseDoc swModel.GetTitle
                Set swDraw = Nothing
                Set swModel = Nothing
            Else
                errorCount = errorCount + 1
            End If
        End If
        
        ' メモリ負荷軽減のためのGC的な振る舞い
        DoEvents 
    Next file

    MsgBox "Process Completed." & vbCrLf & _
           "Success: " & successCount & vbCrLf & _
           "Failed: " & errorCount, vbInformation

    Exit Sub

ErrorHandler:
    MsgBox "Critical System Error: " & Err.Description, vbCritical
    ' エラー時もリソースを解放してプロセスを維持する
    Set swModel = Nothing
    Set swApp = Nothing
End Sub

''' <summary>
''' 注釈コレクションを走査し、特定のキーを持つノートを更新する
''' </summary>
Function UpdateDrawingNotes(swDraw As SldWorks.DrawingDoc, dataMap As Object) As Boolean
    Dim swSheet As SldWorks.Sheet
    Dim swView As SldWorks.View
    Dim swAnn As SldWorks.Annotation
    Dim swNote As SldWorks.Note
    Dim swText As SldWorks.TextAnnotation
    Dim vViews As Variant
    Dim i As Integer
    Dim updated As Boolean
    Dim key As Variant
    
    updated = False
    
    ' 図面内のすべてのビュー(シート自体も含む)を取得
    vViews = swDraw.GetViews
    
    For i = 0 To UBound(vViews)
        Set swView = vViews(i)
        
        ' ビュー内の注釈(Annotation)を取得
        ' SelectByID2を使わず、Annotationコレクションを直接走査する
        Set swAnn = swView.GetFirstAnnotation
        
        Do While Not swAnn Is Nothing
            ' 注釈が「ノート」であるかを確認
            If swAnn.GetType = swAnnotationType_e.swNote Then
                Set swNote = swAnn.GetSpecificAnnotation
                Set swText = swNote.GetTextAnnotation
                
                ' ノートの内容(または属性)をチェック
                ' ここでは例として、ノートのテキスト自体にキーが含まれていると仮定
                ' 運用に合わせて「特定のプロパティ」や「特定の文字列」判定に最適化せよ
                For Each key In dataMap.Keys
                    If InStr(1, swText.GetText, CStr(key), vbTextCompare) > 0 Then
                        ' 値の書き換え実行
                        ' キー文字列を新しい値に置換、あるいは完全に書き換え
                        swText.SetText dataMap(key)
                        updated = True
                    End If
                Next key
            End If
            
            ' 次の注釈へ
            Set swAnn = swAnn.GetNext
        Loop
    Next i
    
    UpdateDrawingNotes = updated
End Function

4. アーキテクトの視点:運用における極限の知見

メモリ管理の真実

`Set swModel = Nothing` を書くだけでは不十分な場合がある。特に、大量のファイルをループで回す場合、SolidWorksの内部プロセスがメモリを抱え込み、最終的に `Out of Memory` を吐き出す。
これを防ぐには、`swApp.CloseDoc` を確実に呼び出し、さらに `DoEvents` を挟むことで、OSに制御を戻し、ガベージコレクションの機会を与えることが肝要である。

検索アルゴリズムの最適化

上記のコードでは `InStr` を用いてノート内のテキストを検索している。これは直感的だが、大規模環境では「ノートの属性(Custom Property)」に識別子を持たせる設計を推奨する。
テキストの内容に依存する検索は、設計者が「改訂日:」を「改訂日 」(スペース入り)に変えただけで壊れる。「データ(値)」と「メタデータ(識別子)」を分離することこそが、堅牢なシステム設計の要諦である。

レガシー環境への配慮

企業内システムでは、古いバージョンのSolidWorksや、Windowsのセキュリティポリシーが厳しい環境が混在する。`FileSystemObject` のようなレガシーだが信頼性の高いコンポーネントを使用し、Windows APIによる低レイヤーの操作を組み合わせることで、環境の変化に強い「動じないコード」を書くことができる。

結論

自動化とは、単に「手作業をなくすこと」ではない。
「人間が行うプロセスを、決定論的(Deterministic)で、再現可能(Reproducible)な計算プロセスへと昇華させること」である。

このマクロを単なるツールとして終わらせるか、それとも強固なデータ管理基盤の第一歩とするか。それは、君がコードの背後にあるオブジェクトの寿命をどれだけ深く理解しているかにかかっている。

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