【実務・中級編】【実務中級者向け】保存時の「ファイル名重複」回避アルゴリズム:連番付与と世代管理の自動化 – AutoCAD VBA解析バイブル

スポンサーリンク

AutoCAD VBAで実現する、図面保存の「絶対安全」戦略:連番付与と世代管理の極限の知見

設計現場の最前線で、AutoCAD VBAを駆使して業務効率化ツールを開発する諸君。
諸君が日々扱うのは、創造性の結晶であり、企業の知的資産そのものであるDWGファイルだ。この大切な資産を、たった一度の操作ミスで失うリスクを、諸君はどこまで真剣に考えているだろうか?

本稿で私が諸君に叩き込むのは、単なるファイル保存のテクニックではない。それは、ファイル管理における堅牢性、信頼性、そして未来への拡張性を見据えた、極限の設計思想だ。一般的なリファレンスをなぞるような安易なアプローチは、私には許せない。オブジェクトのライフサイクル、ファイルI/Oのパフォーマンス、そして何よりヒューマンエラーの恐ろしさを知り尽くした者だけが語れる、魂の知見をここに開示しよう。

—

導入:なぜ「安全な保存」が極めて重要なのか

多くの設計者は、図面を保存する際に「上書き保存」か「別名保存」を選ぶ。手動でファイル名を変更し、「_rev1」や「_final」といったサフィックスを付与するだろう。だが、この「手動」という行為こそが、諸君の業務効率化への道を阻み、致命的なリスクをはらんでいる。

  • ヒューマンエラーのリスク: ファイル名の間違い、既存ファイルの意図しない上書き、変更履歴の欠落。これらはすべて、プロジェクトの遅延、再作業、最悪の場合は訴訟問題に発展しかねない。
  • 履歴管理の欠如: どのバージョンがいつ、誰によって、どのような変更が加えられたのか。その情報が曖昧では、設計変更の経緯を追跡できず、品質保証の観点からも大きな問題となる。
  • 非効率性: 手動でのリネーム作業は、積もり積もれば膨大な時間ロスを生む。諸君の創造的な時間を、そのような単純作業に費やすべきではない。

我々が目指すべきは、「思考停止で保存しても、常に安全な状態が保たれ、かつ履歴が自動で構築される」システムだ。本記事では、既存ファイルが存在する場合に自動で「_revN」のような連番を付与し、過去のデータを上書き消去しない、堅牢なファイル命名ロジックをAutoCAD VBAで実現する方法を伝授する。

—

問題提起:安易な連番付与の限界と「世代管理」の必要性

「既存ファイルがあったら、自動で連番を振って保存すればいいんでしょ?」
そう考える諸君は、まだ甘い。単に連番を増やし続けるだけでは、以下の問題に直面する。

1. ファイル爆発: 無制限に世代が増えれば、ストレージ容量を圧迫し、必要なファイルを探すのに苦労する。
2. 関連ファイルとの整合性: 図面だけでなく、XREF、画像、点群データなど、関連ファイルも連番に追従させる必要がある場合、ロジックは複雑化する。
3. パフォーマンスの低下: 膨大なファイルの中から最新世代や特定の世代を探し出す作業は、ファイルシステムへのアクセス負荷を増大させ、処理速度を低下させる。

これらの問題に対処するためには、「世代管理」という概念を導入しなければならない。単なる上書き回避に留まらず、ファイルのライフサイクル全体を見据えた、より高度なファイル管理戦略が必要なのだ。

世代管理とは、特定の最大世代数を設定し、それを超えた場合に最も古い世代を自動で削除するなど、ファイル群を健全な状態に保つ仕組みを指す。今回はまず「上書き回避」に特化するが、将来的な拡張性を視野に入れた設計思想で臨むべきだ。

—

堅牢なファイル世代管理アルゴリズムの設計思想

ここからが本題だ。堅牢なシステムを構築するための設計思想を明確にしよう。

