【テクニカル・上級編】「オブジェクト変数または With ブロック変数が見つかりません」を根絶するNothing判定の実装パターン – CorelDRAW VBA解析バイブル

スポンサーリンク

「オブジェクト変数または With ブロック変数が見つかりません」を根絶するNothing判定の実装パターン

CorelDRAW VBAの開発現場において、実行時エラー「オブジェクト変数または With ブロック変数が見つかりません」(エラー91)は、幾度となくエンジニアの前に立ちふさがる壁だ。

存在しないシェイプの参照、閉じられたドキュメントへのアクセス、あるいはレイヤー階層の迷子。これらは単なるコーディングミスではなく、CorelDRAWのCOMオブジェクトモデルのライフサイクルと、VBAのランタイムにおける参照解決のメカニズムを軽視していることに起因する。

本稿では、レガシーなVBA環境であっても、C/C++や.NETのネイティブ開発並みの堅牢性を担保し、エラー91を完全に根絶するための「Nothing判定の極限パターン」を、チーフアーキテクトの視点から授けよう。

1. なぜCorelDRAW VBAでエラー91が頻発するのか

CorelDRAWのオブジェクトモデルは、強力である反面、非常に繊細なCOMラッパー上で動作している。

‘ 【アンチパターン】これはいけない
Dim sh As Shape
Set sh = ActivePage.FindShape(“MyTarget”)
sh.Fill.ApplyUniformFill CreateRGBColor(255, 0, 0)

もしアクティブページ上に `”MyTarget”` という名前のシェイプが存在しなかった場合、`FindShape` メソッドは有効なオブジェクトを返さず、内部的に `Nothing` を返す。この状態で `sh.Fill` にアクセスした瞬間、VBAランタイムはメモリ上の不正なアドレスを参照し、容赦なくエラー91を発生させる。

オブジェクトが「存在しない」可能性があるシチュエーションにおいて、参照の有無を確認せずにプロパティやメソッドをたたくのは、地雷原を目隠しで歩くようなものだ。

2. 厳格なNothing判定とオプショナルチェイニング的アプローチ

VBAには現代的な言語にあるようなオプショナルチェイニング(`?.`)は存在しない。そのため、開発者自身が明示的に `Nothing` をガード節(Guard Clause)として記述する必要がある。

実務で即座に使える、最も安全なオブジェクト取得と判定のイディオムを以下に示す。

Public Sub SafeModifyShape()
Dim targetShape As Shape

‘ 1. 安全なオブジェクトの取得
On Error Resume Next
Set targetShape = ActivePage.FindShape(“Dynamic_ID_999”)
On Error GoTo 0

‘ 2. 厳格なNothing判定(ガード節)
If targetShape Is Nothing Then
‘ ログ出力や代替処理
Debug.Print “[WARN] 対象シェイプが見つかりませんでした。”
Exit Sub
End If

‘ 3. オブジェクトが確実に存在する状態での安全な操作
targetShape.Outline.SetProperties Color:=CreateARGBColor(255, 255, 0, 0)

‘ 4. メモリ最適化:明示的な参照解放
Set targetShape = Nothing
End Sub

チーフアーキテクトの知見:`Is` 演算子の絶対性

VBAでオブジェクトを比較する際、`=` 演算子を使ってはならない。必ず `Is` 演算子を使用すること。
`If targetShape = Nothing Then` と書くと、VBAはオブジェクトの中身の値と比較しようとし、ここでも別の実行時エラーを引き起こす。「オブジェクトの生死確認は `Is Nothing` で行う」 これが鉄則である。

3. 階層構造(Document -> Page -> Layer -> Shape)における多重ガード

CorelDRAWの自動化において最も悪名高いのが、ドキュメント構造の深層を探索する際のロストだ。アクティブドキュメントがない状態で `ActiveLayer` などを呼び出すと、それだけでエラーか予期せぬ挙動を引き起こす。

実システム連携や、外部からドキュメントを一括処理するバッチ処理では、以下のような多重防護(レイヤード・ガード)を実装すべきである。

Public Function GetTargetLayer(docName As String, layerName As String) As Layer
Dim doc As Document
Dim lyr As Layer

‘ 初期値の設定
Set GetTargetLayer = Nothing

‘ ドキュメントコレクションの走査と存在確認
Dim i As Long
For i = Documents.Count To 1 Step -1
If Documents(i).Name = docName Then
Set doc = Documents(i)
Exit For
End If
Next i

