【実務・中級編】【実務中級者向け】図面を開く前にファイル更新日時とサイズをチェック:FSO連携による効率化 – AutoCAD VBA解析バイブル

スポンサーリンク

全件オープンは素人の仕事。FileSystemObjectによる「前処理フィルター」でバッチ処理を極限まで高速化せよ

数千枚の図面ファイル(DWG/DXF)をバッチ処理する際、ループ内で脳死で `AcadDocuments.Open` を呼び出していないでしょうか?

もしそうなら、あなたのスクリプトは甚大な時間とコンピューティングリソースをドブに捨てています。

AutoCADの図面を開くという動作は、単にファイルを読み込むだけではありません。データベース構造の構築、参照ファイル(XRef)の解決、表示キャッシュの生成、ActiveXオブジェクトツリーのメモリ割り当てなど、極めて重い「オブジェクトライフサイクルの開始」を意味します。

本稿では、実務中級者の開発者が脱皮し、プロのアーキテクトとして堅牢かつ超高速な自動化ツールを構築するための「FileSystemObject(FSO)による事前のメタデータ検証戦略」を伝授します。図面を開く「前」に必要な判定をすべて終わらせる、本物の設計パターンをマスターしてください。

—

1. なぜ「開く前のチェック」が絶対に必要なのか?

パフォーマンスの圧倒的格差:1000倍の差

AutoCAD APIを通じてDWGファイルを開く処理(`AcadDocuments.Open`)には、ファイルサイズや環境によりますが、1ファイルあたり1秒〜数秒のオーバヘッドが発生します。一方、Windowsのファイルシステムから直接メタデータ(更新日時・サイズ)を取得する処理は、ミリ秒(0.001秒)以下です。

1,000枚の図面のうち、更新があった50枚だけを処理したいケースを考えてみてください。

  • アンチパターン(全件オープン): 1,000枚 × 2秒 = 約33分
  • プロの設計(FSO事前フィルター): 950枚スキップ(0.5秒)+ 50枚処理(100秒)= 約1分40秒

処理速度は20倍以上向上します。

メモリリークとクラッシュの防止

AutoCAD VBAは内部的にActiveX(COM)インターフェースを介して図面オブジェクトを操作します。大量の図面を連続して開閉すると、どれだけ慎重に `Close` や `Set obj = Nothing` を記述していても、COMオブジェクトの解放漏れやメモリ断片化が発生し、最終的にはAutoCADが強制終了(Fatal Error)します。

処理対象を絞り込むことは、単なるスピードアップではなく、バッチ処理全体の完走率(信頼性)を担保する必須要件なのです。

—

2. 実務で落とし穴になる「3つのエッジケース」

単に「日付が新しいか」を見るだけでは、運用環境で必ずバグを生みます。プロの設計では以下の3点をクリアしなければなりません。

1. ネットワーク切断等による「0バイト(破損)ファイル」の存在
保存処理の途中でネットワークが切れたり異常終了すると、0バイトや極小サイズの壊れたDWGが残ることがあります。これを `AcadDocuments.Open` に渡すとAutoCADが応答なし(フリーズ)になります。
2. 他ユーザーが編集中(DWL/DWL2ロック)のハンドリング
誰かがAutoCADで開いている図面には同階層に `.dwl` / `.dwl2` というロックファイルが生成されます。開こうとするとダイアログが出現し、バッチ処理が永久にストップします。
3. レイトブロッキング(遅延結合)による環境依存の排除
`Microsoft Scripting Runtime` への参照設定を必須にすると、ツールを配布した際に「参照不可」エラーで落ちます。プロダクション環境へ配布するコードは遅延結合(Late Binding)で書くのが鉄則です。

—

3. プロダクション適用可能なVBA実装コード

以下のコードは、上記の課題をすべて解決した実務仕様の設計です。モジュールにそのまま貼り付けて使用できます。

Option Explicit

‘ ==============================================================================
‘ 構造体: チェック結果を保持するデータ型
‘ ==============================================================================
Public Type FileValidationResult
CanProcess As Boolean ‘ 処理を実行すべきかフラグ
Reason As String ‘ 判定理由(ログ用)
FileSizeByte As LongLong ‘ ファイルサイズ
LastModified As Date ‘ 最終更新日時
End Type

‘ ==============================================================================
‘ メイン処理: バッチ処理のエントリポイント
‘ ==============================================================================
Public Sub RunSmartBatchProcess()
Dim targetFolder As String
Dim thresholdDate As Date
Dim dwgFiles() As String
Dim filePath As Variant
Dim valResult As FileValidationResult
Dim processedCount As Long
Dim skippedCount As Long