1. 基本方針:明瞭かつ一貫した命名規則

ファイル名に連番を付与する際は、以下の原則を守るべきだ。

  • 基底ファイル名: ユーザーが意図したオリジナルのファイル名。
  • サフィックス: `_revN` の形式を採用する。`N` は連番。
  • ゼロパディング: 連番は `_rev001`, `_rev002` のようにゼロパディングすることで、ファイルエクスプローラー上でのソート順の一貫性を保証し、視認性を高める。最大桁数をあらかじめ決めておくべきだ。例えば3桁なら `_rev999` まで対応できる。

2. 既存世代の効率的な探索ロジック

ファイルシステムへのアクセスは、パフォーマンスボトルネックになりやすい。無駄なアクセスは極力避けるべきだ。

  • ディレクトリ内の関連ファイル特定: `FileSystemObject` (FSO) を用いて、保存しようとしているファイル名とパターンが一致する既存ファイルを効率的に列挙する。
  • 正規表現の活用: `_revN.dwg` のパターンを持つファイルを特定し、その連番 `N` を抽出するには、文字列操作よりも正規表現 (Regular Expression) を使うべきだ。複雑なパターンマッチングを高速かつ堅牢に行える。VBAでは `VBScript.RegExp` オブジェクトが利用できる。
  • 最大連番の特定: 抽出した連番群の中から最大値を見つけ出し、それに1を加えたものが次世代の連番となる。

3. トランザクションとエラーハンドリングの徹底

ファイルI/Oは、予期せぬエラーが発生しやすい領域だ。

  • ディスクフル、アクセス権限、ファイルロック: これらに対するエラーハンドリングは必須だ。
  • AutoCADの安定性: `ThisDrawing.SaveAs` メソッドの実行中にAutoCADがクラッシュした場合、部分的に保存されたファイルやロックされたファイルが残らないよう、慎重な設計が必要となる。
  • ロールバックの可能性: 複雑なファイル操作を行う場合は、万一の失敗に備え、変更を元に戻す(ロールバック)機構も検討すべきだが、今回の「別名保存」シナリオではそこまで必要ないだろう。

4. パフォーマンスへの配慮

特にネットワークドライブ上のファイル操作では、小さな処理の積み重ねが大きな遅延を生む。

  • FSOの活用: ディレクトリ内のファイル列挙は、ワイルドカード検索よりもFSOの `Files` コレクションを直接ループする方が効率的だ。
  • 正規表現のコンパイル: `RegExp` オブジェクトは、パターンを設定する際にコンパイルされる。同じパターンを複数回使う場合は、オブジェクトを再利用することでオーバーヘッドを削減できる。

—

AutoCAD VBAでの実装:実践的なコード例

では、上記の設計思想に基づいた、堅牢なプロダクションコードを見ていこう。このコードは、そのまま諸君の業務ツールに組み込めるように設計されている。

‘————————————————————————————————–
‘ モジュール名: modDrawingSaver
‘ 目的: AutoCAD DWGファイルの安全な保存と世代管理を行う
‘ 作成者: あなたの氏名 / 伝説的なチーフアーキテクト
‘ 最終更新日: 2023-10-27
‘————————————————————————————————–

Option Explicit

‘ VBScript.RegExp オブジェクトを使用するために参照設定が必要です。
‘ VBAエディタ -> ツール -> 参照設定 -> “Microsoft VBScript Regular Expressions 5.5” にチェックを入れてください。

