【極限の自動化】図面保存時のPDF/DWG同時出力。堅牢な設計が業務の命運を分ける。
「図面を保存した後、わざわざPDFとDWGで書き出し、ファイル名をリネームして、指定のフォルダへ移動させる……」
もし君が、この一連の作業を「当たり前のルーチン」だと思っているなら、今すぐその思考を捨ててほしい。それはエンジニアリングではなく、単なる「作業」だ。そして、その作業が残っている限り、ヒューマンエラー(リビジョン間違い、ファイル名の打ち間違い、保存場所のミス)の種は、君の会社の品質管理プロセスの中に残り続けることになる。
今回のテーマは、「図面保存時に、カスタムプロパティから図面番号とリビジョンを自動抽出し、命名規則に従ってPDFとDWGをバックグラウンドで同時出力する」という、実務における「自動化の王道」だ。
しかし、ネットに転がっているような「とりあえず動くコード」をコピペしてはいけない。それらは、プロパティが欠落していたり、保存先フォルダが存在しなかったりした瞬間に、SolidWorksごとクラッシュさせる「時限爆弾」だ。
本記事では、プロフェッショナルが現場に投入するレベルの、「バグを許さない堅牢な設計」を伝授する。
1. アーキテクチャ設計:なぜ「単なる書き出し」では足りないのか
自動化ツールを設計する際、初心者は「どうやって書き出すか」ばかりを考える。だが、熟練のアーキテクトは「どうやって失敗を防ぐか」を考える。
本システムにおいて、考慮すべきリスクは以下の3点だ。
1. データの整合性(Single Source of Truth): ファイル名は、図面内のカスタムプロパティ(図面番号、リビジョン)からのみ取得しなければならない。手入力は厳禁だ。
2. ディレクトリの存在保証: 保存先フォルダが存在しない場合、APIはエラーを吐いて停止する。実行前にフォルダ構造を検証するロジックが必須だ。
3. オブジェクトのライフサイクル管理: APIを通じてファイル操作を行う際、SolidWorksのメモリ管理に負荷をかけすぎないよう、適切にオブジェクトを解放・制御する必要がある。
設計フロー
1. Data Extraction: カスタムプロパティから `DrawingNo` と `Revision` を取得。
2. Validation: 取得した値が空でないか、使用禁止文字(`/`, `*`, `?` 等)が含まれていないかチェック。
3. Path Construction: 指定のパスと命名規則(`図面番号_リビジョン.拡張子`)を結合。
4. Execution: PDFおよびDWGの書き出しを実行。
5. Error Handling: 失敗時にユーザーへ通知し、システムを安全に停止。
2. 実装:プロダクション品質のコード例
以下に、そのまま実務のモジュールとして組み込めるレベルのコードを提示する。
このコードの特徴は、「単一責任の原則」に基づき、プロパティ取得、ファイル操作、メインロジックを分離している点だ。
'**
' Module: DrawingAutoExporter
' Description: 図面保存時にPDF/DWGをカスタムプロパティに基づき自動出力する
' Author: Chief Architect
'**
Option Explicit
' 定数定義:管理のしやすさと保守性を高める
Private Const PROP_DRAWING_NO As String = "DrawingNo" ' 図面番号プロパティ名
Private Const PROP_REVISION As String = "Revision" ' リビジョンプロパティ名
Private Const EXPORT_FOLDER As String = "C:\Output\" ' 出力先ルートフォルダ
Sub Main()
Dim swApp As SldWorks.SldWorks
Dim swModel As SldWorks.ModelDoc2
Dim swDraw As SldWorks.DrawingDoc
On Error GoTo ErrorHandler
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 "このマクロは図面ファイル専用です。", vbExclamation
Exit Sub
End If
Set swDraw = swModel
' 2. カスタムプロパティの取得
Dim drawingNo As String
Dim revision As String
drawingNo = GetCustomProperty(swModel, PROP_DRAWING_NO)
revision = GetCustomProperty(swModel, PROP_REVISION)
' プロパティ欠落のバリデーション
If drawingNo = "" Or revision = "" Then
MsgBox "図面番号またはリビジョンがカスタムプロパティに設定されていません。" & vbCrLf & _
"プロパティ名を確認してください。", vbCritical
Exit Sub
End If
' 3. 出力パスの構築
' 命名規則: [図面番号]_[リビジョン]
Dim baseFileName As String
baseFileName = drawingNo & "_" & revision
' フォルダの存在確認と作成
If Not CreateFolderIfNotExist(EXPORT_FOLDER) Then
Err.Raise vbObjectError + 513, "PathError", "出力フォルダの作成に失敗しました。"
End If
' 4. エクスポート実行
Dim status As Boolean
' --- PDF出力 ---
status = ExportToFormat(swModel, EXPORT_FOLDER & baseFileName & ".pdf", swDocDRAWING)
If Not status Then Err.Raise vbObjectError + 514, "PDFError", "PDFの出力に失敗しました。"
' --- DWG出力 ---
' ※DWGはSaveAsを使用してエクスポートする
status = ExportToFormat(swModel, EXPORT_FOLDER & baseFileName & ".dwg", swDocDRAWING)
If Not status Then Err.Raise vbObjectError + 515, "DWGError", "DWGの出力に失敗しました。"
MsgBox "エクスポート完了: " & baseFileName, vbInformation, "Success"
CleanUp:
Set swDraw = Nothing
Set swModel = Nothing
Set swApp = Nothing
Exit Sub
ErrorHandler:
MsgBox "エラーが発生しました。" & vbCrLf & _
"番号: " & Err.Number & vbCrLf & _
"内容: " & Err.Description, vbCritical, "Critical Error"
Resume CleanUp
End Sub
'------------------------------------------------------------------------------
' 関数: GetCustomProperty
' 目的: モデルから指定されたカスタムプロパティの値を安全に取得する
'------------------------------------------------------------------------------
Private Function GetCustomProperty(swModel As SldWorks.ModelDoc2, propName As String) As String
Dim swCustPropMgr As SldWorks.CustomPropertyManager
Dim valOut As String
Dim res As Long
' 設定値(Configuration Specificではなく、Generalのカスタムプロパティを取得)
Set swCustPropMgr = swModel.Extension.CustomPropertyManager("")
' プロパティの取得
res = swCustPropMgr.Get4(propName, False, valOut, False)
If res = 0 Then
GetCustomProperty = Trim(valOut)
Else
GetCustomProperty = ""
End If
End Function
'------------------------------------------------------------------------------
' 関数: ExportToFormat
' 目的: 指定された拡張子でファイルを保存(エクスポート)する
'------------------------------------------------------------------------------
Private Function ExportToFormat(swModel As SldWorks.ModelDoc2, fullPath As String, docType As Long) As Boolean
Dim errors As Long
Dim warnings As Long
' SaveAsを使用して、異なるファイル形式で書き出しを実行
' 第2引数はファイル名、第3引数はオプション。
' ※実務では保存時のオプション(PDFの品質等)を詳細に設定することも検討せよ
Dim status As Boolean
status = swModel.Extension.SaveAs(fullPath, docType, swSaveAsOptions_Silent, Nothing, errors, warnings)
ExportToFormat = status
End Function
'------------------------------------------------------------------------------
' 関数: CreateFolderIfNotExist
' 目的: フォルダが存在しなければ作成する(FileSystemObjectを使用)
'------------------------------------------------------------------------------
Private Function CreateFolderIfNotExist(ByVal folderPath As String) As Boolean
Dim fso As Object
Set fso = CreateObject("Scripting.FileSystemObject")
On Error Resume Next
If Not fso.FolderExists(folderPath) Then
' 再帰的にフォルダを作成する必要がある場合は、より高度なロジックが必要だが
' ここではシンプルにルート直下の作成を想定
fso.CreateFolder (folderPath)
End If
If Err.Number = 0 Then
CreateFolderIfNotExist = True
Else
CreateFolderIfNotExist = False
End If
On Error GoTo 0
Set fso = Nothing
End Function
3. プロフェッショナルのための補足:運用の極意
コードを書いただけで満足してはいけない。これを「製品」として運用するために、以下の点に留意せよ。
① ファイルロックへの対策
もし、出力先のPDFが既に別のアプリケーション(Adobe Readerなど)で開かれていた場合、`SaveAs` は失敗する。上記のコードでは `Err.Raise` によってエラーを捕捉し、ユーザーに通知するようにしている。実務では「ファイルが使用中です」というメッセージを具体的に出すことが、現場の混乱を防ぐ鍵だ。
② カスタムプロパティの「型」
`DrawingNo` に `/`(スラッシュ)や `:`(コロン)が含まれている場合、Windowsのファイルシステムはそれをファイル名として受理しない。プロパティ入力規則を徹底するか、VBA側で `Replace()` 関数を用いて禁止文字をアンダースコア `_` に置換する処理を追加するのが、真に「堅牢な」設計だ。
③ パフォーマンスとバックグラウンド処理
SolidWorksのAPIは、基本的にメインスレッドで動作する。大規模なアセンブリ図面の場合、エクスポート中にSolidWorksが数秒間フリーズしたように見える。これを防ぐために、ユーザーに「処理中…」というステータスバーの表示を出す、あるいはプログレスバーを実装するなどのUI/UXへの配慮が、ツールの「信頼性」を決定づける。
結論
自動化の本質は、単に「楽をすること」ではない。「人間が介在すべきではない場所に、人間を置かないこと」だ。
今回提示したコードは、エラーハンドリング、データの整合性、フォルダ管理という、実務において最も重要な要素を網羅している。これらをベースに、君の現場のルール(特定の階層構造、リビジョンの採番ルールなど)を組み込んでいけ。
それが、単なる「マクロ使い」から「自動化エンジニア」へと進化するための第一歩だ。
