CorelDRAWの自動化に携わる諸君、今日もベジェ曲線と格闘しているだろうか。
私はこれまで数え切れないほどの自動化ツールを設計してきたが、初心者が必ずと言っていいほど陥り、そして中級者ですら時折足をすくわれる「底なし沼」がある。それが、`Application.ActiveDocument` への過度な依存だ。
単一のファイルを開いて処理するだけのスクリプトなら問題はない。しかし、実務の世界はそんなに甘くない。複数のテンプレートを開き、データベースから流し込み、別名で保存する。そんな並列的なワークフローにおいて、`ActiveDocument` を使うのは、走行中の車のハンドルを、目隠しをしながら握るようなものだ。
今回は、CorelDRAW VBAにおける「ドキュメント操作の極意」を伝授する。
—
1. なぜ ActiveDocument を捨て、Object参照を保持すべきなのか
結論から言おう。「Active」という状態は、ユーザーの操作一つで容易に変動する「不安定なグローバル変数」に過ぎないからだ。
VBAの実行中にユーザーが別のウィンドウをクリックしたり、バックグラウンドで重い処理が走ってフォーカスが移った瞬間、`ActiveDocument` が指す先は変わる。その結果、Aというファイルの内容をBというファイルに書き込んでしまうような、取り返しのつかないバグが発生する。
負の遺産:素人臭いコード
‘ 典型的な失敗例
Documents.Open “C:\Template.cdr”
ActiveDocument.Unit = cdrMillimeter ‘ ターゲットがズレるリスクがある
ActiveLayer.CreateRectangle 0, 0, 100, 100
プロフェッショナルの設計
‘ 堅牢な設計
Dim targetDoc As Document
Set targetDoc = Documents.Open(“C:\Template.cdr”)
targetDoc.Unit = cdrMillimeter ‘ どのドキュメントを操作しているか一目瞭然
targetDoc.ActiveLayer.CreateRectangle 0, 0, 100, 100
このように、ドキュメントを開いた(あるいは生成した)瞬間に、そのオブジェクトを明示的な変数に格納する。 これが、安全な自動化への第一歩だ。
—
2. Documentsコレクションを掌握する
複数のドキュメントが開いている状態で、特定のファイルを探し出す必要がある場合、`Documents` コレクションをループさせるのが定石だ。しかし、単に名前を比較するだけでは不十分な場合がある。
ファイル名による厳密なドキュメント特定関数
実務では、同名のファイルが開いている可能性も考慮しなければならない。以下の関数は、特定のパスにあるドキュメントが既に開いているかを確認し、開いていればその参照を返し、なければ開くというロジックだ。
”’
”’
Public Function GetSafeDocument(fullPath As String) As Document
Dim doc As Document
Dim fileName As String
fileName = Mid(fullPath, InStrRev(fullPath, “\”) + 1)
‘ 1. 既に開いているドキュメントから探す
For Each doc In Application.Documents
‘ 完全一致を確認(パスまで含めるとより確実)
If doc.FullFileName = fullPath Then
Set GetSafeDocument = doc
Exit Function
End If
Next doc
‘ 2. 開いていなければ開く
If Dir(fullPath) <> “” Then
Set GetSafeDocument = Documents.Open(fullPath)
Else
MsgBox “ファイルが見つかりません: ” & fullPath, vbCritical
Set GetSafeDocument = Nothing
End If
End Function
—
3. 実践:複数ファイル間でのデータ転送(プロダクションコード)
ここでは、実務でよくある「マスタファイルから複数の個別ファイルへ要素をコピーする」処理を例に、堅牢なコードの書き方を解説する。
ポイントは、`Optimization`(画面更新停止)と、ドキュメント変数の使い分けだ。
Public Sub BatchProcessWithReferences()
Dim masterDoc As Document
Dim targetDoc As Document
Dim sourceShape As Shape
Dim fileList As Variant
Dim i As Integer
‘ 1. 環境の最適化(これを行わないエンジニアは三流だ)
Application.Optimization = True
Application.EventsEnabled = False
On Error GoTo ErrorHandler
‘ 2. マスタードキュメントの参照を固定
Set masterDoc = Application.ActiveDocument
If masterDoc Is Nothing Then Exit Sub
‘ コピー対象のオブジェクトを取得
Set sourceShape = masterDoc.ActivePage.Shapes(1) ‘ 例えば1番目のシェイプ
‘ 処理対象のファイルリスト(本来はDBやフォルダから取得する)
fileList = Array(“C:\Work\ProjectA.cdr”, “C:\Work\ProjectB.cdr”)
For i = LBound(fileList) To UBound(fileList)
‘ 3. ターゲットを安全に取得
Set targetDoc = GetSafeDocument(CStr(fileList(i)))
If Not targetDoc Is Nothing Then
‘ 4. 明示的な参照による操作
‘ masterDocからコピーし、targetDocにペースト
sourceShape.Copy
targetDoc.ActivePage.Layers(1).Paste
‘ 保存して閉じる(参照を保持しているからこそミスが起きない)
targetDoc.Save
targetDoc.Close
End If
Next i
CleanUp:
‘ 5. 環境を元に戻す(必ず実行されるように設計する)
Application.Optimization = False
Application.EventsEnabled = True
Application.Refresh
MsgBox “バッチ処理が完了しました。”, vbInformation
Exit Sub
ErrorHandler:
MsgBox “エラー発生: ” & Err.Description, vbCritical
Resume CleanUp
End Sub
—
4. 運用上の注意点と「罠」
ドキュメントのクローズと変数の中身
`targetDoc.Close` を実行した後、変数 `targetDoc` にはまだ何かが入っているように見えるが、その中身は「無効なオブジェクト」だ。Closeした直後にその変数にアクセスするとクラッシュの原因になる。使い終わったら `Set targetDoc = Nothing` を徹底せよ。
データベース連携時のヒント
SQL ServerやExcelなどの外部DBからファイルパスを取得して処理する場合、CorelDRAWがそのファイルを開ける状態にあるか(他者が開いてロックしていないか)を確認するロジックを `Documents.Open` の前に挟むのがプロの仕事だ。
UI操作の排除
VBA実行中にダイアログが出て止まるのを防ぐため、`Application.SuppressAllDialogs = True` の活用も検討すべきだが、これは諸刃の剣だ。エラーすら表示されなくなるため、デバッグが完了するまではオフにしておくことを推奨する。
—
結論:コードの品質は「参照の扱い」に宿る
`ActiveDocument` を使わない。たったこれだけのルールを守るだけで、君の作成するツールの信頼性は劇的に向上する。
複数のファイルを跨ぐ処理を書く際は、常に「今、どの変数にどのドキュメントが格納されているか」を脳内マップで描けるように設計してほしい。それができて初めて、CorelDRAWという巨大なエンジンの真の力を引き出すことができるのだ。
次は、レイヤーのライフサイクル管理と、メモリリークを防ぐためのオブジェクト解放について語るとしよう。さらなる高みを目指せ。
