図面変換の迷宮を脱する:大量DWGファイルを「見えざる手」で一括アップグレードする設計思想
多くの開発者が、業務自動化の第一歩として「フォルダ内のファイルを順番に開いて処理する」というコードを書く。しかし、実務の最前線、数千枚の図面が眠るサーバー領域でそのコードを実行した瞬間、彼らは直面することになる。AutoCADがメモリを食いつぶしてクラッシュし、どのファイルでエラーが起きたのかすら分からず、数時間の作業が水の泡になるという絶望に。
「開かずに処理する」とは、単に画面に表示させないことではない。「AutoCADのプロセスとユーザーの作業セッションを、いかに論理的に分離し、リソースのライフサイクルを完全に制御するか」という設計思想の問題なのだ。
本稿では、中級者からプロフェッショナルへとステップアップするために不可欠な、大量のDWGファイルを最新形式(AutoCAD 2018形式以降)へ一括変換するための、堅牢で高効率なバッチ処理の設計と実装について伝授する。
1. 凡庸な設計と、プロフェッショナルな設計の決定的な差
まず、なぜ君たちが書いたバッチ処理が不安定なのか、その原因を解剖しよう。
凡庸な設計(Anti-Pattern)
- `For Each` ループの中で `Documents.Open` を呼び出し、そのまま `SaveAs` する。
- エラーが発生したら `MsgBox` で停止させる。
- ループの最後で `Set doc = Nothing` とだけ書く。
これでは、メモリリーク(Memory Leak)を防げない。AutoCADは、開いたドキュメントのオブジェクトを内部的に保持し続ける性質がある。大量のファイルを処理する場合、メモリの解放が追いつかず、最終的に「Out of Memory」で死ぬのは必然だ。
プロフェッショナルな設計(The Architect’s Way)
- 分離(Isolation): ファイル走査、ファイル操作、AutoCAD処理の3層に分ける。
- 堅牢な例外処理(Robust Exception Handling): 1枚の破損ファイルで全工程を止めない。エラーは「ログ」に吐き出し、次のファイルへ進む。
- ライフサイクル管理(Lifecycle Management): `Close` メソッドを確実に呼び、オブジェクト参照を明示的に破棄する。
- ユーザー体験の制御(UI Suppression): `Application.Visible` を制御し、ダイアログの介入を最小限に抑える。
2. 実装:堅牢なアップグレード・エンジンの構築
以下に、プロダクション環境で使用することを想定したコードを示す。単なるコピペ用ではない。各ブロックに込められた「意図」を読み解いてほしい。
Option Explicit
' ==============================================================================
' 業務自動化エンジニア向け:DWG一括アップグレード・エンジン
' 設計思想:
' 1. エラーを致命傷にせず、ログに記録して続行する(Fault Tolerance)
' 2. メモリリークを防ぐための厳格なオブジェクト破棄(Memory Management)
' 3. FileSystemObjectによる高速なファイル走査(Performance)
' ==============================================================================
Private Const LOG_FILE_NAME As String = "Upgrade_Log.txt"
Public Sub BatchUpgradeDWG()
Dim fso As Object
Dim targetFolder As String
Dim logPath As String
' --- 初期設定 ---
' 実行前に必ず対象フォルダを指定すること
targetFolder = "C:\Work\TargetDrawings" ' ← ここを実環境に合わせて変更
logPath = targetFolder & "\" & LOG_FILE_NAME
Set fso = CreateObject("Scripting.FileSystemObject")
If Not fso.FolderExists(targetFolder) Then
MsgBox "指定されたフォルダが存在しません。", vbCritical
Exit Sub
End If
' ログファイルの初期化
InitializeLog logPath
' --- メインプロセス開始 ---
On Error GoTo ErrorHandler
' AutoCADのUI制御(ユーザーの邪魔をさせない)
' ※注意: Visible = False にすると、デバッグが困難になるため、
' 開発時は True にしておくことを推奨する。
Application.Visible = True
WriteLog logPath, "--- バッチ処理開始 ---"
' 再帰的にファイルを処理
ProcessFolder fso.GetFolder(targetFolder), logPath
WriteLog logPath, "--- 全工程完了 ---"
MsgBox "処理が完了しました。詳細はログを確認してください。", vbInformation
Set fso = Nothing
Exit Sub
ErrorHandler:
WriteLog logPath, "CRITICAL ERROR: " & Err.Description
MsgBox "予期せぬエラーが発生しました。ログを確認してください。", vbCritical
Set fso = Nothing
End Sub
' フォルダ内を再帰的に走査するサブプロシージャ
Private Sub ProcessFolder(folder As Object, logPath As String)
Dim subFolder As Object
Dim file As Object
' 1. ファイルの処理
For Each file In folder.Files
' 拡張子が .dwg かつ ログファイル自身ではない場合
If LCase(Right(file.Name, 4)) = ".dwg" Then
UpgradeSingleFile file.Path, logPath
End If
Next file
' 2. サブフォルダの再帰処理
For Each subFolder In folder.SubFolders
ProcessFolder subFolder, logPath
Next subFolder
End Sub
' 個別の図面をアップグレードするコア・エンジン
Private Sub UpgradeSingleFile(filePath As String, logPath As String)
Dim doc As AcadDocument
Dim fso As Object
Set fso = CreateObject("Scripting.FileSystemObject")
On Error GoTo FileError
' ファイルを開く(バックグラウンドでの動作を想定)
' Openメソッドは、ダイアログが出ないよう制御する必要がある
Set doc = Application.Documents.Open(filePath)
' --- アップグレード処理 ---
' AutoCAD 2018形式以降は、現在のAutoCADバージョンでのSaveAsが標準となる。
' ここでは、現在のバージョンで上書き保存することでアップグレードを行う。
' 必要に応じて、別名保存 (doc.FullName & "_new.dwg") に設計変更せよ。
doc.Save
doc.Close
WriteLog logPath, "SUCCESS: " & filePath
' オブジェクトの明示的な解放(これが生命線だ)
Set doc = Nothing
Set fso = Nothing
Exit Sub
FileError:
' ファイルが破損している、または使用中の場合
WriteLog logPath, "FAILED: " & filePath & " | Error: " & Err.Description
' エラー時も必ずドキュメントを閉じてメモリを解放する
On Error Resume Next
If Not doc Is Nothing Then
doc.Close False ' 保存せずに閉じる
Set doc = Nothing
End If
On Error GoTo 0
End Sub
' --- ユーティリティ関数群 ---
Private Sub InitializeLog(logPath As String)
Dim fso As Object
Set fso = CreateObject("Scripting.FileSystemObject")
Dim ts As Object
Set ts = fso.CreateTextFile(logPath, True)
ts.WriteLine "=== Upgrade Batch Log: " & Now & " ==="
ts.Close
End Sub
Private Sub WriteLog(logPath As String, message As String)
Dim fso As Object
Dim ts As Object
Set fso = CreateObject("Scripting.FileSystemObject")
' Appendモードで書き込み
Set ts = fso.OpenTextFile(logPath, 8, True)
ts.WriteLine "[" & Now & "] " & message
ts.Close
End Sub
3. アーキテクトの視点:ここが「勝ち」のポイントだ
このコードが、単なる「動くコード」ではなく「現場で耐えうるコード」である理由を解説しよう。
① オブジェクト・ライフサイクルの厳格な管理
`UpgradeSingleFile` 内の `FileError` ハンドラに注目してほしい。ファイルが破損していた場合、`doc.Open` は失敗するが、もし不完全な状態でオブジェクトが生成されていた場合、`doc.Close` を呼ばずに次のファイルへ行くと、AutoCADのメモリ空間に「幽霊オブジェクト」が残り続ける。これが連続すると、数分でAutoCADは沈黙する。「エラー時こそ、確実にクローズする」。これが鉄則だ。
② 再帰的走査(Recursive Traversal)
実務の図面管理は、フォルダ階層が深く複雑だ。`For Each file In folder.Files` だけでは、サブフォルダ内の図面を見落とす。`ProcessFolder` を再帰的に呼び出す設計にすることで、どんなに深い階層でも、漏れなく、かつスマートに走査できる。
③ ログこそが唯一の信頼
大量処理において、ユーザーが画面を凝視することは不可能だ。処理が完了したとき、ユーザーが求めるのは「成功したか?」ではなく「失敗したものはどれか? それはどういう理由か?」だ。`WriteLog` を通じた詳細な出力は、後続のリカバリ作業(手動修正)のコストを劇的に下げる。
4. さらに高みを目指すためのアドバイス
もし君が、このツールをさらに進化させたいのであれば、以下の領域に挑戦してほしい。
1. データベース連携: ログをテキストファイルではなく、SQLiteやAccessなどのDBに書き出す。これにより、過去の変換履歴をSQLで分析可能になる。
2. マルチプロセス化(高度): VBA単体では限界があるが、VBScriptやVB.NETを用いて、複数のAutoCADインスタンスを並列起動させる設計に踏み込む。
3. DXFへの対応: `fso.GetExtensionName` を使い、拡張子が `.dxf` の場合に `doc.SaveAs` の引数で適切なフォーマットを指定するロジックを追加する。
結びに
自動化エンジニアの仕事は、コードを書くことではない。「例外が発生することを前提とした、揺るぎないシステムを設計すること」だ。
今回提示した設計思想をマスターすれば、君が作成するツールは、単なる「便利ツール」を超え、企業のインフラを支える「堅牢な資産」へと昇華するだろう。健闘を祈る。