‘ — 設定値 —
targetFolder = “C:\Projects\DrawingFolder\”
‘ 例: 2023年10月1日以降に更新された図面のみを対象とする
thresholdDate = DateSerial(2023, 10, 1)

‘ 対象フォルダ内のDWGファイル一覧を取得(簡易実装)
dwgFiles = GetDwgFileList(targetFolder)

If (Not dwgFiles) = -1 Then
MsgBox “対象のDWGファイルが見つかりません。”, vbExclamation
Exit Sub
End If

‘ パフォーマンス計測開始
Dim startTime As Double
startTime = Timer

‘ — 画面描画と描画警告をオフにして最高速化 —
ThisDrawing.Application.ScreenUpdating = False

On Error GoTo ErrorHandler

For Each filePath In dwgFiles
‘ 【核心部】AutoCADで開く前にFSOで高速検証
valResult = ValidateDwgFile(CStr(filePath), thresholdDate, MinSizeKB:=10)

If valResult.CanProcess Then
‘ — 条件を満たしたファイルのみHeavyなAutoCAD処理を実行 —
Debug.Print “[PROCESS] ” & filePath & ” (Reason: ” & valResult.Reason & “)”

Call ExecuteAutoCADTask(CStr(filePath))
processedCount = processedCount + 1
Else
‘ — 条件不適合のファイルは即座にスキップ —
Debug.Print “[SKIP] ” & filePath & ” (Reason: ” & valResult.Reason & “)”
skippedCount = skippedCount + 1
End If
Next filePath

‘ 処理結果の出力
MsgBox “バッチ処理が完了しました。” & vbCrLf & _
“処理実行: ” & processedCount & ” 件” & vbCrLf & _
“スキップ: ” & skippedCount & ” 件” & vbCrLf & _
“所要時間: ” & Format(Timer – startTime, “0.00”) & ” 秒”, vbInformation

CleanUp:
ThisDrawing.Application.ScreenUpdating = True
Exit Sub

ErrorHandler:
MsgBox “予期せぬエラーが発生しました: ” & Err.Description, vbCritical
Resume CleanUp
End Sub

‘ ==============================================================================
‘ 核心関数: FileSystemObjectを用いた高速ファイル事前検証
‘ ==============================================================================
Public Function ValidateDwgFile( _
ByVal filePath As String, _
ByVal minDate As Date, _
Optional ByVal MinSizeKB As Long = 10 _
) As FileValidationResult

Dim result As FileValidationResult
Dim fso As Object
Dim targetFile As Object
Dim dwlPath1 As String
Dim dwlPath2 As String
Dim minSizeBytes As LongLong

‘ デフォルト初期化
result.CanProcess = False
result.Reason = “Unknown”

‘ 遅延結合(CreateObject)により参照設定のトラブルを防止
Set fso = CreateObject(“Scripting.FileSystemObject”)

‘ 1. 存在チェック
If Not fso.FileExists(filePath) Then
result.Reason = “ファイルが存在しません”
ValidateDwgFile = result
Exit Function
End If

Set targetFile = fso.GetFile(filePath)
result.FileSizeByte = targetFile.Size
result.LastModified = targetFile.DateLastModified

