CorelDRAW VBAを掌握する極限の知見
【上級】サードパーティ製プラグインと動的引数制御によるノンストップ・エクスポートパイプライン
CorelDRAWの標準VBAメソッドである `ActiveDocument.Export` や `StructExportOptions` は、一般的なPDFやJPEG、PNGの出力においては十分な機能を提供する。しかし、現場が求める「真の自動化」はそこにはない。
専用のRIPソフト、特殊なサードパーティ製プラグイン、あるいはレガシーなカスタムエクスポートフィルターを介する際、CorelDRAWの標準ラッパーは無力と化す。決まって画面の描画を伴うモーダルダイアログが立ち上がり、VBAの実行スレッドは冷酷に凍結(ハング)する。
本稿では、Windows APIの深部をハックし、外部プロセスのウィンドウライフサイクルをVBAのメモリ空間から直接制御することで、あらゆるサードパーティ製フィルターを完全なスクリプト制御下に置くアドバンスド連携の極限を解説する。
—
1. 標準エクスポートの限界と「ダイアログの呪縛」
サードパーティ製のエクスポートフィルターやプラグインをVBAから呼び出す時、彼らは決まって独自のGUI(設定画面)を前面に出す。これはユーザーインタラクションを前提としているためであり、`Application.Optimization = True` を宣言しようとも、この割り込みを防ぐことはできない。
この呪縛を断ち切るには、以下の2つのアプローチを統合する必要がある。
1. Windows API (`FindWindow`, `SendMessage`, `PostMessage`) を用いた、非同期ウィンドウメッセージの直接流し込み。
2. コレクションや構造体のメモリリークを完全に排除した オブジェクトのライフサイクル管理。
—
2. 実装アーキテクチャ:動的引数制御エンジン
以下に提示するのは、外部プラグインが突如として描画する設定ダイアログを背後から捕捉し、人間が介在することなく「OK」ボタンを自動押下、さらに背後のファイルI/O競合を防ぐための同期ウェイトを挟むプロダクションコードの骨子である。
Option Explicit
‘ ==============================================================================
‘ Windows API Declarations for Advanced Window Lifecycle Control
‘ ==============================================================================
If VBA7 Then
Private Declare PtrSafe Function FindWindowEx Lib “user32” Alias “FindWindowExA” (ByVal hWndParent As LongPtr, ByVal hWndChildAfter As LongPtr, ByVal lpszClass As String, ByVal lpszWindow As String) As LongPtr
Private Declare PtrSafe Function SendMessage Lib “user32” Alias “SendMessageA” (ByVal hWnd As LongPtr, ByVal wMsg As Long, ByVal wParam As LongPtr, ByVal lParam As LongPtr) As LongPtr
Private Declare PtrSafe Function Sleep Lib “kernel32” (ByVal dwMilliseconds As Long) As Long
Else
Private Declare Function FindWindowEx Lib “user32” Alias “FindWindowExA” (ByVal hWndParent As Long, ByVal hWndChildAfter As Long, ByVal lpszClass As String, ByVal lpszWindow As String) As Long
Private Declare Function SendMessage Lib “user32” Alias “SendMessageA” (ByVal hWnd As Long, ByVal wMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
Private Declare Function Sleep Lib “kernel32” (ByVal dwMilliseconds As Long) As Long
End If
Private Const BM_CLICK As Long = &HF5
Private Const MAX_WAIT_RETRIES As Long = 50
‘ ==============================================================================
‘ Core Subroutine: Non-Stop Plugin Export Pipeline
‘ ==============================================================================
Public Sub ExecuteNonStopPluginExport(ByVal targetFilePath As String)
Dim doc As CorelDRAW.Document
Dim expFilter As CorelDRAW.ExportFilter
Dim expopt As CorelDRAW.StructExportOptions
‘ 1. 極限のパフォーマンス最適化と画面描画の凍結
EventsEnabled = False
Optimization = True
On Error GoTo ErrorHandler
Set doc = ActiveDocument
If doc Is Nothing Then Err.Raise 9999, “ExportPipeline”, “アクティブなドキュメントが存在しません。”
‘ 2. エクスポートオプションの構築 (メモリ割り当て)
Set expopt = New CorelDRAW.StructExportOptions
With expopt
.Antialiasing = True
.Overwrite = True
‘ サードパーティ製プラグイン固有のフィルタIDを指定(例としてカスタムIDを使用)
.IncludeFileHeader = False
End With
‘ 3. 別スレッド/非同期を想定したダイアログ監視・自動応答の準備
‘ ※実際のプラグイン呼び出しとAPI監視は同期するため、シェル/タイマー連携または
‘ マルチスレッドラッパー(COMアドイン等)との併用が理想だが、ここではVBA単体での限界を突く。
Debug.Print “Export process initiated for: ” & targetFilePath
‘ 4. エクスポート実行(ここでプラグインのモーダルダイアログが割り込む可能性がある)
‘ ※CorelDRAWのネイティブメソッドによるサードパーティフィルター呼び出し
Set expFilter = doc.ExportEx(targetFilePath, cdrExtensionsFilter, cdrCurrentPage, expopt)
‘ プラグイン固有の設定をコード側から確定させるためのAPI介入トリガー
Call AutoDismissPluginDialog(“プラグイン設定画面のウィンドウ名”)
expFilter.Finish
ErrorHandler:
If Err.Number <> 0 Then
MsgBox “致命的なエラーが発生しました: ” & Err.Description, vbCritical, “CorelDRAW Automation Engine”
End If
‘ ==============================================================================
‘ 5. メモリの完全解放とオブジェクトライフサイクルの終結 (極めて重要)
‘ ==============================================================================
Set expFilter = Nothing
Set expopt = Nothing
Set doc = Nothing
Optimization = False
EventsEnabled = False
Application.Refresh
End Sub
‘ ==============================================================================
‘ Utility: Background Window Interceptor
‘ ==============================================================================
Private Sub AutoDismissPluginDialog(ByVal windowTitle As String)
Dim hDlg As LongPtr
Dim hBtn As LongPtr
Dim retryCount As Long
retryCount = 0
‘ ダイアログが生成されるまでポーリングで待機 (最大数秒間)
Do
#If VBA7 Then
hDlg = FindWindowEx(0&, 0&, vbNullString, windowTitle)
#Else
hDlg = FindWindowEx(0, 0, vbNullString, windowTitle)
#End If
If hDlg <> 0 Then Exit Do
Sleep 100 ‘ CPUを圧迫しないためのウェイト
retryCount = retryCount + 1
If retryCount > MAX_WAIT_RETRIES Then Exit Sub
Loop
‘ ダイアログ内の「OK」ボタン(通常クラス名 “Button”、キャプション “OK”)を探索
#If VBA7 Then
hBtn = FindWindowEx(hDlg, 0&, “Button”, “OK”)
#Else
hBtn = FindWindowEx(hDlg, 0, “Button”, “OK”)
#End If
If hBtn <> 0 Then
‘ ボタンへクリックメッセージを強制送信し、ダイアログを強制突破
SendMessage hBtn, BM_CLICK, 0&, 0&
End If
End Sub
—
3. チーフアーキテクトが指摘する「メモリ管理と破滅の回避」
CorelDRAW VBAにおいて、サードパーティ製プラグインや外部COMオブジェクトを操作する際、最も恐れなければならないのはCOM参照のゾンビ化とメモリリークである。
- オブジェクトの明示的破棄の順序
`StructExportOptions` や `ExportFilter` などのVBAラッパーオブジェクトは、スコープを抜けただけでは即座にCOM参照のカウンタがデクリメントされない場合がある。特に大量のファイルをループ処理ですり潰すバッチ処理では、必ずループの各イテレーションの末尾で `Set obj = Nothing` を明示し、ガベージコレクションの確実な誘導を行わなければ、数千回のエクスポートでCorelDRAW本体がメモリエラー(Out of Memory)でクラッシュする。
- グローバルステートの復元
`Optimization = True` や `EventsEnabled = False` は描画速度を極限まで引き上げるが、エラーハンドラー(`On Error GoTo`)を通った際にもこれらが確実に元の状態(`False` / `True`)に戻るよう、トランザクション的なコード構造を徹底すること。異常終了時に最適化フラグが立ちっぱなしになったドキュメントは、UI操作を受け付けないゾンビ状態と化す。
—
4. システム間連携の極限:ファイルI/Oの非同期競合対策
サードパーティ製プラグインを経由したエクスポートのもう一つの罠は、「ファイルハンドルが解放される前の後続プロセスによるファイル強奪(アクセス拒否エラー)」である。
プラグインがディスクへの書き込みを完了したとVBAが検知する前に、次段のシステム(例えば自動面付けソフトやWebDAVアップローダー)が動き出すと、共有違反エラーが発生する。これを防ぐためには、単なる `Sleep` による時間稼ぎではなく、Windowsのファイルシステム変更通知(ReadDirectoryChangesW)のラップ、あるいは排他制御によるファイルオープンの成否判定ループをVBA側に実装する必要がある。
実務においては、エクスポート処理の直後に以下のような堅牢なポーリング関数を挟むことが、ノンストップ運用を担保する絶対条件となる。
Private Function WaitUntilFileIsReady(ByVal filePath As String, ByVal timeoutSec As Long) As Boolean
Dim fNum As Integer
Dim startTime As Single
startTime = Timer
Do
On Error Resume Next
fNum = FreeFile
‘ 排他モードでファイルを開き、他のプロセスが書き込み中でないか確認
Open filePath For Input Lock Read Write As #fNum
If Err.Number = 0 Then
Close #fNum
WaitUntilFileIsReady = True
Exit Function
End If
Close #fNum
On Error GoTo 0
If (Timer – startTime) > timeoutSec Then
WaitUntilFileIsReady = False
Exit Function
End If
Sleep 200
Loop
End Function
—
総括
CorelDRAW VBAは、単なるマクロ記録の延長ではない。Windows OSのメモリ空間とプロセス間通信を正しく理解し、サードパーティ製プラグインの隠された挙動すらもAPIによって調伏するならば、それは強固で信頼性の高い「エンタープライズ・エクスポート・パイプライン」へと昇華する。
妥協のない設計と、リソースのライフサイクルに対する絶対的な支配力こそが、プロフェッショナルな自動化エンジニアに求められる唯一の資格である。
