【実務・中級編】【初心者】AcadApplication.Visible = False 状態で大量の図面から「属性情報」だけを高速に抜き出すスクレイピング手法 – AutoCAD VBA解析バイブル

スポンサーリンク

【AutoCAD VBA】画面非表示で爆速!数百枚図面から属性情報をスクレイピングする秘技

おい、諸君。君たちは日々、AutoCADを駆使して設計業務に邁進していることだろう。しかし、その一方で、図面データから部品構成表(BOM)を作成したり、属性情報を集計したりする単純作業に追われ、貴重な時間を浪費していないだろうか?

今回のテーマは、そんな君たちの悩みを根本から解決する、AcadApplication.Visible = False を最大限に活用した、図面からの属性情報高速スクレイピング手法だ。AutoCADの描画処理という「重い」処理を極限まで削ぎ落とし、数百枚、いや数千枚の図面からでも、驚くべきスピードで必要な情報を抽出し、君たちの業務効率を劇的に向上させる。

これは単なる小手先のテクニックではない。オブジェクトのライフサイクル、メモリ管理、そしてAPIの深淵を理解した者だけが辿り着ける、AutoCAD VBA活用の「極限」を知るための道標となるだろう。開発プロジェクトのリーダーとして、君たちにその真髄を、ロジカルかつシャープに伝授しよう。

なぜAcadApplication.Visible = False なのか?そのパフォーマンスの重みを知れ!

まず、なぜ `AcadApplication.Visible = False` を使うのか、その理由を明確に理解する必要がある。AutoCADは、画面への描画処理に膨大なリソースを消費する。図形を描画する、画面を更新する、ズームやパンを行う……これら一つ一つの操作が、CPUやGPUに負荷をかけ、処理時間を増大させる。

君たちが普段、手動でAutoCADを操作する際には、この描画処理は不可欠だ。しかし、VBAで自動化を行う場合、特に大量の図面を処理する際には、この描画処理こそがボトルネックとなる。

`AcadApplication.Visible = False` を設定することで、AutoCADのGUI(グラフィカルユーザーインターフェース)を非表示にし、画面描画処理を完全にスキップできる。これは、「目に見えない」ところでAutoCADが動いている状態を意味する。あたかも、バックグラウンドで静かに、しかし力強く処理をこなしてくれる、優秀なアシスタントを雇ったようなものだ。

この設定一つで、処理速度は文字通り桁違いに向上する。数時間かかっていた処理が数分で終わる、そんな劇的な変化を君たちは目の当たりにするだろう。

堅牢な設計:バグを生まないための鉄則

「画面が非表示だと、エラーが発生したときに気づけないのでは?」という懸念を持つかもしれない。その通りだ。しかし、堅牢な設計を心がければ、そのリスクは最小限に抑えられる。

1. エラーハンドリングの徹底

VBAにおけるエラーハンドリングは、もはや「あれば良い」というレベルではない。「必須」である。特にバックグラウンド処理においては、問題が発生してもユーザーには一切通知されないため、意図しない処理の停止や、データの破損に繋がる可能性がある。

On Error GoTo ErrorHandler
‘ ここにメインの処理を記述

Exit Sub ‘ 正常終了時はErrorHandlerをスキップ

ErrorHandler:
‘ エラー発生時の処理
Dim errNum As Long
Dim errText As String
errNum = Err.Number
errText = Err.Description

‘ エラーログの記録(ファイル出力やデータベース登録など)
LogErrorMessage errNum, errText, ThisWorkbook.Path & “\error_log.txt”

‘ ユーザーへの通知(必要に応じて)
MsgBox “エラーが発生しました。詳細はログファイルを確認してください。”, vbCritical

‘ AutoCADオブジェクトの解放を確実に行う
Call CleanUpAutoCAD

‘ プログラムを終了
End

2. オブジェクトのライフサイクル管理:解放を怠るな!

AutoCADのCOMオブジェクトは、使用後に適切に解放しないと、メモリリークや予期せぬ動作を引き起こす。特に `AcadApplication` オブジェクトは、アプリケーションそのものを指すため、その管理は極めて重要だ。

‘ グローバル変数またはモジュールレベル変数として宣言
Dim acadApp As AcadApplication
Dim acadDoc As AcadDocument