‘————————————————————————————————–
‘ 関数名: SaveDrawingWithRevision
‘ 目的: 現在開いている図面を指定されたディレクトリに、連番を付与して保存します。
‘ 既存ファイルが存在する場合、_rev001, _rev002 のように自動で連番を付与します。
‘ 引数:
‘ TargetDirPath (String): 保存先のディレクトリパス
‘ BaseFileName (String): 保存するファイルのベース名(拡張子なし)。省略すると現在の図面名が使用されます。
‘ OverwriteExisting (Boolean): Trueの場合、連番を付与せず既存ファイルを上書きします。
‘ Falseの場合(推奨)、常に連番を付与して新しい世代として保存します。
‘ ※今回はFalseを前提とした堅牢なロジックを組んでいます。
‘ 戻り値:
‘ Boolean: 保存が成功した場合はTrue、失敗した場合はFalse
‘————————————————————————————————–
Public Function SaveDrawingWithRevision(ByVal TargetDirPath As String, _
Optional ByVal BaseFileName As String = “”, _
Optional ByVal OverwriteExisting As Boolean = False) As Boolean

SaveDrawingWithRevision = False ‘ デフォルトは失敗

Dim acadApp As AcadApplication
Dim acadDoc As AcadDocument
Set acadApp = ThisDrawing.Application
Set acadDoc = ThisDrawing

Dim fso As Object ‘ FileSystemObject
Dim folder As Object ‘ Folder
Dim file As Object ‘ File
Dim regEx As Object ‘ VBScript.RegExp
Dim matches As Object ‘ MatchCollection
Dim currentMaxRev As Long
Dim nextRevNum As Long
Dim newFileName As String
Dim newFullPath As String
Dim originalBaseName As String
Dim dwgExtension As String
Dim pattern As String

‘ ——————————————————————
‘ 1. 初期化と引数チェック
‘ ——————————————————————
If acadDoc Is Nothing Then
Call MsgBox(“現在開いている図面がありません。”, vbCritical, “エラー”)
Exit Function
End If

‘ 保存先のディレクトリパスを正規化
If Right(TargetDirPath, 1) <> “\” Then
TargetDirPath = TargetDirPath & “\”
End If

‘ ベースファイル名が指定されていなければ、現在の図面名から取得
If BaseFileName = “” Then
‘ 拡張子を除いたファイル名を取得
originalBaseName = acadDoc.Name
If InStrRev(originalBaseName, “.”) > 0 Then
originalBaseName = Left(originalBaseName, InStrRev(originalBaseName, “.”) – 1)
End If
Else
originalBaseName = BaseFileName
End If

‘ DWGファイルの拡張子を定義 (大文字小文字は考慮しないため小文字で統一)
dwgExtension = “.dwg”

‘ FileSystemObjectを生成
On Error GoTo ErrorHandler_FSO
Set fso = CreateObject(“Scripting.FileSystemObject”)
On Error GoTo ErrorHandler

‘ 保存先ディレクトリの存在チェック
If Not fso.FolderExists(TargetDirPath) Then
‘ ディレクトリが存在しない場合は作成を試みる
On Error Resume Next ‘ ディレクトリ作成エラーを一時的に無視
fso.CreateFolder TargetDirPath
On Error GoTo ErrorHandler ‘ エラーハンドリングを戻す
If Not fso.FolderExists(TargetDirPath) Then
Call MsgBox(“保存先ディレクトリ ‘” & TargetDirPath & “‘ が存在しないか、作成できませんでした。”, _
vbCritical, “エラー: ディレクトリ不正”)
Exit Function
End If
End If

‘ RegExpオブジェクトを生成し、パターンを設定
Set regEx = CreateObject(“VBScript.RegExp”)
With regEx
.IgnoreCase = True ‘ 大文字小文字を区別しない
.Global = True ‘ 全てのMacthを検索
‘ パターン: (ベース名)_rev(連番).dwg
‘ 例: MyDrawing_rev001.dwg
‘ (ベース名) は正規表現でエスケープが必要な文字 (\ . + ? [ ] ( ) { } | ^ $ /) を考慮
‘ これは厳密には originalBaseName を動的に正規表現パターンに変換する処理が必要だが、
‘ 今回は簡略化のため、ファイル名に特殊文字が含まれないことを前提とする。
‘ 汎用性を高めるには、`RegExp.Escape` のような関数を自作して使うべき。
pattern = “^” & originalBaseName & “_rev(\d{3})” & dwgExtension & “$”
.Pattern = pattern
End With

