【実務直結】SolidWorks図面保存時のDXF/DWG同時出力:堅牢な自動命名ルールと設計思想
エンジニアの諸君、現場で「ツールを作れ」と言われて、とりあえず `SaveAs` を並べただけのコードを書いていないか?
「図面を保存したら、決まったルールでDXFとDWGも一緒に出てくるようにしてほしい」
現場から飛んでくるこの要求は、一見すると単純な「作業の自動化」に見える。しかし、プロフェッショナルな自動化エンジニアとして、我々はこの要求の裏に潜む「不完全な設計が招く致命的なリスク」を読み解かなければならない。
ファイル名がルールから外れる、プロパティが欠落していてマクロが止まる、あるいは書き出し中にファイルがロックされていてエラーを吐く――。これらはすべて、設計段階で「例外」を考慮していない初学者が陥る罠だ。
本記事では、単なるコピペコードの提供ではない。「業務で耐えうる、保守性と堅牢性を兼ね備えた設計」を、SolidWorks VBAの極限的な視点から伝授する。
1. なぜ「単純なSaveAs」ではいけないのか
多くの開発者が、以下のようなアプローチを取る。
1. 現在のファイル名を取得する。
2. 拡張子を `.dxf` に書き換える。
3. `SaveAs` を実行する。
これは「動く」が、「運用には耐えられない」。理由は3つある。
A. プロパティの不整合(Data Integrity)
設計変更によって図面内のカスタムプロパティ(図番、リビジョンなど)が未入力のまま保存されることは日常茶飯事だ。命名ルールにこれらを使用する場合、プロパティが空の時に「ファイル名が空」になる、あるいは「不正な文字」が混入する事態を想定しなければならない。
B. ファイルIOの競合(Race Condition)
DXF/DWGは、他のCADソフトやビューアで開かれていることが多い。書き出し時にファイルがロックされていれば、マクロは即座にクラッシュするか、不完全なファイルを作成する。
C. 責務の混在(Lack of Separation of Concerns)
「名前を決めるロジック」と「ファイルを保存するロジック」が密結合しているコードは、後からのルール変更(例:リビジョンを末尾ではなく先頭にしたい)に対応する際、コード全体を破壊するリスクを孕む。
2. アーキテクチャ設計:堅牢な自動化への3原則
我々が実装すべきは、以下の3つのレイヤーに分離された設計である。
1. Property Layer (抽出層): SolidWorksのカスタムプロパティから、安全に値を抜き出す。
2. Naming Engine (命名層): 抽出した値を、社内ルールに基づいた文字列へと変換する。
3. IO Controller (実行層): ファイルの存在確認、保存実行、エラーハンドリングを一手に引き受ける。
3. プロダクション・コード例
以下に、そのまま実務の標準モジュールとして組み込めるレベルのコードを示す。
Option Explicit
' ==============================================================================
' 構成定数: 命名ルールや拡張子の管理を中央集権化する
' ==============================================================================
Private Const EXT_DXF As String = "dxf"
Private Const EXT_DWG As String = "dwg"
Private Const DELIMITER As String = "_" ' ファイル名の区切り文字
' ==============================================================================
' メインルーチン: 図面保存時に呼び出す、あるいはボタンに割り当てる
' ==============================================================================
Public Sub ExportDrawingToDXFAndDWG()
Dim swApp As SldWorks.SldWorks
Dim swModel As SldWorks.ModelDoc2
Dim swDraw As SldWorks.DrawingDoc
Dim swModExt As SldWorks.ModelDocExtension
Set swApp = Application.SldWorks
Set swModel = swApp.ActiveDoc
' --- 1. 事前検証 (Guard Clauses) ---
If swModel Is Nothing Then
MsgBox "ドキュメントが開かれていません。", vbCritical
Exit Sub
End If
If swModel.GetType <> swDocDRAWING Then
MsgBox "このマクロは図面ファイル(.slddrw)専用です。", vbExclamation
Exit Sub
End If
Set swDraw = swModel
Set swModExt = swModel.Extension
' --- 2. 命名ルールの生成 ---
Dim targetFileName As String
targetFileName = GenerateTargetFileName(swModel)
If targetFileName = "" Then
MsgBox "命名に必要なプロパティ(図番など)が取得できませんでした。中断します。", vbCritical
Exit Sub
End If
' ベースとなるパス(現在のslddrwと同じフォルダ)
Dim baseFilePath As String
baseFilePath = Left(swModel.GetPathName, InStrRev(swModel.GetPathName, ".") - 1)
' --- 3. ファイル出力実行 ---
' DXF出力
Call SafeSaveAs(swModExt, baseFilePath & DELIMITER & targetFileName & "." & EXT_DXF, swDocEXPORT_DXF)
' DWG出力
Call SafeSaveAs(swModExt, baseFilePath & DELIMITER & targetFileName & "." & EXT_DWG, swDocEXPORT_DWG)
MsgBox "エクスポート処理が完了しました。" & vbCrLf & "保存先: " & baseFilePath, vbInformation
End Sub
' ==============================================================================
' 命名エンジン: プロパティから社内ルールに基づいたファイル名を構築する
' ==============================================================================
Private Function GenerateTargetFileName(swModel As SldWorks.ModelDoc2) As String
On Error GoTo ErrorHandler
Dim swCustPropMgr As SldWorks.CustomPropertyManager
Set swCustPropMgr = swModel.Extension.CustomPropertyManager("")
Dim partNo As String
Dim rev As String
Dim projName As String
' プロパティの取得(Get6を使用し、値の欠落を確実にハンドリングする)
swCustPropMgr.Get6 "PartNo", False, partNo, False, False, False
swCustPropMgr.Get6 "Revision", False, rev, False, False, False
swCustPropMgr.Get6 "Project", False, projName, False, False, False
' --- バリデーション: 必須項目が空なら失敗させる ---
If partNo = "" Then
GenerateTargetFileName = ""
Exit Function
End If
' --- フォーマット整形: [Project]_[PartNo]_[Rev] ---
' リビジョンが空の場合は "0" とするなどの補完ロジックもここに集約
If rev = "" Then rev = "0"
GenerateTargetFileName = projName & DELIMITER & partNo & DELIMITER & rev
Exit Function
ErrorHandler:
GenerateTargetFileName = ""
End Function
' ==============================================================================
' IOコントローラー: エラーを許容し、堅牢に保存を実行する
' ==============================================================================
Private Sub SafeSaveAs(swModExt As SldWorks.ModelDocExtension, fullPath As String, fileType As Long)
Dim errors As Long
Dim warnings As Long
Dim boolStatus As Boolean
' ファイルの存在チェックや書き込み権限の確認をここで行うのが理想的
' 今回はAPIの戻り値によるエラーハンドリングを重視
boolStatus = swModExt.SaveAs(fullPath, fileType, swSaveAsOptions_Silent, Nothing, errors, warnings)
If Not boolStatus Then
' エラー発生時、ユーザーに詳細を知らせる(ログ出力への切り替えも検討すべき)
Debug.Print "Failed to save: " & fullPath & " | Error Code: " & errors
MsgBox "保存に失敗しました:" & vbCrLf & fullPath & vbCrLf & "エラーコード: " & errors, vbCritical
End If
End Sub
4. コードの解説:プロフェッショナルの視点
4.1 `Get6` メソッドの採用
`Get` ではなく `Get6` を使用している。これは最新のSolidWorks APIにおいて、プロパティの取得がより詳細に(値が空なのか、そもそもプロパティが存在しないのか)判定できるためだ。情報の「欠落」を「空文字」として扱うか「エラー」として扱うかの境界線を明確にできる。
4.2 ガード節(Guard Clauses)による早期リターン
`If swModel Is Nothing Then Exit Sub` のように、条件を満たさない場合に即座に処理を抜ける設計。これにより、メインロジックが `Else` の入れ子(ネスト)にならず、読みやすく、バグの混入を防ぐ「フラットなコード」になる。
4.3 `SafeSaveAs` によるエラーの局所化
保存処理を独立したサブプロシージャに切り出した。これにより、DXFの保存に失敗しても、DWGの保存に影響を与えずにエラーログを残すことができる。また、`swSaveAsOptions_Silent` を指定することで、保存時のダイアログによるユーザー操作の割り込みを防ぎ、自動化の連続性を担保している。
5. さらなる高みへ:実務における発展的検討事項
このコードをベースに、現場の要求に応じて以下の機能を拡張せよ。
1. ファイル名禁止文字の置換: `Project` プロパティに `/` や `*` が含まれていると、ファイルシステムが拒絶する。`Replace` 関数を用いて、安全な文字(`-` や `_`)へ置換するロジックを `GenerateTargetFileName` 内に組み込め。
2. ログ出力機能: `Debug.Print` ではなく、テキストファイルやデータベースへ実行結果を書き出す機能を実装せよ。これにより、「いつ、誰が、どの図面をエクスポートしたか」の監査証跡(Audit Trail)が得られる。
3. 非同期処理の検討: 大量の図面を一括処理する場合、APIの呼び出しは非常に重い。もし .NET (VB.NET) への移行が可能であれば、マルチスレッド化による高速化を検討すべきだ。
結論
「動けばいい」という考えは、エンジニアリングではなく単なる「作業」だ。
プロフェッショナルは、「動かない時、あるいは仕様が変わった時に、いかに最小限の修正で、いかに安全に復旧できるか」を設計の主眼に置く。
このコードを単なるコピペの道具としてではなく、堅牢なソフトウェアを作るための「設計思想」として受け取ってほしい。