‘ 処理開始時にAutoCADオブジェクトを作成
Set acadApp = GetObject(, “AutoCAD.Application”) ‘ 既存のAutoCADインスタンスを取得
‘ または
‘ Set acadApp = CreateObject(“AutoCAD.Application”) ‘ 新規にAutoCADを起動(Visible=Falseなので画面には表示されない)

‘ 処理中にドキュメントを開く
Set acadDoc = acadApp.Documents.Open(filePath)

‘ 処理終了後、またはエラー発生時に必ず解放
Sub CleanUpAutoCAD()
On Error Resume Next ‘ 解放時にエラーが発生しても処理を続行

If Not acadDoc Is Nothing Then
‘ 図面を保存せずに閉じる場合(必要に応じて)
‘ acadDoc.Close (False)
Set acadDoc = Nothing
End If

‘ 実行中のAutoCADプロセスを終了させる場合(注意深く使用する)
‘ If Not acadApp Is Nothing Then
‘ acadApp.Quit
‘ Set acadApp = Nothing
‘ End If

‘ 既存のインスタンスを取得した場合、ここでQuitすると他のユーザーに影響が出る可能性があるため、
‘ 基本的には解放のみ(Set acadApp = Nothing)に留めるのが安全。
If Not acadApp Is Nothing Then
Set acadApp = Nothing
End If

‘ オブジェクトへの参照を削除
On Error GoTo 0
End Sub

`GetObject` を使用して既存のAutoCADインスタンスを取得する場合、それが他のユーザーによって使用されている可能性も考慮する必要がある。`CreateObject` で新規に起動する方が、独立した環境で処理できるため、より安全な場合が多い。ただし、その場合でも `acadApp.Visible = False` の設定は、起動直後に行う必要がある。

高速抽出の核心:属性情報へのダイレクトアクセス

画面描画をスキップしたところで、次に重要なのは、いかに効率的に属性情報へアクセスするかだ。

1. Block Reference と Attribute Objects

AutoCAD図面において、属性情報は通常、「ブロック参照 (Block Reference)」に付随する「属性オブジェクト (Attribute Object)」として格納されている。

2. 図面内の全ブロック参照を走査する

目的の属性情報を持つブロック参照を特定するために、図面内の全てのブロック参照を走査する必要がある。

Sub ExtractAttributeData(filePath As String, ByRef dataList As Collection)
Dim acadApp As AcadApplication
Dim acadDoc As AcadDocument
Dim acadBlockRef As AcadBlockReference
Dim acadAttribute As AcadAttribute
Dim blockName As String
Dim attributeTag As String
Dim attributeValue As String
Dim i As Integer

‘ AutoCADアプリケーションオブジェクトを取得または作成
On Error Resume Next
Set acadApp = GetObject(, “AutoCAD.Application”)
If acadApp Is Nothing Then
Set acadApp = CreateObject(“AutoCAD.Application”)
‘ 起動直後に非表示設定を行う
acadApp.Visible = False
End If
On Error GoTo 0

‘ ファイルを開く
On Error GoTo FileOpenError
Set acadDoc = acadApp.Documents.Open(filePath)
acadDoc.Application.WindowState = acMax ‘ ウィンドウ状態を最大化(Visible=Falseなので影響なし)
On Error GoTo 0

‘ 図面内の全てのオブジェクトを走査
For Each acadObj In acadDoc.ModelSpace
‘ オブジェクトがブロック参照であるか判定
If TypeOf acadObj Is AcadBlockReference Then
Set acadBlockRef = acadObj

‘ ブロック名でフィルタリング(必要に応じて)
‘ If acadBlockRef.Name = “部品表” Then ‘ 例: 特定のブロック名のみを対象とする

‘ ブロック参照に付随する属性オブジェクトを走査
For Each acadAttribute In acadBlockRef.GetAttributes
attributeTag = acadAttribute.TagString ‘ 属性タグ名
attributeValue = acadAttribute.TextString ‘ 属性値

‘ 抽出したい属性タグ名でフィルタリング(例: “部品番号”, “数量” など)
If attributeTag = “部品番号” Or attributeTag = “数量” Then
‘ 抽出したデータをコレクションに追加
‘ ここで、どのブロック参照から抽出したかの情報も保持すると便利
Dim itemData As New Collection
itemData.Add acadBlockRef.Name, “BlockName”
itemData.Add attributeTag, “AttributeTag”
itemData.Add attributeValue, “AttributeValue”
dataList.Add itemData
End If
Next acadAttribute
‘ End If
End If
Next acadObj

