「単なる自動化」で終わるな。堅牢な図面一括出力エンジンの設計思想
設計現場において、「図面をPDFとDXFで書き出す」という作業は、一見すると単純なルーチンワークに見える。しかし、これを「数百枚の図面に対して、製品番号ごとのフォルダへ自動仕分けしながら実行する」という実務レベルの要求に昇華させたとき、そこには初心者とプロフェッショナルの決定的な差が現れる。
多くの開発者が陥る罠は、「動けばいい」という思考停止だ。
ファイルが見つからない、カスタムプロパティが空だった、書き出し中にSolidWorksがフリーズした、フォルダを作成する権限がない……。こうした例外処理を軽視したコードは、現場では「使い物にならないゴミ」として扱われる。
本稿では、単なるスクリプトではない、エラーに強く、保守性の高い「図面一括変換エンジン」を構築するための、アーキテクトの視点を提供しよう。
1. アマチュアのコードとプロフェッショナルの設計
まず、設計思想の差を明確にしよう。
アマチュアの設計:逐次処理型(Sequential Approach)
「ファイルを開く」→「プロパティを読む」→「保存する」→「閉じる」を、単一のループ内で愚直に繰り返す。
- 欠陥: プロパティが欠落していた瞬間にマクロが停止する。ファイルが読み取り専用だとエラーで止まる。SolidWorksのメモリリークを考慮していないため、枚数が増えると動作が極端に重くなる。
プロフェッショナルの設計:パイプライン型(Pipeline Approach)
処理を「メタデータ抽出」「ディレクトリ管理」「変換実行」の3つの責務に分離(Separation of Concerns)する。
- 利点:
- 堅牢性: プロパティがなくても、エラーログに記録して次のファイルへ進むことができる。
- 拡張性: 保存先ルールが変わっても、ディレクトリ管理ロジックだけを修正すればよい。
- 信頼性: `FileSystemObject` を活用し、ディレクトリの存在確認と生成を確実に行う。
2. 技術的な急所:APIの挙動とリソース管理
実装に入る前に、SolidWorks API特有の「重み」を理解しておく必要がある。
メタデータの取得(Custom Property Manager)
製品番号(PartNumber)は、図面のカスタムプロパティから取得する。ここで重要なのは、「プロパティが存在しない場合」のハンドリングだ。`Get6` メソッドを使用し、取得できなかった場合に空文字を返すのではなく、明確にエラーとして扱う設計が必要である。
保存処理(Extension.SaveAs)
`Document.SaveAs` ではなく、`Document.Extension.SaveAs` を使用せよ。後者は、PDFやDXFといった異なるフォーマットへの書き出しにおいて、より詳細なオプション制御が可能であり、現代的なSolidWorks開発のスタンダードである。
メモリの解放
ループ内で `swDoc.CloseDoc` を忘れることは、メモリリークへの片道切符だ。また、オブジェクト変数への参照を明示的に `Nothing` にすることで、COMオブジェクトのライフサイクルを制御する。
3. 実装例:プロダクション・グレードの変換エンジン
以下に、実務でそのまま利用可能なレベルの、堅牢な設計に基づいたVBAコードを示す。このコードは、`FileSystemObject` を利用してディレクトリを制御し、エラーが発生してもプロセスを中断させない構造になっている。
' ==============================================================================
' Module: DrawingExportEngine
' Description: 図面ファイルの一括PDF/DXF出力および製品番号別フォルダ振り分け
' Author: Chief Architect
' ==============================================================================
Option Explicit
' 外部参照設定: Microsoft Scripting Runtime (FileSystemObject用)
' 参照設定から "Microsoft Scripting Runtime" にチェックを入れてください。
Sub Main()
Dim swApp As SldWorks.SldWorks
Set swApp = Application.SldWorks
' 1. 処理対象ディレクトリの設定
Dim targetDir As String
Dim outputBaseDir As String
targetDir = "C:\Work\SourceDrawings\" ' 図面が入っているフォルダ
outputBaseDir = "C:\Work\Output\" ' 出力先ルート
' 2. エンジンの起動
Debug.Print "--- Batch Process Started ---"
Call ExecuteBatchConversion(swApp, targetDir, outputBaseDir)
Debug.Print "--- Batch Process Completed ---"
End Sub
''' <summary>
''' メインのループ処理。エラーが発生してもプロセスを止めない。
''' </summary>
Private Sub ExecuteBatchConversion(swApp As SldWorks.SldWorks, sourceDir As String, outputBaseDir As String)
Dim fso As New FileSystemObject
Dim sourceFolder As Folder
Dim fileItem As File
Dim swModel As SldWorks.ModelDoc2
' ディレクトリ存在確認
If Not fso.FolderExists(sourceDir) Then
MsgBox "ソースディレクトリが見つかりません: " & sourceDir, vbCritical
Exit Sub
End If
Set sourceFolder = fso.GetFolder(sourceDir)
' 高速化のための設定
swApp.UserControl = False ' ユーザー操作を抑制
' ファイルループ
For Each fileItem In sourceFolder.Files
' 図面ファイル(.SLDDRW)のみを対象とする
If LCase(fso.GetExtensionName(fileItem.Path)) = "slddrw" Then
On Error GoTo ErrorHandler
' ファイルを開く
Set swModel = swApp.OpenDoc6(fileItem.Path, swDocDRAWING, swOpenDocOptions_Silent, "", 0, 0)
If Not swModel Is Nothing Then
' 1. メタデータの取得(製品番号)
Dim partNumber As String
partNumber = GetCustomProperty(swModel, "PartNumber")
' プロパティが取得できない場合は「Unknown」として扱う
If partNumber = "" Then partNumber = "Unknown_Part"
' 2. 保存先フォルダの構築
Dim targetFolder As String
targetFolder = outputBaseDir & partNumber & "\"
If Not fso.FolderExists(targetFolder) Then
fso.CreateFolder targetFolder
End If
' 3. PDFとDXFの同時出力
Call ExportFiles(swModel, targetFolder, fso.GetBaseName(fileItem.Path))
' 4. 文書のクローズとリソース解放
swApp.CloseDoc swModel.GetTitle
Set swModel = Nothing
End If
End If
NextFile:
Next fileItem
swApp.UserControl = True
Exit Sub
ErrorHandler:
Debug.Print "Error processing file: " & fileItem.Path & " | Error: " & Err.Description
Resume NextFile
End Sub
''' <summary>
''' カスタムプロパティを取得する。
''' </summary>
Private Function GetCustomProperty(swModel As SldWorks.ModelDoc2, propName As String) As String
Dim swCustPropMgr As SldWorks.CustomPropertyManager
Dim valOut As String
Dim resValOut As String
Set swCustPropMgr = swModel.Extension.CustomPropertyManager("")
swCustPropMgr.Get6 propName, valOut, resValOut
GetCustomProperty = resValOut
End Function
''' <summary>
''' PDFおよびDXFへの書き出しを実行する。
''' </summary>
Private Sub ExportFiles(swModel As SldWorks.ModelDoc2, targetDir As String, baseName As String)
Dim swExt As SldWorks.ModelDocExtension
Set swExt = swModel.Extension
Dim errors As Long
Dim warnings As Long
Dim pdfPath As String
Dim dxfPath As String
pdfPath = targetDir & baseName & ".pdf"
dxfPath = targetDir & baseName & ".dxf"
' PDF出力
Dim statusPdf As Boolean
statusPdf = swExt.SaveAs(pdfPath, swSaveAsCurrentVersion, swSaveAsOptions_Silent, Nothing, errors, warnings)
If Not statusPdf Then
Debug.Print " [PDF Error] " & baseName & " : Code " & errors
End If
' DXF出力
Dim statusDxf As Boolean
statusDxf = swExt.SaveAs(dxfPath, swSaveAsCurrentVersion, swSaveAsOptions_Silent, Nothing, errors, warnings)
If Not statusDxf Then
Debug.Print " [DXF Error] " & baseName & " : Code " & errors
End If
End Sub
4. プロフェッショナルのための運用アドバイス
このコードを実戦配備するにあたり、以下の3点を念頭に置いてほしい。
① 冪等性(Idempotency)の確保
「同じプログラムを2回実行しても、システムが壊れないこと」が重要だ。このコードでは、`fso.FolderExists` を用いてフォルダの重複作成を防ぎ、`swSaveAsOptions_Silent` を用いてダイアログによる停止を防いでいる。これにより、途中でエラーが起きても、再実行時にスムーズに継続できる。
② ログ出力の重要性
`Debug.Print` を使っているが、実運用ではこれをテキストファイルへの書き出しに変更することを強く推奨する。ユーザーは「どのファイルが、なぜ失敗したのか」を知る権利がある。エラーを無視するのではなく、「エラーを記録して、プロセスを完遂させる」のがプロの設計だ。
③ データベース連携への発展
もし、製品番号の管理がExcelやSQL Serverで行われているなら、`GetCustomProperty` の代わりに、そのデータベースから情報を引き出すロジックを組み込むだけでいい。責務を分離していれば、この「拡張」は極めて容易だ。
結論
自動化エンジニアの価値は、「コードを書くこと」ではなく、「設計の不備による現場の混乱を防ぐこと」にある。
例外を恐れず、リソースを制御し、構造的に考える。この規律こそが、SolidWorks VBAを「単なるマクロ」から「信頼されるエンジニアリングツール」へと昇華させるのだ。
