【実務・中級編】【初心者向け】図面シートの追加・並び替え・不要シートの安全な削除を自動化するVBAマクロ – SolidWorks VBA解析バイブル

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

図面管理の自動化において、初心者が陥る「致命的な設計ミス」とその回避策

「図面シートを自動で追加し、名前を変え、不要なものを消す。そんな簡単な処理、マクロの記録を使えばすぐできる」

もし君がそう考えているのなら、今すぐその考えを捨ててほしい。

実務における自動化ツールは、単に「動く」だけではゴミ同然だ。ユーザーが予期せぬ操作をしたとき、あるいは大量のシートを処理している最中に、インデックスの不整合でエラーを吐いて止まるようなプログラムは、現場の信頼を失う。それどころか、最悪の場合は図面データそのものを破損させるリスクすら孕んでいる。

今日は、SolidWorks VBAにおける「図面シート操作」という、一見単純に見えて実は極めて奥が深い領域について、プロフェッショナルが守るべき設計思想を伝授する。

1. なぜ「ActiveSheet」に依存してはいけないのか

初心者が最初に書くコードは、おそらくこうだ。
`Set swSheet = swDrawing.GetActiveSheet`

これは、設計としては「極めて脆弱」だ。
プログラムの実行中にユーザーがマウスをクリックして別のシートを選択した瞬間に、`ActiveSheet` の参照先は変わる。君のロジックは、意図しないシートに対して破壊的な操作(削除や名前変更)を行うことになる。

鉄則: オブジェクトを操作する際は、常に「インデックス(番号)」または「明確な名前」を起点とし、取得したオブジェクトを確実に変数に保持(キャプチャ)してから処理に移ること。

2. シート削除における「逆順ループ」の法則

図面シートの削除において、最も多くのエンジニアが犯すミスがこれだ。

「1番目から順番に、条件に合うシートを消していく」というロジック。これは絶対にやってはいけない。

なぜ失敗するのか?

SolidWorksのシートコレクションは、動的なリストだ。
例えば、3枚のシート(Index 1, 2, 3)がある状態で、Index 1を削除したとする。その瞬間、元のIndex 2はIndex 1に繰り上がる。

もし君が `For i = 1 To sheetCount` という正方向のループで回していたらどうなるか?
Index 1を消した直後、ループは `i = 2` に進む。しかし、繰り上がってきた「元のIndex 2」は、新しい「Index 1」になっているため、ループのチェックをすり抜けてしまうのだ。 これが「一部のシートが削除されない」というバグの正体だ。

解決策: 削除を行う際は、必ず 「末尾(インデックスの大きい方)から逆順に」 ループを回せ。これがコレクション操作における黄金律だ。

3. プロダクション品質のコード実装例

それでは、これらを踏まえた「堅牢なシート操作モジュール」のコード例を示す。
単なるコピペではなく、なぜこのように書くのかという「意図」を読み解いてほしい。

Option Explicit

''' <summary>
''' 図面シート操作のプロフェッショナル・モジュール
''' </summary>
Public Sub ManageDrawingSheets()
    Dim swApp As SldWorks.SldWorks
    Dim swModel As SldWorks.ModelDoc2
    Dim swDraw As SldWorks.DrawingDoc
    Dim vSheetNames As Variant
    Dim i As Long
    
    Set swApp = Application.SldWorks
    Set swModel = swApp.ActiveDoc
    
    ' 1. 型チェック:ドキュメントが図面であることを保証する
    If swModel Is Nothing Then
        MsgBox "ドキュメントが開かれていません。", vbCritical
        Exit Sub
    End If
    
    If swModel.GetType <> swDocDRAWING Then
        MsgBox "このマクロは図面ファイルでのみ実行可能です。", vbCritical
        Exit Sub
    End If
    
    Set swDraw = swModel
    
    ' --- 業務ロジック開始 ---
    On Error GoTo ErrorHandler
    
    ' 2. 新規シートの追加 (安全な追加)
    ' 第2引数は用紙サイズ、第3引数はスケール等。ここでは標準的な設定を想定
    Call SafeAddSheet(swDraw, "Sheet_New_01")
    
    ' 3. シートの削除 (逆順ループによる安全な削除)
    ' 例: "Old_" で始まるシートをすべて削除する
    Call SafeDeleteSheetsByPrefix(swDraw, "Old_")
    
    ' 4. シート名の変更 (重複チェック付き)
    Call SafeRenameSheet(swDraw, "Sheet_New_01", "Production_Sheet_A")

    MsgBox "シート操作が正常に完了しました。", vbInformation
    Exit Sub

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

''' <summary>
''' 重複を考慮しつつシートを追加する
''' </summary>
Private Sub SafeAddSheet(swDraw As SldWorks.DrawingDoc, newName As String)
    Dim swSheet As SldWorks.Sheet
    ' すでに同名が存在するかチェックするロジックをここに入れるべき
    ' 今回は簡略化のため直接追加
    Set swSheet = swDraw.NewSheet(newName, swDwgPaperAsize, swDwgTemplatesASize)
    
    If swSheet Is Nothing Then
        Err.Raise 1001, , "シートの作成に失敗しました。名前が重複している可能性があります。"
    End If