‘ 図面を閉じる(保存しない)
acadDoc.Close (False)

GoTo CleanExit

FileOpenError:
‘ ファイルオープン時のエラーハンドリング
LogErrorMessage Err.Number, “Failed to open file: ” & filePath & ” – ” & Err.Description, ThisWorkbook.Path & “\error_log.txt”
If Not acadDoc Is Nothing Then acadDoc.Close (False) ‘ エラー発生時も閉じる試み
GoTo CleanExit ‘ 処理を続行

CleanExit:
‘ AutoCADオブジェクトの解放
‘ 注: ここでacadAppを解放すると、次のファイル処理で再度GetObject/CreateObjectが必要になる
‘ 大量ファイル処理の場合は、ループの外で一度だけ作成・解放するのが効率的
Set acadBlockRef = Nothing
Set acadAttribute = Nothing
Set acadDoc = Nothing
‘ acadAppの解放は、このサブルーチンを呼び出す上位の処理で行う
End Sub

重要な注意点:

  • `acadBlockRef.GetAttributes` は、属性が存在しないブロック参照に対して呼び出すとエラーになる可能性があるため、`On Error Resume Next` で囲むか、事前に属性の存在を確認する処理を入れるべきです。
  • 図面内に存在する全てのオブジェクト(ModelSpaceだけでなく、PaperSpaceも必要に応じて)を走査する必要があるかもしれません。
  • ブロック名や属性タグ名によるフィルタリングは、処理速度を向上させるために不可欠です。

ファイル・データベース連携の注意点

抽出したデータは、最終的に何らかの形で利用されるはずだ。その連携部分で発生しうる落とし穴に注意しよう。

1. ファイル出力(CSV, Excelなど)

  • 文字コード: 日本語環境で作成されたCSVファイルを、異なる文字コードの環境で開くと文字化けすることがあります。VBAでCSV出力する際は、`ADODB.Stream` を使用して明示的に文字コードを指定すると、より堅牢になります。
  • Excelオブジェクトの利用: Excelファイルを直接操作する場合、`Excel.Application` オブジェクトを利用することになります。これもAutoCADと同様に、COMオブジェクトの解放を徹底しないと、Excelプロセスがバックグラウンドで残り続け、リソースを圧迫します。

‘ Excelオブジェクトの解放例
If Not excelApp Is Nothing Then
excelApp.Quit
Set excelApp = Nothing
End If

  • ファイルパスの管理: 長大なファイルパスや、特殊文字を含むファイル名には注意が必要です。

2. データベース連携

  • ADO (ActiveX Data Objects) の利用: データベースへのデータ登録には、ADOを利用するのが一般的です。SQLインジェクションのリスクを避けるため、パラメータクエリを必ず使用しましょう。
  • トランザクション処理: 大量のデータを登録する場合、トランザクション処理を適切に利用することで、データの一貫性を保ち、パフォーマンスを向上させることができます。
  • 接続文字列の管理: データベースへの接続文字列は、コード内に直接埋め込まず、設定ファイルなどで管理することを強く推奨します。

プロダクションコード例:コピペで動く、保守性の高いスクレイピングマクロ

では、これまでの知見を統合し、実務でそのまま使えるような、堅牢かつ保守性の高いVBAコード例を示そう。

‘==============================================================================
‘ ファイル名: AttributeExtractor.xlsm (Excel VBAとして記述)
‘ 概要: 指定されたフォルダ内のAutoCAD図面から属性情報を抽出し、
‘ Excelシートに出力する。AcadApplication.Visible = Falseで高速化。
‘ 作成日: YYYY/MM/DD
‘ 更新日: YYYY/MM/DD
‘ 作成者: [君の名前]
‘==============================================================================

Option Explicit ‘ 変数の宣言を強制する

‘==============================================================================
‘ 定数定義
‘==============================================================================
Const LOG_FILE_NAME As String = “AttributeExtractionLog.txt” ‘ ログファイル名
Const TARGET_ATTRIBUTE_TAGS As String = “部品番号,数量,品名” ‘ 抽出したい属性タグ名をカンマ区切りで指定
Const IGNORE_ERROR_FILES As Boolean = True ‘ エラーが発生したファイルをスキップするかどうか