‘ ドキュメントが存在しない場合の早期リターン
If doc Is Nothing Then Exit Function

‘ レイヤーの探索
For Each lyr in doc.Pages.First.Layers
If lyr.Name = layerName Then
Set GetTargetLayer = lyr
Exit For
End If
Next lyr

‘ クリーンアップ
Set doc = Nothing
End Function

この関数を使用する側でも、返り値が `Nothing` である可能性を常に想定したロジックを組む必要がある。

4. メモリ最適化とCOMオブジェクトの明示的解放

VBAのガベージコレクションは、.NETのそれほど洗練されていない。特にCorelDRAWのような重厚なCOMサーバーを操作する場合、参照を切ったつもりのオブジェクトがメモリリークを引き起こし、長時間のバッチ処理でCorelDRAW本体をフリーズさせる原因となる。

ライフサイクル管理の鉄則

1. 変数のスコープを最小限にする
2. ループ内でオブジェクト変数を使い回さない(毎回 `Set` し直すか、ループ後に解放する)
3. プロシージャの終了前には、必ず `Set xxx = Nothing` で参照を明示的に断つ

Public Sub BatchProcessShapes()
Dim doc As Document
Dim sh As Shape
Dim sRange As ShapeRange

Set doc = Application.ActiveDocument
If doc Is Nothing Then Exit Sub

Set sRange = doc.SelectionRange
If sRange.Count = 0 Then
GoTo CleanUp
End If

Dim i As Long
For i = 1 To sRange.Count
Set sh = sRange(i)
If Not sh Is Nothing Then
‘ 処理の実行
sh.Rotate 90#
End If
‘ ループ内での個別変数の解放
Set sh = Nothing
Next i

CleanUp:
‘ 参照の完全な解放(逆順が望ましい)
Set sRange = Nothing
Set doc = Nothing

‘ 強制的なメモリガベージコレクションの誘発(必要に応じて)
DoEvents
End Sub

5. レガシー環境・外部システム連携における堅牢性

社内ニッチな基幹システムや、外部のPDM/ERPサーバーからVBAを介してCorelDRAWを自動制御する場合、CorelDRAWのUIスレッドやCOMオブジェクトの応答遅延に直面する。

「オブジェクトが存在するはずなのに、タイミングによって `Nothing` が返る」という競合状態(レースコンディション)には、Windows APIの `Sleep` を組み合わせたリトライ機構が極めて有効だ。

If VBA7 Then
Private Declare PtrSafe Sub Sleep Lib “kernel32” (ByVal dwMilliseconds As Long)
Else
Private Declare Sub Sleep Lib “kernel32” (ByVal dwMilliseconds As Long)
End If

Public Function SafeFindShapeWithRetry(parentLayer As Layer, shapeName As String, maxRetries As Long) As Shape
Dim sh As Shape
Dim attempt As Long

Set SafeFindShapeWithRetry = Nothing

For attempt = 1 to maxRetries
On Error Resume Next
Set sh = parentLayer.FindShape(shapeName)
On Error GoTo 0

If Not sh Is Nothing Then
Set SafeFindShapeWithRetry = sh
Exit Function
End If

‘ 描画更新やCOMの応答を待つためのウェイト(50ms〜100ms)
Sleep 50
DoEvents
Next attempt

‘ タイムアウト時はNothingが返る
End Function

この実装を取り入れることで、マシンパワーの差異や、重い画像ファイルを処理している最中のタイミングズレによるエラー91を完全にハイドレート(水際で阻止)することが可能となる。

総括

CorelDRAW VBAにおけるエラー91「オブジェクト変数または With ブロック変数が見つかりません」は、もはや不可避の災難ではない。

1. すべての外部参照・検索メソッドの戻り値を疑うこと
2. `Is Nothing` によるガード節を徹底すること
3. スコープ単位での明示的な `Set xxx = Nothing` によるメモリマネジメント
4. 非同期・タイミング問題を考慮したリトライ機構の実装

これらをコードの標準(コーディング標準)としてチーム全体に浸透させることができれば、あなたのCorelDRAW自動化システムは、商業現場の酷使にも微動だにしない、鉄壁の安定性を手に入れることになるだろう。

技術とは、祈ることではなく、構造で支配することだ。今すぐコードを見直し、無防備なオブジェクト参照を駆逐せよ。

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