‘ ——————————————————————
‘ 2. 既存の世代を探し、最大連番を特定
‘ ——————————————————————
currentMaxRev = 0
Set folder = fso.GetFolder(TargetDirPath)

‘ ディレクトリ内のファイルをループして、該当パターンを検索
For Each file In folder.Files
If regEx.Test(file.Name) Then
Set matches = regEx.Execute(file.Name)
If matches.Count > 0 Then
‘ パターンにマッチした連番を抽出
Dim revStr As String
revStr = matches(0).SubMatches(0) ‘ \d{3} にマッチした部分
currentMaxRev = Application.Max(currentMaxRev, CLng(revStr))
End If
End If
Next file

‘ ——————————————————————
‘ 3. 次の連番を決定し、新しいファイル名を生成
‘ ——————————————————————
‘ OverwriteExistingがTrueの場合、連番を付与せずそのまま保存を試みる
If OverwriteExisting Then
newFileName = originalBaseName & dwgExtension
Else
‘ 次の連番を生成 (例: 001, 002, …)
nextRevNum = currentMaxRev + 1
‘ ゼロパディングして3桁の文字列にする
Dim revSuffix As String
revSuffix = Format(nextRevNum, “000”) ‘ 例: 1 -> “001”, 12 -> “012”, 123 -> “123”

newFileName = originalBaseName & “_rev” & revSuffix & dwgExtension
End If

newFullPath = TargetDirPath & newFileName

‘ ——————————————————————
‘ 4. AutoCAD図面の保存実行
‘ ——————————————————————
‘ 既に同じファイル名が存在する場合の最終チェック (OverwriteExisting=Trueの場合のみ意味を持つ)
If OverwriteExisting And fso.FileExists(newFullPath) Then
Dim response As VbMsgBoxResult
response = MsgBox(“ファイル ‘” & newFileName & “‘ は既に存在します。上書きしますか?”, _
vbYesNo + vbExclamation, “ファイル上書き確認”)
If response = vbNo Then
Call MsgBox(“保存をキャンセルしました。”, vbInformation, “キャンセル”)
Exit Function
End If
End If

‘ 保存処理
acadDoc.SaveAs newFullPath

Call MsgBox(“図面を ‘” & newFileName & “‘ として正常に保存しました。”, vbInformation, “保存完了”)
SaveDrawingWithRevision = True

‘ ——————————————————————
‘ 5. 後処理
‘ ——————————————————————
Exit_Function:
Set regEx = Nothing
Set folder = Nothing
Set fso = Nothing
Set acadDoc = Nothing
Set acadApp = Nothing
Exit Function

‘ ——————————————————————
‘ 6. エラーハンドリング
‘ ——————————————————————
ErrorHandler_FSO:
MsgBox “FileSystemObjectの初期化に失敗しました。参照設定 ‘Microsoft Scripting Runtime’ が不足しているか、” & _
“セキュリティ設定に問題がある可能性があります。詳細: ” & Err.Description, vbCritical, “重大なエラー”
Resume Exit_Function

ErrorHandler:
MsgBox “図面の保存中にエラーが発生しました。” & vbCrLf & _
“エラー番号: ” & Err.Number & vbCrLf & _
“説明: ” & Err.Description & vbCrLf & _
“ファイルパス: ” & newFullPath, vbCritical, “エラー: 保存失敗”
Resume Exit_Function

End Function

‘————————————————————————————————–
‘ 使用例: このプロシージャを呼び出すことで、現在の図面が指定されたパスに保存されます。
‘————————————————————————————————–
Public Sub Test_SaveDrawingWithRevision()
Dim targetDir As String
‘ ここに保存したいディレクトリパスを指定してください
targetDir = “C:\AutoCAD_Projects\Revisions”

