【実務・中級編】【上級プロフェッショナル向け】バイナリ解析による「DWG保存バージョン」の高速特定:AutoCAD非起動での互換性チェック – AutoCAD VBA解析バイブル

スポンサーリンク
スポンサーリンク

【極限の最適化】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の挙動を理解し、「いかにリソースを消費せずに目的を達成するか」を常に問い続けてほしい。

バイナリ解析というアプローチは、その思考の第一歩となるはずだ。

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