‘ 2. ファイルサイズチェック(0バイトや極小破損ファイルの除外)
minSizeBytes = CLngLng(MinSizeKB) 1024
If targetFile.Size < minSizeBytes Then result.Reason = "ファイルサイズが小さすぎます(" & Format(targetFile.Size / 1024, "0.0") & "KB)" ValidateDwgFile = result Exit Function End If ' 3. 更新日時チェック(判定対象より古い場合はスキップ) If targetFile.DateLastModified < minDate Then result.Reason = "更新日時が指定日より古い(" & Format(targetFile.DateLastModified, "yyyy/mm/dd hh:nn") & ")" ValidateDwgFile = result Exit Function End If ' 4. ロックファイル(DWL/DWL2)チェック(他人が開いているか判定) ' DWGと同じフォルダにある同名の.dwl / .dwl2の存在を確認 dwlPath1 = fso.GetParentFolderName(filePath) & "\" & fso.GetBaseName(filePath) & ".dwl" dwlPath2 = dwlPath1 & "2" If fso.FileExists(dwlPath1) Or fso.FileExists(dwlPath2) Then result.Reason = "他ユーザーまたは別プロセスによりロックされています(.dwl検出)" ValidateDwgFile = result Exit Function End If ' すべてのゲートキーパーを通過 result.CanProcess = True result.Reason = "処理対象(最終更新: " & Format(targetFile.DateLastModified, "yyyy/mm/dd hh:nn") & ")" Set targetFile = Nothing Set fso = Nothing ValidateDwgFile = result End Function ' ============================================================================== ' 個別処理: 実際にAutoCADで図面を開いて行う重い処理 ' ============================================================================== Private Sub ExecuteAutoCADTask(ByVal filePath As String) Dim doc As AcadDocument On Error GoTo DocError ' FSOのチェックを通過しているため、安全にOpenできる ' ReadOnly:=False で開く(必要に応じて設定) Set doc = ThisDrawing.Application.Documents.Open(filePath) ' ------------------------------------------------------------- ' ここに本来の編集・抽出処理を記述(例:図面枠ブロックの属性書き換え等) ' ------------------------------------------------------------- ' 処理完了後、保存して閉じる doc.Close SaveChanges:=True Exit Sub DocError: ' 開く処理自体が失敗した場合のリカバリ Debug.Print "[ERROR] 開く処理に失敗: " & filePath & " - " & Err.Description If Not doc Is Nothing Then doc.Close SaveChanges:=False End If End Sub ' ============================================================================== ' ユーティリティ: フォルダ内のDWGファイルを配列で取得 ' ============================================================================== Private Function GetDwgFileList(ByVal folderPath As String) As String() Dim fso As Object Dim folder As Object Dim file As Object Dim list() As String Dim count As Long Set fso = CreateObject("Scripting.FileSystemObject") If Not fso.FolderExists(folderPath) Then Exit Function Set folder = fso.GetFolder(folderPath) ReDim list(0 To folder.Files.count) count = 0 For Each file In folder.Files If LCase(fso.GetExtensionName(file.Path)) = "dwg" Then list(count) = file.Path count = count + 1 End If Next file If count > 0 Then
ReDim Preserve list(0 To count – 1)
GetDwgFileList = list
End If
End Function

—

4. この設計が優れている理由(アーキテクチャの解説)

① 「ゲートキーパー(門番)」パターンの採用

関数の責任を明確に分離しています。`ValidateDwgFile` 関数はファイルシステムレベルの「門番」として機能し、AutoCADオブジェクト空間へ足を踏み入れるべきか否かだけを高速に判定します。これにより、メインループの可読性が格段に向上し、テストも容易になります。

② DWL/DWL2による競合回避

AutoCAD特有のロックファイル(`.dwl` / `.dwl2`)の存在チェックをFSOで行うことで、`AcadDocuments.Open` を実行した際にダイアログでスクリプトが停止(ハングアップ)する事態を未然に防いでいます。これは夜間の無人バッチ処理などを運用する上で不可欠なテクニックです。

③ ユーザー定義型(`Type`)による情報のカプセル化

判定結果(Boolean)だけでなく、「なぜスキップされたのか」の理由(`Reason`)やファイルメタデータを構造体として返しています。これにより、実行後に「どのファイルがなぜ処理されなかったのか」の精細なログファイル(CSV等)を容易に生成できます。

—

5. さらに先を目指すための拡張設計(データベース/ログ連携)

実務でさらに一歩進んだシステムを構築する場合、このFSO事前チェックを差分管理データベースと連携させます。

差分処理(Delta Processing)の仕組み

CSVファイルやSQLite、あるいはレジストリ等に「前回のバッチ実行日時」と「各ファイルのハッシュ値(または最終更新日時+サイズ)」を記録しておきます。

[処理判定フロー]
1. FSOで図面の UpdateDate を取得
2. 内部データベースの前回処理日時と比較
3. UpdateDate > LastProcessDate の場合のみ AutoCAD処理へ回す
4. 処理成功後、データベースの LastProcessDate を更新

この設計を導入することで、万が一途中でバッチが強制終了した場合でも、スクリプトを再実行すれば「未処理のファイルおよび途中で落ちたファイルから自動的に再開(レジューム)」することが可能になります。

—

まとめ:エレガントなコードは「無駄な処理をさせない」

優れた自動化エンジニアと駆け出しの開発者の差は、「いかにAutoCAD APIを使いこなすか」ではなく、「いかに不要な場面でAutoCAD APIを呼び出さないか」という設計思想に現れます。

1. 重い処理(AutoCAD API)の前に、軽い処理(FSO)でフィルタリングする。
2. 0バイト・ロック状態・更新日付などの例外を事前に排除する(Fail-Fast)。
3. 環境に依存しないレイトブロッキングとエラーハンドリングを徹底する。

今回紹介したコードパターンを自身のライブラリに組み込み、数十倍のパフォーマンスとビクともしない堅牢性を備えたバッチツールの構築に役立ててください。

タイトルとURLをコピーしました