‘ この行をアンコメントすると、フォルダ作成を自動実行
‘ MkDir targetDir ‘ テスト用: フォルダが存在しない場合のために

If SaveDrawingWithRevision(targetDir) Then
Debug.Print “図面が正常に保存されました。”
Else
Debug.Print “図面の保存に失敗しました。”
End If
End Sub

コードのポイントと解説

1. 参照設定: `VBScript.RegExp` を使用するため、「ツール」→「参照設定」で「Microsoft VBScript Regular Expressions 5.5」にチェックを入れることを忘れないでほしい。これは、VBAが外部のCOMオブジェクトを利用するための重要なステップだ。
2. `FileSystemObject` (FSO): ファイルやディレクトリの操作に特化したオブジェクトだ。VBAの標準機能よりもはるかに強力で、パスの解析、存在確認、ファイル列挙などを効率的に行える。`CreateObject(“Scripting.FileSystemObject”)` でインスタンス化する。
3. 正規表現 `RegExp`: `_rev` の後に続く3桁の数字を抽出するために使用している。

  • `^`:行の先頭
  • `originalBaseName`:ベースファイル名(変数から取得)
  • `_rev`:リビジョンを示す文字列
  • `(\d{3})`:3桁の数字をキャプチャグループとして指定。これにより `matches(0).SubMatches(0)` でこの部分を抽出できる。
  • `dwgExtension`:拡張子
  • `$`:行の末尾
  • これにより、「`MyDrawing_rev001.dwg`」のようなファイル名を正確に識別し、`001` の部分だけを抽出することが可能になる。

4. ゼロパディング: `Format(nextRevNum, “000”)` を使用することで、連番が常に3桁(例: `001`, `010`, `123`)になるように整形している。これにより、ファイルエクスプローラーでのソート順が期待通りになり、視認性が向上する。
5. エラーハンドリング: `On Error GoTo` を用いて、ファイルI/Oの失敗やオブジェクトの生成失敗など、あらゆる例外を捕捉するようにしている。これはプロダクションコードにおいて絶対に必要な処理だ。特にFSOの初期化エラーは、参照設定不足など致命的な原因が考えられるため、専用のハンドラを用意している。
6. `OverwriteExisting` オプション: デフォルトでは `False` にしており、常に新しい世代として保存する安全第一の設計にしている。`True` に設定すると上書き確認ダイアログが表示されるが、この機能は慎重に扱うべきだ。
7. AutoCAD図面の保存: 最終的に `acadDoc.SaveAs newFullPath` を呼び出し、生成された新しいパスで図面を保存する。

データベース連携と拡張性:次なるステージへ

諸君がこの連番付与システムをマスターしたら、次に考えるべきは、メタデータ管理だ。

  • いつ: 保存日時
  • 誰が: 保存ユーザー(AutoCADのログインユーザー名やWindowsのユーザー名)
  • 何を: 変更内容の簡単なメモ

これらの情報を、図面ファイル名だけでなく、別途データベースで管理することで、より強力な世代管理システムを構築できる。

考慮すべきデータベース連携のポイント

1. 図面プロパティへの書き込み: AutoCADの図面自体には「図面プロパティ」というカスタム情報を保存できる領域がある。ここに世代番号、保存者、日時などを書き込むことで、図面単体でも履歴情報を持つことが可能になる。これは最も手軽なデータベース連携と言える。

‘ 図面プロパティに情報を書き込む例
Dim prop As Object
On Error Resume Next ‘ 既存プロパティがない場合にエラー回避
Set prop = acadDoc.GetCustomProperty(“RevisionNumber”)
If prop Is Nothing Then
acadDoc.AddCustomProperty “RevisionNumber”, revSuffix
Else
prop.Value = revSuffix
End If
‘ 他にも保存日時、ユーザー名などを追加
acadDoc.AddCustomProperty “SavedBy”, Environ(“USERNAME”)
acadDoc.AddCustomProperty “SavedDateTime”, Now()
On Error GoTo 0

