「動く」コードで満足するな。図面シート制御における「壊れない」設計の極意
多くの開発者が、SolidWorks VBAの自動化に取り組む際、最初に突き当たる壁がある。それは、「マクロを走らせるたびに、意図しないシートが消えたり、名前が重複してエラーで止まったりする」という、極めて初歩的かつ致命的な問題だ。
もし君が、単に「シートを追加する」「名前を変える」といったリファレンスの引き写しでコードを書いているなら、そのツールは実務の現場では「信頼できないゴミ」と見なされる。業務自動化の本質は、単なる動作の自動化ではない。「例外的な状況(イレギュラー)をいかに設計に組み込み、システムの整合性を維持するか」にある。
本稿では、図面(Drawing)ファイルのシート管理において、プロフェッショナルが実践している「堅牢な設計思想」と、それを具現化したプロダクションレベルのコードを伝授する。
1. 削除の罠:インデックスの動的変化を制御せよ
初心者が最も陥りやすいミス、それは「正方向へのループによるシート削除」だ。
例えば、3枚のシートがある状態で、1枚目から順に削除しようとして `For i = 1 To SheetCount` と書いたとしよう。1枚目を削除した瞬間、元の2枚目が「新しい1枚目」へと昇格する。しかし、ループのカウンタ `i` は次に `2` へ進むため、本来削除すべきだった2枚目をスキップしてしまうのだ。
アーキテクトの教え
破壊的な操作(削除)を行う際は、必ず「末尾から逆順に走査する(Reverse Loop)」。これが、インデックスの不整合を防ぐ鉄則である。
2. 名前の整合性:型番や工程名を「識別子」として扱う
図面シートの名前(Sheet Name)は、単なるラベルではない。それは、Excelやデータベースから流し込まれる「工程名」や「部品番号」といった、業務データと図面を紐付けるためのキー(Key)である。
ここで注意すべきは、SolidWorksの仕様による制約だ。
- 重複の禁止: 同一ドキュメント内に同じ名前のシートは存在できない。
- 禁止文字: シート名に使用できない特殊文字への配慮。
- 存在確認の欠如: 「名前を変える」命令を出す前に、「その名前のシートが既に存在するか」を検証しなければ、ランタイムエラーでプログラムは死ぬ。
3. 実装例:堅牢なシートマネージャー・モジュール
以下に、実務でそのまま使えるレベルの設計思想を盛り込んだコードを示す。単なる命令の羅列ではなく、「状態を確認し、必要に応じて操作を行う」という、冪等性(べきとうせい:何度実行しても同じ結果になる性質)を意識した構成だ。
Option Explicit
' ==============================================================================
' Module: DrawingSheetManager
' Description: 図面シートの追加・削除・リネームを安全に制御する
' Author: Legendary Chief Architect
' ==============================================================================
Sub Main()
' 本来は外部(Excel等)から渡されるパラメータを想定
Dim targetSheetNames As Variant
targetSheetNames = Array("ASSEMBLY_VIEW", "DETAIL_VIEW", "BOM_VIEW")
Call ExecuteRobustSheetManagement(targetSheetNames)
End Sub
''' <summary>
''' 図面シートの整合性を保つためのメインルーチン
''' </summary>
''' <param name="desiredNames">存在すべきシート名のリスト</param>
Sub ExecuteRobustSheetManagement(desiredNames As Variant)
Dim swApp As SldWorks.SldWorks
Dim swModel As SldWorks.ModelDoc2
Dim swDraw As SldWorks.DrawingDoc
Dim swSheet As SldWorks.Sheet
Dim i As Integer
Dim sheetCount As Integer
Dim currentSheetName As String
Dim isExist As Boolean
On Error GoTo ErrorHandler
Set swApp = Application.SldWorks
Set swModel = swApp.ActiveDoc
' 1. ドキュメントチェック
If swModel Is Nothing Then Exit Sub
If swModel.GetType <> swDocDRAWING Then
MsgBox "図面ファイルを開いた状態で実行してください。", vbCritical
Exit Sub
End If
Set swDraw = swModel
' --------------------------------------------------------------------------
' STEP 1: 不要なシートの削除 (Reverse Loop Strategy)
' --------------------------------------------------------------------------
' 目的:desiredNamesに含まれないシートをすべて削除する
' --------------------------------------------------------------------------
sheetCount = swDraw.GetSheetCount
For i = sheetCount To 1 Step -1
swDraw.ActivateSheet GetSheetNameByIndex(swDraw, i)
Set swSheet = swDraw.GetCurrentSheet
currentSheetName = swSheet.GetName
isExist = False
' 必要なリストの中に、現在のシート名が含まれているかチェック
For Each varName In desiredNames
If currentSheetName = CStr(varName) Then
isExist = True
Exit For
End If
Next varName
' リストにないシートは削除(ただし、全シートが消えるのを防ぐため最低1枚は残す設計を推奨)
If Not isExist And sheetCount > 1 Then
swDraw.DeleteSheet currentSheetName
End If
Next i
' --------------------------------------------------------------------------
' STEP 2: 不足しているシートの追加とリネーム
' --------------------------------------------------------------------------
' 目的:必要なシートが存在しなければ追加し、名前を確定させる
' --------------------------------------------------------------------------
For i = LBound(desiredNames) To UBound(desiredNames)
Dim targetName As String
targetName = CStr(desiredNames(i))
' シートが存在するか確認
isExist = CheckSheetExists(swDraw, targetName)
If Not isExist Then
' シートが存在しない場合、新規追加(デフォルトのテンプレートを使用)
' ※実務では AddSheet2 等でテンプレート指定を検討せよ
swDraw.NewSheet targetName, swDwgSheetModelView, 0, 0, 0, 0, 0
Debug.Print "Added: " & targetName
Else
Debug.Print "Already Exists: " & targetName
End If
Next i
swModel.ForceRebuild3 True
MsgBox "シート管理プロセスが正常に完了しました。", vbInformation
Exit Sub
ErrorHandler:
MsgBox "致命的なエラーが発生しました。" & vbCrLf & _
"Error No: " & Err.Number & vbCrLf & _
"Description: " & Err.Description, vbCritical
End Sub
' ==============================================================================
' Helper Functions (内部ユーティリティ)
' ==============================================================================
''' <summary>
''' 指定した名前のシートが存在するか判定する
''' </summary>
Private Function CheckSheetExists(swDraw As SldWorks.DrawingDoc, sheetName As String) As Boolean
Dim i As Integer
Dim count As Integer
count = swDraw.GetSheetCount
For i = 1 To count
If swDraw.GetSheetName(i) = sheetName Then
CheckSheetExists = True
Exit Function
End If
Next i
CheckSheetExists = False
End Function
''' <summary>
''' インデックスからシート名を取得する(安全なラップ)
''' </summary>
Private Function GetSheetNameByIndex(swDraw As SldWorks.DrawingDoc, index As Integer) As String
On Error Resume Next
GetSheetNameByIndex = swDraw.GetSheetName(index)
On Error GoTo 0
End Function
4. 実務への応用:データベース・Excel連携時の注意点
このコードをさらに進化させ、Excelの管理表からシート構成を自動生成する場合、以下の「設計上の防壁」を設けるべきだ。
1. データのサニタイズ (Sanitization)
Excelのセルに「/」や「*」といった、ファイルシステムやSolidWorksの命名規則に抵触する文字が入っている可能性がある。シート名に変換する前に、これらをアンダースコア `_` 等に置換するロジックを必ず挟め。
2. トランザクションの概念
「シートの削除は成功したが、追加中にエラーが起きた」という中途半端な状態は、ユーザーにとって最悪の体験だ。大規模な処理を行う場合は、作業前に「テンプレートとなるファイルをコピーしてバックアップを取っておく」といった、ロールバック(巻き戻し)を考慮した設計がプロの仕事である。
3. パフォーマンスの重み
`swModel.ForceRebuild3`(再構築)は非常に重い処理だ。ループの中で毎回呼び出すのではなく、すべてのシート操作が完了した最後に一度だけ呼び出すこと。これが、数千枚の図面を処理する際の速度差となって現れる。
結論
「動くコード」を書くのはプログラミングの入り口に過ぎない。
「想定外のデータが来ても、壊れず、整合性を維持し、安全に終了するコード」を書くこと。それこそが、エンジニアリングの本質であり、現場から信頼される唯一の道である。
このコードをベースに、君自身の業務ドメインに合わせた「最強の自動化エンジン」を構築してほしい。
