【極限の最適化】AutoCADを起動させない:バイナリ解析によるDWGバージョン高速特定術
業務自動化の現場において、多くのエンジニアが陥る「致命的な設計ミス」がある。それは、「ファイルの状態を知るために、そのファイルを開こうとする」ことだ。
例えば、数千個のDWGファイルが格納されたサーバーから、特定のバージョン(例:AutoCAD 2018形式以降)のみを抽出したいとする。このとき、愚直に `Application.Documents.Open` や `ObjectDBX` を使用してファイルを開いていく設計を採用しているとしたら、そのツールは「使い物にならない」と言わざるを得ない。
AutoCADのエンジンを起動し、図面データをメモリにロードするプロセスには、膨大なオーバーヘッド(CPU、メモリ、I/O待ち)が伴う。数千ファイルに対してこれを行うのは、針の穴に糸を通す作業を、毎回重機を使って行っているようなものだ。
本記事では、プロフェッショナルが実践すべき「バイナリ解析による、AutoCAD非起動でのDWGバージョン高速特定」の技術を伝授する。
1. 理論:DWGファイルの「指紋」を読み解く
DWGファイルは、その物理的な構造の先頭数バイトに、ファイル形式を定義する「マジックナンバー(Signature)」を持っている。
具体的には、ファイルの先頭に `AC10xx` という形式の文字列(ヘッダ)が存在する。この `xx` の部分が、AutoCADのバージョンを決定づけている。
| ヘッダ文字列 | 対応するAutoCADバージョン |
|---|---|
| AC1015 | AutoCAD 2000 |
| AC1018 | AutoCAD 2004 |
| AC1021 | AutoCAD 2007 |
| AC1024 | AutoCAD 2010 |
| AC1027 | AutoCAD 2013 |
| AC1032 | AutoCAD 2018 |
我々がすべきことは、図面全体をロードすることではない。ファイルの先頭からわずか数バイトを「覗き見」するだけでいい。これだけで、処理速度は数千倍、あるいはそれ以上に跳ね上がる。
2. 設計思想:堅牢なバイナリ・リーダーの実装
単に `Open` 文で読み込むだけでは、プロのコードとは呼べない。実務では、以下の「例外」を考慮した設計が不可欠だ。
1. ファイルアクセスの競合: 他のユーザーやプロセスがファイルを開いている場合、エラーで停止する。
2. 破損ファイル: ファイルサイズが極端に小さい、あるいは中身が空の場合のハンドリング。
3. 非DWGファイル: 拡張子が `.dwg` でも、中身がテキストや別の形式である可能性。
これらを回避するために、`Binary Access` モードを用いた、低レイヤーな読み込みロジックを構築する。
3. プロダクション・コード:高速バージョン判別エンジン
以下に、そのまま実務に投入可能な、保守性の高いVBAクラスモジュール相当のコードを示す。
Option Explicit
' ==============================================================================
' Module: dwg_inspector
' Description: AutoCADを起動せずにDWGのバージョンを高速特定する
' Author: Chief Architect
' ==============================================================================
' バージョン情報を格納する列挙型(拡張性を考慮)
Public Enum DwgVersion
dwgUnknown = 0
dwg2000 ' AC1015
dwg2004 ' AC1018
dwg2007 ' AC1021
dwg2010 ' AC1024
dwg2013 ' AC1027
dwg2018 ' AC1032
End Enum
''' <summary>
''' 指定されたパスのDWGファイルのバージョンを高速に判別する
''' </summary>
''' <param name="filePath">DWGファイルのフルパス</param>
''' <returns>DwgVersion 列挙型</returns>
Public Function GetDwgVersionFast(ByVal filePath As String) As DwgVersion
Dim fileNum As Integer
Dim header As String
Dim versionCode As String
' 初期値はUnknown
GetDwgVersionFast = dwgUnknown
' 1. ファイルの存在確認
If Dir(filePath) = "" Then
Debug.Print "Error: File not found -> " & filePath
Exit Function
End If
On Error GoTo ErrorHandler
' 2. バイナリモードでファイルを開く
' Access Read を指定することで、書き込み権限がなくても読み取り可能にする
fileNum = FreeFile
Open filePath For Binary Access Read As #fileNum
' 3. 先頭6バイトを読み取る (AC10xx)
' Get コマンドを使用して、ファイルポインタの先頭から直接データを取得
header = Space$(6)
Get #fileNum, 1, header
Close #fileNum
' 4. ヘッダの解析
versionCode = Trim(header)
Select Case versionCode
Case "AC1015": GetDwgVersionFast = dwg2000
Case "AC1018": GetDwgVersionFast = dwg2004
Case "AC1021": GetDwgVersionFast = dwg2007
Case "AC1024": GetDwgVersionFast = dwg2010
Case "AC1027": GetDwgVersionFast = dwg2013
Case "AC1032": GetDwgVersionFast = dwg2018
Case Else
' 想定外のヘッダ、または非DWGファイル
GetDwgVersionFast = dwgUnknown
End Select
Exit Function
ErrorHandler:
' ファイルがロックされている場合や、アクセス権限がない場合の処理
If fileNum > 0 Then Close #fileNum
Debug.Print "Error accessing file: " & filePath & " | Error: " & Err.Description
GetDwgVersionFast = dwgUnknown
End Function
''' <summary>
''' 列挙型を人間が読める文字列に変換する
''' </summary>
Public Function ConvertVersionToString(ByVal ver As DwgVersion) As String
Select Case ver
Case dwg2000: ConvertVersionToString = "AutoCAD 2000"
Case dwg2004: ConvertVersionToString = "AutoCAD 2004"
Case dwg2007: ConvertVersionToString = "AutoCAD 2007"
Case dwg2010: ConvertVersionToString = "AutoCAD 2010"
Case dwg2013: ConvertVersionToString = "AutoCAD 2013"
Case dwg2018: ConvertVersionToString = "AutoCAD 2018"
Case Else: ConvertVersionToString = "Unknown/Corrupted"
End Select
End Function
実装のポイント解説
1. `Binary Access Read` の採用:
通常の `Input` や `Sequential` モードではなく、`Binary` モードを使用。これにより、OSのファイルシステムに対して「特定のバイト位置から、特定のバイト数だけを読み取る」という極めて低レイヤーな命令が可能になる。また、`Access Read` を指定することで、他者が開いているファイルに対しても読み取りを試行できる(共有モードの活用)。
2. `Space$(6)` によるメモリ確保:
読み込み先の変数に事前に適切なサイズを確保しておくのは、メモリ管理における鉄則だ。
3. `Enum`(列挙型)による型安全性:
戻り値を単なる `String` や `Integer` にせず、`Enum` を定義することで、呼び出し側のコードにおけるマジックナンバーを排除し、保守性を飛躍的に高めている。
4. 実務への応用:データベース連携とスケーラビリティ
この関数を単体で使うだけでは、エンジニアの仕事は終わらない。
大量のファイルをスキャンする場合、結果をExcelシートに書き出すのではなく、「結果を構造体やコレクションに格納し、一括でデータベース(SQL ServerやAccess)へバルクインサートする」という設計を目指すべきだ。
' 実務的なループ処理のイメージ
Sub BatchProcessDwgFiles(folderPath As String)
Dim fileName As String
Dim fullPath As String
Dim ver As DwgVersion
fileName = Dir(folderPath & "\*.dwg")
Do While fileName <> ""
fullPath = folderPath & "\" & fileName
ver = GetDwgVersionFast(fullPath)
' ここでDBへの書き込み、あるいはログへの記録を行う
' シートへの書き込みは、ループ内で行うと遅いため、配列に溜めて最後に一括出力するのが鉄則
Debug.Print fileName & " -> " & ConvertVersionToString(ver)
fileName = Dir()
Loop
End Sub
結論:エンジニアに求められる「視点」
「動くコード」を書くことは誰にでもできる。しかし、「効率的で、堅牢で、スケーラブルなコード」を書ける者は一握りだ。
AutoCAD VBAの世界においても、APIが提供する便利なメソッド(`Documents.Open`)をそのまま使うのではなく、その裏側にあるファイル構造やOSの挙動を理解し、「いかにリソースを消費せずに目的を達成するか」を常に問い続けてほしい。
バイナリ解析というアプローチは、その思考の第一歩となるはずだ。
