AutoCAD VBAを掌握する極限の知見:`GetDistance`のキーワード入力併用術で“標準コマンドのUX”を実装する
AutoCAD VBAにおいて、ユーザーとの対話インターフェースの質は、そのままツールの「実用性」と「評価」に直結する。
あなたが作ったマクロを起動したとき、ユーザーがいちいちプロンプトに従って数値を打ち込むだけの味気ない挙動になっていないだろうか?
「ちょっと設定を変えたいから一旦キャンセルして…」とユーザーに思わせた時点で、そのツールはUX(ユーザー体験)の観点から失格である。
AutoCADの標準コマンド(`LINE`や`CIRCLE`など)を思い出してほしい。コマンド実行中に `S` と叩けば設定画面が開き、数値を入れればそのまま処理が続行される。あの滑らかな操作感を、我々が書くVBAの `AcadDocument.Utility` でも完全に再現できるとしたらどうだろうか?
今回は、`AcadDocument.Utility.GetDistance` メソッドの真のポテンシャルを引き出し、「数値入力」と「キーワード入力(オプション分岐)」をシームレスに共存させる極限のテクニックを授与する。
—
1. なぜ「普通の `GetDistance`」では実務で通用しないのか?
多くの初級~中級プログラマーは、`GetDistance` を以下のように単体で呼び出して満足している。
‘ 【アンチパターン】これでは実務で使い物にならない
Dim retDist As Double
retDist = ThisDrawing.Utility.GetDistance(, “2点間の距離を指定してください: “)
このコードの何が問題か?
ユーザーが「あ、やっぱりスナップ設定やオフセットの基準を変えたい」と思ったとき、このメソッドは文字列(キーワード)を受け付けないため、例外エラー(あるいはVBAのエラー)を吐いて強制終了するか、ユーザーが `Esc` を叩いてやり直すしかない。
実務の現場で求められるのは、「入力を待ち受けている最中に、特定のキー(例えば `S` や `Settings`)が押されたら、処理を中断せずに設定用UserFormを呼び出し、設定変更後に何食わぬ顔で距離入力を再開する」という堅牢なループ構造である。
これを実現するのが `InitializeUserInput` メソッドとの組み合わせだ。
—
2. 堅牢な対話ループを構築する設計思想
`GetDistance` でキーワード入力を受け付けるためには、直前に `Utility.InitializeUserInput` を実行する必要がある。
`InitializeUserInput` のビットマスク制御
このメソッドの第一引数(`BitFlags`)と第二引数(`Keywords`)の仕様を正確に理解しているエンジニアは少ない。
- `BitFlags = 1`: キーワード入力時、負の数値(マイナス値)の入力を禁止する。
- キーワードの書式: `設定(S)` のように記述する場合、大文字で返すのがAutoCADの作法である。キーワードはスペース区切りで複数登録できる(例: `”Settings Options”`)。
ユーザーが数値を入力すれば `Double` 型の距離が返り、キーワードを入力すれば `Err.Number` が発生するか、あるいは戻り値の制御が必要になる。VBAにおけるエラーハンドリングの特性を利用して、「数値が返ったのか」「キーワードが選ばれたのか」「キャンセルされたのか」を完璧にハンドリングするアーキテクチャが必要となる。
—
3. 【プロダクションコード】コピペで即戦力となる実装例
以下のコードは、実務の現場でそのまま組み込める、エラーハンドリングとUserForm連携を網羅した完成形のモジュールだ。
標準モジュール(MainLogic.bas)
Option Explicit
‘ グローバルまたはモジュールレベルで設定値を保持(必要に応じてクラス化してください)
Public g_Tolerance As Double
Sub RunInteractiveDistanceTool()
Dim acadDoc As AcadDocument
Set acadDoc = ThisDrawing
‘ 初期値の設定
g_Tolerance = 1.0
On Error GoTo ErrorHandler
Do
‘ 1. キーワードの登録
‘ 第1引数: 1 (負の値の入力を許可しない等のフラグ)
‘ 第2引数: スペース区切りでキーワードを指定。大文字が認識される。
acadDoc.Utility.InitializeUserInput 1, “Settings”
Dim retDist As Double
Dim promptMsg As String
promptMsg = vbCrLf & “基準距離を指定 [設定(S)] <" & g_Tolerance & ">: ”
‘ 2. 距離の取得(ここでユーザーの入力を待つ)
retDist = acadDoc.Utility.GetDistance(, promptMsg)
‘ — A. 数値が正常に入力された場合の処理 —
MsgBox “取得した距離: ” & retDist & vbCrLf & “現在の許容値設定: ” & g_Tolerance, vbInformation, “処理実行”
Exit Do ‘ 目的を達成したのでループを抜ける
ErrorHandler:
‘ — B. キーワード入力やキャンセルが発生した場合のトラップ —
If Err.Number = -2145320939 Then
‘ ※AutoCADのCOMエラーでキーワードが入力された場合のエラー番号、
‘ もしくはGetInputメソッドでキーワードを回収するアプローチに切り替える。
‘ より確実な「GetInput併用パターン」を以下に展開する。
End If
Err.Clear
Loop
End Sub
【洗練されたアプローチ】`GetInput` を組み合わせた堅牢な実装
実は、`GetDistance` はキーワードが入力された瞬間にランタイムエラーを発生させる仕様があるため、実務では `GetKeyword` とのハイブリッド、あるいは `GetPoint` 系譜のテクニックを使うのが最もバグが少ない。
ここでは、実務で最も安定する「入力値の型をまず文字列として受け取り、数値かキーワードかを判定する」プロダクションコードを提示する。
Option Explicit
‘ 設定値を管理する構造体
Type ToolSettings
OffsetVal As Double
ModeStr As String
End Type
Public CurrentSettings As ToolSettings
Sub ExecuteProInteractiveTool()
Dim acadUtil As AcadUtility
Set acadUtil = ThisDrawing.Utility
‘ 基礎設定の初期化
CurrentSettings.OffsetVal = 5.0
CurrentSettings.ModeStr = “Standard”
On Error GoTo CatchError
Do
‘ キーワードの初期化 (Bit 1: 負数拒否, Bit 32: キーワード無効化を避ける等)
‘ ユーザーが「S」と入力できるようにする
acadUtil.InitializeUserInput 0, “Settings”
Dim inputVal As Variant
‘ 一度String/Variantとしてプロンプトを出すために GetString を使うか、
‘ あるいは GetDistance の特殊挙動を利用する。
‘ ここでは王道の GetDistance を使い、エラーハンドリングでキーワードを回収する。
Dim retDist As Double
retDist = acadUtil.Utility.GetDistance(, vbLf & “距離を指定 [設定(S)]: “)
‘ 正常に数値(距離)が取れた場合
Call ProcessMainLogic(retDist)
Exit Do
CatchError:
‘ AutoCAD特有のエラー番号:ユーザーがキーワードを入力した、または右クリック/ESC等
If Err.Number <> 0 Then
Dim errDesc As String
errDesc = Err.Description
‘ キーワード “SETTINGS” が入力された場合の捕捉
‘ ※AutoCADのバージョンによりエラーメッセージが異なるため、GetInputで再取得するのがプロの技
Err.Clear
On Error Resume Next
Dim keyWordResult As String
acadUtil.InitializeUserInput 0, “Settings”
keyWordResult = acadUtil.GetKeyword(vbLf & “オプション [設定(S)]: “)
If Err.Number = 0 Then
If UCase(keyWordResult) = “SETTINGS” Then
‘ ── ここでUserFormを呼び出す! ──
Dim frm As frmSettings
Set frm = New frmSettings
frm.Show vbModal
‘ UserForm側でグローバル変数や構造体が書き換えられるため、ループを継続
Unload frm
End If
Else
‘ ユーザーによるキャンセル(ESCなど)
MsgBox “処理がキャンセルされました。”, vbExclamation
Exit Sub
End If
On Error GoTo CatchError
End If
Loop
End Sub
Private Sub ProcessMainLogic(d As Double)
‘ 実際の業務処理(画層作成、図形描画、DB連携など)
MsgBox “処理完了。距離: ” & d & ” / 設定値: ” & CurrentSettings.OffsetVal, vbInformation
End Sub
—
4. ファイル・データベース連携における極上の注意点
このような対話型ツールを実務展開する際、以下の「現場特有の罠」に必ず直面する。チーフアーキテクトからの警告として心してほしい。
1. トランザクションのスコープと画面描画のロック
対話ループ(`Do…Loop`)の中で頻繁にUserFormを開閉したり、外部のExcelやAccessデータベースにアクセスさせたりすると、AutoCADのオブジェクトデータベース(`AcadDatabase`)との間でポインタの整合性が狂うことがある。データベースへの書き込みは、対話ループの「内部」ではなく、数値が確定して `ProcessMainLogic` に移行した「確定時」に一括で行うこと。
2. モデル空間のトランザクション汚染を防ぐ
`ThisDrawing.ModelSpace` への要素追加は、必ずエラーハンドリング(`Exit Sub` 時のクリーンアップ)を網羅した状態で行うこと。途中で `Esc` が押されてトラップ漏れを起こすと、AutoCADのセッション自体が不安定になる。
—
5. まとめ
AutoCAD VBAで「動くだけのスクリプト」を書く時代は終わった。
ユーザーがストレスなく操作でき、標準コマンドと同等のUXを提供するインターフェースを作ることこそが、業務自動化エンジニアの存在意義である。
今回解説した `InitializeUserInput` とエラーハンドリングを組み合わせたキーワード入力併用術は、あなたの作るすべてのVBAツールの品質を一段上のステージへと引き上げるはずだ。
妥協のない設計で、現場の生産性を爆発的に向上させてほしい。
