「オブジェクト変数または With ブロック変数が見つかりません」を根絶するNothing判定の実装パターン
CorelDRAW VBAの開発現場において、最も頻繁に遭遇し、かつ最も開発者の精神を削るエラーが何か知っているか?
そう、実行時エラー 91:「オブジェクト変数または With ブロック変数が見つかりません」だ。
画面上にあるはずのシェイプを選択してマクロを実行したはずなのに、少し条件が変わってオブジェクトが消えていただけで、VBAは容赦なくクラッシュする。エラーダイアログの「デバッグ」ボタンを押すたびに、自分のコードの脆弱性を突きつけられているようで嫌気が差すことだろう。
アマチュアプログラマーは「エラーが出たら `On Error Resume Next` で力技で握りつぶせ」と教える。しかし、世界最高峰の自動化エンジニアを目指す我々がそんな泥臭いハックを使う必要はない。
オブジェクトのライフサイクルを正しく理解し、堅牢な `Nothing` 判定を体系化すれば、このエラーは100%根絶できる。
今回は、CorelDRAWのオブジェクトモデルの挙動を深く掘り下げ、実務の現場で絶対に破綻しないプロダクションコードの設計思想を授けよう。
—
1. なぜ「エラー91」は発生するのか?(CorelDRAW VBAの闇)
C#やVB.NETなどのモダンな言語であれば、存在しないオブジェクトへのアクセスは `NullReferenceException` として安全に捕捉できる。しかし、VBA(特にCOMコンポーネントを操作するCorelDRAW VBA)の世界では、ポインタが無効なメモリ領域を指した瞬間にランタイムが悲鳴を上げる。
特にCorelDRAW特有の罠がこれだ:
- コレクションの1始まりと存在チェックの罠: `ActivePage.Shapes(1)` は、ページ上に1つもシェイプがない場合に容赦なくエラーを吐く。
- ユーザーの気まぐれ: 「選択されているシェイプ」に対して処理を行うマクロ(`ActiveSelection`)を実行した際、ユーザーが何も選択していなかった場合の考慮漏れ。
- DocumentやLayerの消滅: 外部連携やマルチドキュメント環境で、参照していたドキュメントがすでに閉じられているケース。
これらを「存在するものとしてコードを書く」こと自体が、設計の敗北なのだ。
—
2. 堅牢なコードの要:型安全なオブジェクト参照とNothing判定
VBAでオブジェクト変数を宣言すると、初期状態ではメモリ上の実体を指していない「空(Nothing)」の状態になる。これを `Set` ステートメントでCorelDRAWのCOMオブジェクトに結びつけるわけだ。
したがって、操作を行う前に「その変数が本当に実体を保持しているか」を検証するガード節(Guard Clause)を挟むのが、プロフェッショナルの鉄則である。
❌ 悪い例:脆弱なアンチパターン
Sub BadExample()
‘ 選択されているシェイプを取得しようとするが、未選択だとここで即死する
Dim shp As Shape
Set shp = ActiveSelection.Shapes(1)
‘ エラー91のせいで、この行に到達することすらない
shp.Fill.UniformColor.CmykAssign 0, 100, 100, 0
End Sub
⭕ 良い例:ガード節による安全な判定
Sub GoodExample()
Dim shp As Shape
‘ 1. アクティブな選択範囲が存在するかチェック
If ActiveSelection.Shapes.Count = 0 Then
MsgBox “シェイプが選択されていません。”, vbExclamation
Exit Sub
End If
‘ 2. 安全にオブジェクトを変数に格納
Set shp = ActiveSelection.Shapes(1)
‘ 3. 二重の備えとしてのNothing判定
If Not shp Is Nothing Then
shp.Fill.UniformColor.CmykAssign 0, 100, 100, 0
End If
End Sub
—
3. 【実践】ファイル・データベース連携を見据えたプロダクションコード
実務におけるCorelDRAWの自動化は、単体のドキュメント操作にとどまらない。CSVやデータベースからデータを読み込み、図形を動的に生成・置換するバッチ処理がメインになるだろう。
ここでは、「指定した名前のレイヤーやシェイプが存在しない場合、自動的に新規作成する」という、実務で頻出する堅牢なパターンを実装したモジュールを公開する。そのままコピー&ペーストして、プロジェクトの基盤として活用してほしい。
Option Explicit
” =================================================================
” Module: 堅牢なオブジェクト操作モジュール
” Description: エラー91を完全に排除し、安全なオブジェクト取得と自動生成を行う
” =================================================================
Public Sub ExecuteRobustAutomation()
Dim doc As Document
Dim targetLayer As Layer
Dim targetShape As Shape
‘ 1. ドキュメントが存在するか(開かれているか)の確認
If Documents.Count = 0 Then
MsgBox “処理対象のドキュメントが開かれていません。”, vbCritical, “致命的エラー”
Exit Sub
End If
Set doc = ActiveDocument
‘ 2. 特定名義のレイヤーを取得、なければ作成する(安全なファンクションの活用)
Set targetLayer = GetOrCreateLayer(doc, “Automation_Data_Layer”)
If targetLayer Is Nothing Then
MsgBox “レイヤーの取得または作成に失敗しました。”, vbCritical
Exit Sub
End If
‘ 3. 特定名義のシェイプを取得、なければプレースホルダーを生成
Set targetShape = FindShapeByName(targetLayer, “Header_Logo”)
If targetShape Is Nothing Then
‘ 存在しない場合のフォールバック処理(新規作成)
Set targetShape = targetLayer.CreateRectangle(0, 0, 100, 50)
targetShape.Name = “Header_Logo”
targetShape.Fill.UniformColor.RGBAssign 200, 50, 50
Debug.Print “新規シェイプ ‘Header_Logo’ を作成しました。”
Else
‘ 存在する場合の更新処理
targetShape.Fill.UniformColor.RGBAssign 50, 200, 50
Debug.Print “既存のシェイプ ‘Header_Logo’ を更新しました。”
End If
End Sub
” —————————————————————–
” ユーティリティ1: レイヤーフリーク回避・安全な取得/作成関数
” —————————————————————–
Private Function GetOrCreateLayer(ByRef doc As Document, ByVal layerName As String) As Layer
Dim lyr As Layer
On Error GoTo ErrorHandler
‘ レイヤーの存在確認
For Each lyr In doc.Pages(1).Layers
If lyr.Name = layerName Then
Set GetOrCreateLayer = lyr
Exit Function
End If
Next lyr
‘ 存在しない場合は新規作成
Set GetOrCreateLayer = doc.Pages(1).CreateLayer(layerName)
Exit Function
ErrorHandler:
‘ 予期せぬCOMエラーをトラップし、Nothingを返す
Set GetOrCreateLayer = Nothing
End Function
” —————————————————————–
” ユーティリティ2: 名前の衝突を防ぐ安全なシェイプ検索関数
” —————————————————————–
Private Function FindShapeByName(ByRef lyr As Layer, ByVal shapeName As String) As Shape
Dim shp As Shape
On Error GoTo ErrorHandler
For Each shp In lyr.Shapes
If shp.Name = shapeName Then
Set FindShapeByName = shp
Exit Function
End If
Next shp
‘ 見つからなかった場合は明示的にNothingを返す
Set FindShapeByName = Nothing
Exit Function
ErrorHandler:
Set FindShapeByName = Nothing
End Function
—
4. チーフアーキテクトからの提言:防衛的プログラミングのすすめ
プログラミングのスキルとは、「うまく動くコードを書く能力」ではない。「予期せぬ異常事態に直面したとき、システムがどのように優雅に(Gracefulに)振る舞うかをデザインする能力」だ。
業務効率化ツールを現場に導入する際、ユーザーは開発者の想像を超えた奇行(存在しないレイヤーの削除、オブジェクト未選択でのボタン連打、ファイルの強制終了など)を平気で行う。そのたびに「エラー91」でVBAのコード画面が開き、一般の事務スタッフがパニックを起こすようなツールは、自動化ツールではなく「ただの爆弾」である。
今回紹介した `Nothing` 判定とガード節の徹底、そして安全なファンクションによる抽象化をマスターすれば、あなたの書くCorelDRAWマクロは見違えるほど堅牢になるだろう。
エラーに怯える日々は今日で終わりだ。今すぐ既存のコードをリファクタリングし、鉄壁の自動化基盤を築き上げてほしい。