‘==============================================================================
‘ グローバル変数
‘==============================================================================
Dim acadApp As Object ‘ AcadApplicationオブジェクト (Late BindingでCOMオブジェクトを扱う)
Dim outputSheet As Worksheet ‘ 出力先のExcelシート

‘==============================================================================
‘ メイン処理
‘==============================================================================
Sub ExtractAttributesFromDrawings()

Dim folderPath As String
Dim filePath As String
Dim fileSys As Object ‘ FileSystemObject
Dim folder As Object
Dim file As Object
Dim dataList As Collection ‘ 抽出データを格納するコレクション
Dim startTime As Double, endTime As Double
Dim processedCount As Long, errorCount As Long
Dim attributesToExtract() As String
Dim logFilePath As String

startTime = Timer

‘ — 初期設定 —
Application.ScreenUpdating = False ‘ Excelの画面更新を停止
Application.DisplayAlerts = False ‘ Excelの警告表示を無効化

‘ ログファイルのパスを設定
logFilePath = ThisWorkbook.Path & “\” & LOG_FILE_NAME

‘ 抽出したい属性タグ名を配列に変換
attributesToExtract = Split(TARGET_ATTRIBUTE_TAGS, “,”)

‘ 出力シートの設定
Set outputSheet = ThisWorkbook.Sheets.Add(After:=ThisWorkbook.Sheets(ThisWorkbook.Sheets.Count))
outputSheet.Name = “ExtractedAttributes_” & Format(Now, “yyyymmdd_hhmmss”)
Call SetupOutputSheetHeader

‘ FileSystemObjectを作成
On Error Resume Next
Set fileSys = CreateObject(“Scripting.FileSystemObject”)
If fileSys Is Nothing Then
MsgBox “FileSystemObjectの作成に失敗しました。”, vbCritical
Exit Sub
End If
On Error GoTo 0

‘ フォルダパスの指定(ユーザーに選択させるのが親切)
With Application.FileDialog(msoFileDialogFolderPicker)
.Title = “属性情報を抽出したいAutoCAD図面フォルダを選択してください”
.AllowMultiSelect = False
If .Show <> -1 Then
MsgBox “フォルダが選択されませんでした。”, vbInformation
Exit Sub
End If
folderPath = .SelectedItems(1)
End With

‘ AutoCADアプリケーションオブジェクトの取得または作成
Set acadApp = GetObject(, “AutoCAD.Application”)
If acadApp Is Nothing Then
Set acadApp = CreateObject(“AutoCAD.Application”)
‘ 起動直後に非表示設定を行う
acadApp.Visible = False
‘ AutoSaveを無効化(必要に応じて)
acadApp.AutoSaveEnable = False
End If
‘ AutoCADの画面表示を確実にOFFにする
acadApp.Visible = False

‘ フォルダ内のファイルを走査
Set folder = fileSys.GetFolder(folderPath)
Set dataList = New Collection ‘ 抽出データを格納するコレクションを初期化

For Each file In folder.Files
‘ AutoCAD図面ファイル(.dwg)のみを対象とする
If LCase(fileSys.GetExtensionName(file.Name)) = “dwg” Then
filePath = file.Path
processedCount = processedCount + 1
Debug.Print “Processing: ” & filePath ‘ イミディエイトウィンドウに表示

On Error Resume Next ‘ 個別ファイル処理のエラーハンドリング
Call ExtractAttributesFromFile(filePath, dataList, attributesToExtract)
If Err.Number <> 0 Then
errorCount = errorCount + 1
Call LogError(“Error processing file: ” & filePath, Err.Number, Err.Description, logFilePath)
If Not IGNORE_ERROR_FILES Then
‘ エラーを上位に伝播させて処理を停止する場合
Err.Raise Err.Number, Err.Source, Err.Description
End If
End If
On Error GoTo 0 ‘ エラーハンドリングをリセット
End If
Next file

‘ — 結果の出力 —
If dataList.Count > 0 Then
Call OutputDataToSheet(dataList)
MsgBox dataList.Count & ” 件の属性情報を抽出しました。”, vbInformation
Else
MsgBox “抽出できる属性情報が見つかりませんでした。”, vbInformation
End If

