なぜ「Documents.Openの例外処理」では二流なのか
AutoCAD VBAでバッチ処理ツールや図面インフラを構築した経験があるなら、一度は次の洗礼を受けているはずだ。
「外部から受領した図面を開こうとした瞬間、実行時エラーが発生してマクロが止まる」
あるいは最悪の場合、AutoCADが内部エラーを吐いて沈黙し、ユーザーには不親切なエラーダイアログが残される。
多くの現場では、次のような「お粗末な」コードが量産されている。
‘ 二流の典型例:開いてみてコケたらエラーを握りつぶす
On Error Resume Next
Set doc = AcadApplication.Documents.Open(filePath)
If Err.Number <> 0 Then
‘ エラー処理…
End If
On Error GoTo 0
なぜこの書き方が失格なのか。アーキテクトの視点から言えば、`Documents.Open` は「重厚なCOMオブジェクトのライフサイクルを開始するトリガー」だからだ。
上位バージョン(自環境のAutoCAD内部データベースが解釈不能なフォーマット)のDWGを`Open`メソッドに渡すと、AutoCADは内部でCOM例外や`eInvalidDwgVersion`を発生させる。このとき、内部メモリの確保中途半端な解放、ObjectARXレイヤーの不安定化、さらにはGUIモード時のモーダルダイアログの割り込みなど、バッチ処理を破綻させるあらゆる地雷が炸裂する。
一流のエンジニアは「開けないファイルは、開く前に弾く(あるいは変換する)」。
AutoCADエンジンにファイルを渡す前に、ファイルシステムのレイヤーでファイルヘッダー(バイナリ)を数バイト読み取るだけで、その図面が自環境で安全に展開できるかを瞬時に判定できる。
今回は、DWGの内部仕様に基づいたヘッダー判定ロジックと、将来バージョンを検知した際にシームレスにAutoCAD外部のコンバーターを駆動して自動回避する極限の設計パターンを公開する。
—
DWGバイナリヘッダーの深層構造とバージョンシグネチャ
DWGファイルはオートデスク独自のバイナリフォーマットだが、先頭6バイトはプレーンテキストのアスキー文字列(Magic Number)として固定されている。
具体的には、先頭6文字が常に `AC10xx` という形式で書き込まれている。この末尾の数値が、内部データベースのフォーマットリビジョンを示している。
DWGファイルバージョン対照表
| ヘッダー文字列 | 内部コード | 正式バージョン | 備考 |
| :— | :— | :— | :— |
| `AC1015` | 2000 | AutoCAD 2000 / 2000i / 2002 | |
| `AC1018` | 2004 | AutoCAD 2004 / 2005 / 2006 | |
| `AC1021` | 2007 | AutoCAD 2007 / 2008 / 2009 | |
| `AC1024` | 2010 | AutoCAD 2010 / 2011 / 2012 | |
| `AC1027` | 2013 | AutoCAD 2013 / 2014 / 2015 / 2016 / 2017 | |
| `AC1032` | 2018 | AutoCAD 2018 / 2019 / … / 2024 / 2025+ | 現行の共通最新形式 |
ここで重要なのは、AutoCADは自環境のバージョンより「大きい内部コード」を持つファイルを開くことができないという点だ(例えば、AutoCAD 2016[上限AC1027]は、AC1032形式のファイルを開けない)。
我々が実装すべきは、AutoCAD本体のCOMオブジェクトを一切介さず、VBA標準のローレベルファイルI/Oを用いて先頭6バイトだけを瞬時に吸い出し、実行環境の対応上限と比較するロジックである。
—
【実装】DWGヘッダー検知エンジン(DwgVersionChecker)
保守性を極限まで高めるため、クラスモジュール(名前:`DwgVersionChecker`)として設計する。ファイルI/Oの共有違反(他プロセスによる排他ロック)やファイルサイズゼロの不正ファイルも考慮し、一切のクラッシュを排除した堅牢な実装だ。
クラスモジュール:`DwgVersionChecker.cls`
VERSION 1.0 CLASS
BEGIN
MultiUse = -1 ‘True
END
Attribute VB_Name = “DwgVersionChecker”
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = True
Attribute VB_PredeclaredId = False
Attribute VB_Exposed = False
Option Explicit
‘ ==============================================================================
‘ クラス名: DwgVersionChecker
‘ 責務: DWGバイナリヘッダーの解析および実行環境互換性の高速判定
‘ ==============================================================================
Public Enum DwgCompatibility
Compat_OK = 0 ‘ 自環境で安全にオープン可能
Compat_FutureVersion = 1 ‘ 自環境より新しい(オープン不可)
Compat_InvalidFormat = 2 ‘ DWG形式ではない、または破損
Compat_FileAccessError = 3 ‘ ファイル不在、ロック中、アクセス権限不足
End Enum
Private m_HostDwgCode As Long
Private Sub Class_Initialize()
‘ ホストAutoCADのメジャーバージョンから最大サポートDWGコードを算出
m_HostDwgCode = DetermineHostMaxDwgCode()
End Sub
”’
”’
Private Function DetermineHostMaxDwgCode() As Long
Dim acadVer As String
Dim majorVer As Double
‘ 例: “24.2s (LMS Tech)” -> 24.2
acadVer = ThisDrawing.Application.Version
majorVer = Val(acadVer)
‘ AutoCAD内部バージョンとDWGフォーマットのマッピング
‘ AutoCAD 2018以降 (Ver 22.0以上) は AC1032 (コード1032)
If majorVer >= 22.0 Then
DetermineHostMaxDwgCode = 1032
ElseIf majorVer >= 19.0 Then ‘ AutoCAD 2013-2017 (Ver 19.0 – 21.0)
DetermineHostMaxDwgCode = 1027
ElseIf majorVer >= 18.0 Then ‘ AutoCAD 2010-2012 (Ver 18.0 – 18.2)
DetermineHostMaxDwgCode = 1024
ElseIf majorVer >= 17.0 Then ‘ AutoCAD 2007-2009
DetermineHostMaxDwgCode = 1021
Else
DetermineHostMaxDwgCode = 1018 ‘ 2004以前
End If
End Function
”’
”’
Public Function CheckCompatibility(ByVal filePath As String, ByRef outHeader As String) As DwgCompatibility
Dim fileNum As Integer
Dim headerBytes(0 To 5) As Byte
Dim headerStr As String
Dim targetCode As Long
outHeader = “”
‘ 1. ファイルの存在およびサイズチェック (最低6バイト以上)
If Dir(filePath, vbNormal Or vbReadOnly Or vbHidden) = “” Then
CheckCompatibility = Compat_FileAccessError
Exit Function
End If
On Error GoTo FileAccessErr
If FileLen(filePath) < 6 Then
CheckCompatibility = Compat_InvalidFormat
Exit Function
End If
' 2. バイナリモードで先頭6バイトのみをピンポイント読込(リソース消費ゼロ)
fileNum = FreeFile
Open filePath For Binary Access Read Shared As #fileNum
Get #fileNum, 1, headerBytes
Close #fileNum
On Error GoTo 0
' 3. アスキー文字列へ変換
headerStr = StrConv(headerBytes, vbUnicode)
outHeader = headerStr
' 4. Magic String "AC10" の正当性判定
If Left$(headerStr, 4) <> “AC10” Then
CheckCompatibility = Compat_InvalidFormat
Exit Function
End If
‘ 5. DWG内部コードの数値比較
targetCode = Val(Mid$(headerStr, 3, 4))
If targetCode > m_HostDwgCode Then
CheckCompatibility = Compat_FutureVersion
Else
CheckCompatibility = Compat_OK
End If
Exit Function
FileAccessErr:
If fileNum <> 0 Then Close #fileNum
CheckCompatibility = Compat_FileAccessError
End Function
—
自動回避策:DWGコンバーター呼び出しによるパイプライン統合
上位バージョンと判明した際、エンドユーザーに「手動で保存し直してください」と要求するのは自動化システムとして敗北を意味する。
オートデスクが公式に無償配布している「Autodesk DWG TrueView」には、CUIから一括変換を実行できる強力なツール(`DWGVIEWR.exe` およびDWG Convert機能)が備わっている。あるいは、実務ではオープンソースのCLIコンバーター(ODA File Converterなど)をバックグラウンドで召喚するのがスマートだ。
ここでは、上位バージョンを検知した場合に、安全に外部コンバーターをキックして「自環境が読めるバージョンへとダウングレードさせた一時ファイル」を生成、それをAutoCADに渡す堅牢なワークフローを提示する。
標準モジュール:`Mod_BatchProcessor.bas`
Option Explicit
‘ ==============================================================================
‘ モジュール名: Mod_BatchProcessor
‘ 責務: 互換性チェックから透過的コンバート・オープンまでの統合オーケストレーション
‘ ==============================================================================
Public Sub SafeOpenDrawingWorkflow(ByVal targetFilePath As String)
Dim checker As New DwgVersionChecker
Dim result As DwgCompatibility
Dim header As String
Dim openedDoc As AcadDocument
Dim fileToOpen As String
fileToOpen = targetFilePath
result = checker.CheckCompatibility(targetFilePath, header)
Select Case result
Case Compat_OK
‘ 互換性あり:そのままオープン
Debug.Print “[OK] 互換性確認済 (” & header & “): ” & targetFilePath
Case Compat_FutureVersion
‘ 将来バージョン検知:コンバーターによるフォールバック発動
Debug.Print “[WARN] 未サポートの将来バージョンを検知: ” & header
Dim convertedPath As String
convertedPath = ExecuteExternalConversion(targetFilePath)
If convertedPath = “” Then
MsgBox “上位バージョンの図面 (” & header & “) ですが、” & vbCrLf & _
“外部コンバーターによる自動変換に失敗しました。” & vbCrLf & _
“処理を中断します。”, vbCritical, “バージョン不整合”
Exit Sub
End If
fileToOpen = convertedPath
Case Compat_InvalidFormat
MsgBox “ファイルが破損しているか、有効なDWG形式ではありません。”, vbCritical, “エラー”
Exit Sub
Case Compat_FileAccessError
MsgBox “ファイルにアクセスできません。他のプロセスで開かれている可能性があります。”, vbExclamation, “I/Oエラー”
Exit Sub
End Select
‘ 安全が証明されたファイルのみをCOMレイヤーへ渡す
Set openedDoc = AcadApplication.Documents.Open(fileToOpen)
‘ 目的の業務処理を実行…
ProcessDocument openedDoc
End Sub
”’
”’
Private Function ExecuteExternalConversion(ByVal sourceDwgPath As String) As String
Dim wsh As Object
Dim fso As Object
Dim tempOutputDir As String
Dim exePath As String
Dim cmdLine As String
Dim exitCode As Long
Set fso = CreateObject(“Scripting.FileSystemObject”)
Set wsh = CreateObject(“WScript.Shell”)
‘ 一時出力ディレクトリの準備
tempOutputDir = fso.GetSpecialFolder(2) & “\DwgDowngrade_” & Format(Now, “yyyymmddhhnnss”)
fso.CreateFolder tempOutputDir
‘ ODA File Converter または社内配置のDWG変換エンジンのパス
‘ 引数仕様: [入力Dir] [出力Dir] [出力バージョン] [形式] [再帰フラグ] [監査フラグ]
exePath = “C:\Program Files\ODA\ODAFileConverter\ODAFileConverter.exe”
If Not fso.FileExists(exePath) Then
‘ コンバーター未インストールの場合は空文字を返して中断
ExecuteExternalConversion = “”
Exit Function
End If
Dim sourceDir As String
Dim fileName As String
sourceDir = fso.GetParentFolderName(sourceDwgPath)
fileName = fso.GetFileName(sourceDwgPath)
‘ コマンドライン文字列の組み立て(同期実行:ウィンドウは非表示)
‘ ※ 出力バージョン: “ACAD2018” や “ACAD2013” など自環境の対応形式
cmdLine = “””” & exePath & “”” “”” & sourceDir & “”” “”” & tempOutputDir & “”” ACAD2013 DWG 0 0 ” & “””” & fileName & “”””
‘ 実行と待機(Runメソッドの第3引数=Trueで同期完了を待つ)
exitCode = wsh.Run(cmdLine, 0, True)
Dim generatedFilePath As String
generatedFilePath = tempOutputDir & “\” & fileName
If exitCode = 0 And fso.FileExists(generatedFilePath) Then
ExecuteExternalConversion = generatedFilePath
Else
ExecuteExternalConversion = “”
End If
End Function
Private Sub ProcessDocument(ByVal doc As AcadDocument)
‘ 実際の図面操作ロジック
Debug.Print “図面処理中: ” & doc.Name
End Sub
—
アーキテクトが教える現場運用の急所
このパイプラインを本番運用に乗せる際、必ず考慮すべき落とし穴が3点存在する。
1. ネットワークレイテンシと共有違反
巨大なDWG(数十MB以上)がNAS上に存在する場合、VBAのバイナリ読み込みを安易に同期で行うとネットワーク遅延に巻き込まれるリスクがある。だが、今回提示したコードは`Get #fileNum, 1, headerBytes`で「先頭6バイト」のみをターゲットにシークして即時クローズしている。ギガバイト級の図面であっても、数ミリ秒でヘッダー取得は完了する。このパフォーマンスの軽さこそがバイナリ直読の最大の武器だ。
2. コンバート済み一時ファイルのライフサイクル管理
外部コンバーターを介して生成したダウングレード図面は、バッチ処理完了後にAutoCADがドキュメントを閉じた段階で確実に破棄(`Kill`または`fso.DeleteFolder`)しなければならない。一時フォルダの肥大化はディスク容量の圧迫だけでなく、同名ファイルの衝突バグを生む温床となる。
3. 一時保存時のオブジェクトの喪失(プロキシオブジェクト)
上位バージョンから下位バージョンへDWGをダウングレードした際、新バージョン特有の最新オブジェクト(新機能の寸法スタイル、マルチ引出線の新機能、Civil 3DやPlant 3DのAECObject)は「プロキシオブジェクト(Proxy Object)」へと降格される。
このファイルを開いて保存し直すと、元々の拡張データが欠落するリスクがある。したがって、ダウングレードして読み込む運用は「読み取り専用(ReadOnly)でのデータ抽出やPDF一括出力」に限定し、マスターDWGへの無思慮な上書き保存は禁止するアーキテクチャルールを策定しておくべきだ。
—
結論:守りの設計が自動化の品格を決める
AutoCAD VBAは手軽に導入できる反面、エラーハンドリングが杜撰になりやすい技術スタックである。受領したファイルが「常に自環境で開ける」という前提で書かれたバッチ処理は、いずれ業務の現場で大規模な停止事故を引き起こす。
1. ファイルを開く前にバイナリシグネチャを検知する
2. CADエンジン内部を汚染させない
3. 将来バージョンを検知した場合は外部プロセスで安全に回避する
この防御的プログラミングの原則を実装に落とし込んでこそ、何万枚の図面処理でも途中で止まらない「真のエンタープライズ・オートメーション」が完成する。ツールを作る側から、システムを設計する側へ。コードの一行一行にアーキテクチャの意思を宿らせよう。