2. 外部データベース (Access, SQL Server): 大規模なプロジェクトや企業レベルでの運用を考えるなら、Microsoft Access、SQL Server、PostgreSQLなどの外部データベースとの連携を検討すべきだ。

  • 保存時に、ファイルパス、世代番号、保存者、日時、変更コメントなどをデータベースのレコードとして追加する。
  • これにより、膨大な図面ファイルの中から特定の世代を検索したり、変更履歴を一覧表示したりするツールを構築できる。
  • VBAからODBC/ADO経由でデータベースに接続し、SQLクエリを実行することになる。

よくある落とし穴と回避策

どんなに堅牢な設計でも、予期せぬ問題は発生しうる。先回りして対処策を考えておこう。

1. ネットワークドライブ上のファイル操作:

  • 落とし穴: ネットワーク遅延、ファイルロック、アクセス権限の問題により、ファイル操作が失敗しやすい。
  • 回避策: エラーハンドリングを徹底する。再試行ロジックを組み込む(ただし、無限ループにならないように注意)。ユーザーにエラーメッセージを具体的に伝え、対処法を促す。

2. 読み取り専用ファイルの扱い:

  • 落とし穴: 読み取り専用ファイルを開いている場合、`SaveAs` が失敗する可能性がある。
  • 回避策: `acadDoc.GetVariable(“DBMOD”)` や `acadDoc.GetVariable(“DWGTITLED”)` などで図面の状態をチェックする。`fso.GetFile(filePath).Attributes` を使ってファイル属性を確認し、読み取り専用であればユーザーに警告するか、属性を一時的に変更する(ただし、これは慎重に。元の属性に戻す必要がある)。

3. 図面参照 (XREF) のパス更新:

  • 落とし穴: ホスト図面が別名で保存された際、内部のXREFパスが更新されないと、XREFが見つからなくなる。
  • 回避策: `SaveAs` 実行後に、`acadDoc.Blocks` コレクションをループし、XREFであるブロック定義の `Path` プロパティを新しいパスに合わせて更新するロジックを追加する。これは非常に複雑な処理なので、別途専用の関数を構築する必要があるだろう。

4. マルチユーザー環境での競合:

  • 落とし穴: 複数のユーザーが同時に同じ図面を編集し、ほぼ同時に保存しようとした場合、連番が重複したり、意図しない上書きが発生したりする可能性がある。
  • 回避策: ファイルロック機構や、保存直前に再度最大連番を確認するロジック(二重チェック)を導入する。真のマルチユーザー対応には、P&DM (Product & Data Management) システムのような専門ソリューションの導入が望ましい。VBAだけで完璧な競合解決は困難だ。

まとめ:安全な保存は未来への投資

諸君。
本記事で示した「連番付与と世代管理の自動化」は、単なるマクロではない。それは、設計資産を守り、未来の業務効率化の基盤を築くための、戦略的な投資だ。

  • ヒューマンエラーの排除: 自動化により、単純なミスによるデータ損失のリスクを極小化できる。
  • 明確な履歴管理: どの時点の図面が何であるか、一目瞭然となり、トレーサビリティが向上する。
  • 生産性の向上: 保存作業における思考のコストと時間を削減し、諸君をより創造的な活動に集中させる。

今回提供したコードは、実務で直ちに活用できる堅牢な基盤だ。しかし、これに満足してはならない。諸君は、この基盤の上に、図面プロパティへの情報書き込み、外部データベースとの連携、XREFパスの自動更新といった、さらなる拡張性を追求すべきだ。

AutoCAD VBAを掌握する極限の知見とは、単にコードを書くことではない。それは、システム全体を見渡し、潜在的なリスクを予見し、それを回避するための堅牢なアーキテクチャを設計する能力だ。この「安全な保存戦略」が、諸君の業務自動化プロジェクトにおける羅針盤となることを心から願う。

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