‘ — クリーンアップ —
Call CleanUpAutoCAD ‘ AutoCADオブジェクトの解放
Set fileSys = Nothing
Set folder = Nothing
Set file = Nothing
Set dataList = Nothing
Set outputSheet = Nothing

endTime = Timer
MsgBox “処理が完了しました。” & vbCrLf & _
“処理ファイル数: ” & processedCount & vbCrLf & _
“エラーファイル数: ” & errorCount & vbCrLf & _
“処理時間: ” & Format(endTime – startTime, “0.00”) & ” 秒”, vbInformation

Application.ScreenUpdating = True ‘ Excelの画面更新を再開
Application.DisplayAlerts = True ‘ Excelの警告表示を再開

End Sub

‘==============================================================================
‘ サブルーチン: 指定されたAutoCAD図面ファイルから属性を抽出する
‘==============================================================================
Sub ExtractAttributesFromFile(filePath As String, ByRef dataList As Collection, attributesToExtract() As Variant)

Dim acadDoc As Object ‘ AcadDocumentオブジェクト
Dim acadObj As Object ‘ 図面内のオブジェクト
Dim acadBlockRef As Object ‘ AcadBlockReferenceオブジェクト
Dim acadAttribute As Object ‘ AcadAttributeオブジェクト
Dim attributeTag As String
Dim attributeValue As String
Dim blockName As String
Dim i As Integer

‘ 図面を開く
Set acadDoc = acadApp.Documents.Open(filePath)
‘ AutoCADのウィンドウ状態を最大化(Visible=Falseなので画面には影響しないが、内部的な挙動に影響する可能性)
acadApp.WindowState = acMax

‘ ModelSpaceのオブジェクトを走査
For Each acadObj In acadDoc.ModelSpace
If TypeOf acadObj Is AcadBlockReference Then
Set acadBlockRef = acadObj

‘ Block Referenceに属性が存在するか確認
If acadBlockRef.HasAttributes Then
‘ 属性オブジェクトを走査
For Each acadAttribute In acadBlockRef.GetAttributes
attributeTag = acadAttribute.TagString
attributeValue = acadAttribute.TextString
blockName = acadBlockRef.Name

‘ 抽出対象の属性タグ名かを判定
For i = LBound(attributesToExtract) To UBound(attributesToExtract)
If Trim(attributesToExtract(i)) = attributeTag Then
‘ 抽出データをコレクションに追加
Dim itemData As New Collection
itemData.Add blockName, “BlockName”
itemData.Add attributeTag, “AttributeTag”
itemData.Add attributeValue, “AttributeValue”
itemData.Add file.Name, “FileName” ‘ ファイル名も追加
dataList.Add itemData
Exit For ‘ 一致したら次の属性へ
End If
Next i
Next acadAttribute
End If
End If
Next acadObj

‘ PaperSpaceのオブジェクトも必要に応じて走査
‘ For Each acadObj In acadDoc.PaperSpace
‘ ‘ 同様の処理
‘ Next acadObj

‘ 図面を保存せずに閉じる
acadDoc.Close (False)

Set acadBlockRef = Nothing
Set acadAttribute = Nothing
Set acadObj = Nothing
Set acadDoc = Nothing

End Sub

‘==============================================================================
‘ サブルーチン: 出力シートのヘッダーを設定する
‘==============================================================================
Sub SetupOutputSheetHeader()
outputSheet.Cells(1, 1).Value = “FileName”
outputSheet.Cells(1, 2).Value = “BlockName”
outputSheet.Cells(1, 3).Value = “AttributeTag”
outputSheet.Cells(1, 4).Value = “AttributeValue”
outputSheet.Range(“A1:D1”).Font.Bold = True
outputSheet.Columns(“A:D”).AutoFit
End Sub

‘==============================================================================
‘ サブルーチン: 抽出データをExcelシートに出力する
‘==============================================================================
Sub OutputDataToSheet(dataList As Collection)
Dim i As Long
Dim rowNum As Long
Dim itemData As Collection

rowNum = 1 ‘ ヘッダー行から開始

For i = 1 To dataList.Count
Set itemData = dataList(i)
rowNum = rowNum + 1