End Sub

''' <summary>
''' 接頭辞に基づき、シートを「末尾から」安全に削除する
''' </summary>
Private Sub SafeDeleteSheetsByPrefix(swDraw As SldWorks.DrawingDoc, prefix As String)
    Dim vSheetNames As Variant
    Dim i As Long
    Dim swSheet As SldWorks.Sheet
    
    ' 全シート名の取得
    vSheetNames = swDraw.GetSheetNames
    
    ' 重要:インデックスの大きい方からループを回す (逆順ループ)
    ' LBoundからUBoundまでではなく、UBoundからLBoundへ
    For i = UBound(vSheetNames) To LBound(vSheetNames) Step -1
        Set swSheet = swDraw.GetSheet(vSheetNames(i))
        
        ' 接頭辞が一致するか判定
        If Left(swSheet.GetName, Len(prefix)) = prefix Then
            ' 削除実行
            swDraw.DeleteSheet swSheet.GetName
        End If
    Next i
End Sub

''' <summary>
''' 重複を回避しながらシート名を変更する
''' </summary>
Private Sub SafeRenameSheet(swDraw As SldWorks.DrawingDoc, oldName As String, newName As String)
    Dim swSheet As SldWorks.Sheet
    Dim vSheetNames As Variant
    Dim i As Long
    Dim isDuplicate As Boolean
    
    Set swSheet = swDraw.GetSheet(oldName)
    If swSheet Is Nothing Then Exit Sub
    
    ' 新しい名前が既に使われていないか検証
    vSheetNames = swDraw.GetSheetNames
    isDuplicate = False
    For i = LBound(vSheetNames) To UBound(vSheetNames)
        If vSheetNames(i) = newName Then
            isDuplicate = True
            Exit For
        End If
    Next i
    
    If isDuplicate Then
        Err.Raise 1002, , "変更後の名前 '" & newName & "' は既に存在します。"
    Else
        swSheet.SetName newName
    End If
End Sub

4. 実務への応用:データベース連携時の注意点

このマクロを、ExcelやSQLデータベースから取得したリストに基づいて実行する場合、さらに一段上の設計が求められる。

1. トランザクションの概念を持て
10枚のシートを追加するプロセスで、5枚目でエラーが起きた場合、どうするか?「中途半端に5枚追加された状態」は、自動化ツールとしては失敗だ。可能であれば、処理開始前にファイルをバックアップするか、エラー発生時にロールバック(元の状態に戻す)できる仕組みを検討せよ。
2. 名前の正規化(Normalization)
データベースから取得した文字列には、空白や改行、使用不可能な記号が含まれていることが多い。`SafeRenameSheet` に渡す前に、必ず文字列のクリーニング(Trim関数や記号置換)を行う工程を挟め。
3. パフォーマンスの最適化
大量のシート操作を行う場合、SolidWorksの描画更新がボトルネックになる。APIの呼び出しごとに画面が更新されるのを防ぐため、可能であれば `swApp.Visible = False` にするか、処理の合間に `swModel.ForceRebuild3` を適切に配置し、メモリ上の整合性を保て。

結論

「動くコード」を書くのはプログラミングだ。「壊れないツール」を作るのがエンジニアリングだ。

シートのインデックス、コレクションの動的な変化、重複の検証。これらの当たり前すぎる概念を、どれだけ厳格にコードに落とし込めるか。その差が、君の作るツールの価値を決める。

次からは、単に `DeleteSheet` を叩く前に、一度立ち止まって考えてほしい。
「このループ、逆順になっているか?」 と。

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