outputSheet.Cells(rowNum, 1).Value = itemData(“FileName”)
outputSheet.Cells(rowNum, 2).Value = itemData(“BlockName”)
outputSheet.Cells(rowNum, 3).Value = itemData(“AttributeTag”)
outputSheet.Cells(rowNum, 4).Value = itemData(“AttributeValue”)
Next i

outputSheet.Columns(“A:D”).AutoFit
End Sub

‘==============================================================================
‘ サブルーチン: AutoCADオブジェクトを解放する
‘==============================================================================
Sub CleanUpAutoCAD()
On Error Resume Next ‘ 解放時にエラーが発生しても処理を続行

If Not acadApp Is Nothing Then
‘ 実行中のAutoCADプロセスを終了させる(注意深く使用)
‘ acadApp.Quit
Set acadApp = Nothing ‘ オブジェクトへの参照を削除
End If

On Error GoTo 0
End Sub

‘==============================================================================
‘ サブルーチン: エラーログをファイルに記録する
‘==============================================================================
Sub LogError(errorMessage As String, errNum As Long, errDesc As String, logFilePath As String)
Dim ts As Object ‘ TextStream
Dim fileSys As Object

Set fileSys = CreateObject(“Scripting.FileSystemObject”)
On Error Resume Next
Set ts = fileSys.OpenTextFile(logFilePath, 8, True) ‘ 追記モードで開く、ファイルがなければ新規作成
If Err.Number <> 0 Then
MsgBox “エラーログファイルへの書き込みに失敗しました: ” & logFilePath, vbCritical
Set ts = Nothing
Set fileSys = Nothing
Exit Sub
End If
On Error GoTo 0

ts.WriteLine Format(Now, “yyyy/mm/dd hh:nn:ss”) & ” – ” & errorMessage
ts.WriteLine “Error Number: ” & errNum
ts.WriteLine “Error Description: ” & errDesc
ts.WriteLine “—————————————-”
ts.Close

Set ts = Nothing
Set fileSys = Nothing
End Sub

このコードのポイント:

  • Late Binding: `Dim acadApp As Object` のように、オブジェクト型を `Object` とすることで、AutoCADがインストールされていない環境でもコードのコンパイルエラーを防ぎます。実行時にCOMオブジェクトが生成されるため、より柔軟な対応が可能です。
  • エラーハンドリング: ファイルオープン時、個別ファイル処理時、ログ記録時など、随所でエラーハンドリングを実装しています。`IGNORE_ERROR_FILES` 定数で、エラー発生時の挙動(スキップするか、処理を停止するか)を制御できます。
  • ログ出力: エラー発生時には、詳細な情報をテキストファイルに記録します。これにより、問題発生時の原因究明が容易になります。
  • 出力シートの動的作成: 実行ごとに新しいシートに結果を出力するため、過去のデータと混ざる心配がありません。
  • 属性タグ名のカスタマイズ: `TARGET_ATTRIBUTE_TAGS` 定数で、抽出したい属性タグ名を簡単に指定できます。
  • FileSystemObjectの活用: フォルダ内のファイル操作を効率的に行います。
  • オブジェクト解放の徹底: `CleanUpAutoCAD` サブルーチンで、AutoCADオブジェクトの解放を確実に行います。
  • コメント: 各処理ブロックに丁寧なコメントを付記し、コードの可読性と保守性を高めています。

さあ、君たちの手で「自動化」を形にせよ!

`AcadApplication.Visible = False` という一見シンプルな設定が、AutoCAD VBAにおけるパフォーマンスチューニングの鍵を握っていることを理解してもらえただろうか。

今回紹介した手法は、単に図面から属性を抽出するだけでなく、君たちの業務における「無駄」を徹底的に排除し、より創造的で価値の高い業務に集中するための強力な武器となる。

重要なのは、このコードを「コピペして終わり」ではなく、その背後にある「なぜそう書くのか」というロジックを理解し、君自身のプロジェクトに合わせてカスタマイズしていくことだ。

オブジェクトのライフサイクル、エラーハンドリング、そしてAPIの挙動を深く理解することで、君たちはバグに悩まされることなく、堅牢で高速な自動化ツールを開発できるようになる。

さあ、この秘技をマスターし、君たちのAutoCAD VBA開発スキルを次のレベルへと引き上げ、業務効率化の旗手となれ!

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