【実務・中級編】【全対象】複数のVisio図面を結合して1つのPDFにするためのVBA連携術 – Visio VBA解析バイブル

スポンサーリンク

【Visio VBA極限活用】散在する複数VSDXを1撃で統合PDF化する!Acrobat API連携の全技術

開発現場のエンジニア諸君、日々の図面管理に疲弊していないか?
「複数のVisio図面(.vsdx)をそれぞれPDFに変換し、それを手動でAcrobatを開いて結合する」——もし、そんな不毛なルーティンワークを部下にやらせている、あるいは自分がやっているなら、今すぐその手を止めてほしい。

我々はエンジニアだ。定型作業はコードに語らせ、人間はより創造的な設計にリソースを割くべきだ。

今回は、「複数のVisio図面をVBAでサイレント一括PDF化し、AcrobatのCOMインターフェース(API)を叩いて一瞬で1つのPDFに結合する」という、実務の現場でそのまま使える堅牢な自動化アーキテクチャを伝授する。

よくある「PDFの結合にフリーの不安定なライブラリを使う」といった愚行はしない。Adobe公式の堅牢なAPIを叩き、エンタープライズ環境でも耐えうる堅牢性を持ったコードを解説しよう。

—

1. なぜこの連携でハマるのか?(設計思想の共有)

多くのプログラマがVisio VBAと外部COM連携の組み合わせで挫折する理由は明確だ。

1. Visioの裏でダイアログが固まる(モーダル地獄)
`Document.ExportAsFixedFormat` を実行する際、同名のPDFが既に存在していると、Visioは容赦なく上書き確認のダイアログを出し、VBAの実行は背後でフリーズする。
2. AcrobatのCOMオブジェクトのライフサイクル管理の失敗
`AcroExch.PDDoc` や `AcroExch.App` は、ガベージコレクションの挙動が極めてシビアであり、プロセスがメモリ上に残留してゾンビ化する。

これらを完全にハックし、「完全非対話(完全サイレント)」「確実なメモリ解放」「例外ハンドリング」を網羅したプロダクションコードを以下に提示する。

—

2. プロダクションコード:一括PDF結合エンジン

以下のコードを、統合を統括する親VSDX、またはマクロ専用のExcel/Wordブックの標準モジュールに実装してほしい。今回は「指定フォルダ内のすべてのVSDXを名前順に結合する」という最も需要の高いユースケースを実装している。

Option Explicit

‘ ==============================================================================
‘ 処理名: 複数Visio図面一括PDF結合マクロ
‘ 概要 : 指定フォルダ内の.vsdxファイルを順次PDF化し、Acrobat APIを用いて
‘ 1つのPDFファイルに結合する。
‘ 前提 : Adobe Acrobat Pro (またはAcrobat Standard) がインストールされていること
‘ 参照設定: Adobe Acrobat Type Library (Acrobat.tlb) を推奨(今回はレイトバインドで実装)
‘ ==============================================================================
Public Sub MergeVisioFilesToSinglePDF()

‘ — 設定エリア —
Dim targetFolder As String
targetFolder = “C:\VisioWorkSpace\TargetDrawings\” ‘ 対象VSDXがあるフォルダ

Dim outputPdfPath As String
outputPdfPath = “C:\VisioWorkSpace\Output_Merged.pdf” ‘ 最終的な結合PDFの出力先

‘ フォルダパスの末尾バックスラッシュチェック
If Right$(targetFolder, 1) <> “\” Then targetFolder = targetFolder & “\”

‘ — エラーハンドリングの要 —
On Error GoTo ErrorHandler

‘ 1. 対象ファイルの存在確認と配列への取得
Dim fileNames() As String
Dim fileCount As Long
fileCount = 0

Dim fso As Object
Set fso = CreateObject(“Scripting.FileSystemObject”)

If Not fso.FolderExists(targetFolder) Then
MsgBox “指定されたフォルダが存在しません: ” & targetFolder, vbCritical, “パスエラー”
Exit Sub
End If

Dim targetFile As Object
For Each targetFile In fso.GetFolder(targetFolder).Files
If LCase(fso.GetExtensionName(targetFile.Name)) = “vsdx” Then
ReDim Preserve fileNames(fileCount)
fileNames(fileCount) = targetFile.Path
fileCount = fileCount + 1
End If
Next targetFile

If fileCount = 0 Then
MsgBox “対象となる.vsdxファイルが見つかりませんでした。”, vbExclamation, “ファイルなし”
Exit Sub
End If

‘ ファイル名でソート(必要に応じてバブルソート等で順序を担保)
Call SortStringArray(fileNames)

‘ 2. 一時PDFファイルの保存用フォルダ準備
Dim tempPdfPaths() As String
ReDim tempPdfPaths(fileCount – 1)

‘ 3. Visio図面を順次PDFエクスポート
Dim visApp As Visio.Application
Set visApp = New Visio.Application
visApp.Visible = False
visApp.ScreenUpdating = False
visApp.AlertsEnabled = False ‘ 警告ダイアログを完全にシャットアウト

Dim i As Long
Dim doc As Visio.Document
Dim tempPdf As String

For i = 0 To fileCount – 1
tempPdf = targetFolder & “temp_” & i & “_” & Format(Now, “hhmmss”) & “.pdf”
tempPdfPaths(i) = tempPdf

‘ ドキュントのサイレントオープン
Set doc = visApp.Documents.OpenEx(fileNames(i), visOpenRO + visOpenHidden)

‘ PDFエクスポート (FixedFormat)
‘ visFixedFormatPDF = 1
doc.ExportAsFixedFormat 1, tempPdf, 1, 0 ‘ 1:pxls/vector調整, 0:全ページ

doc.Close
Set doc = Nothing
Next i

‘ Visioアプリの終了
visApp.Quit
Set visApp = Nothing

‘ 4. Acrobat APIを用いたPDF結合の核心部
Dim acroApp As Object
Dim basePDDoc As Object
Dim insertPDDoc As Object

Set acroApp = CreateObject(“AcroExch.App”)
Set basePDDoc = CreateObject(“AcroExch.PDDoc”)

‘ 1つ目のPDFをベースドキュメントとしてオープン
If Not basePDDoc.Open(tempPdfPaths(0)) Then
Err.Raise 9999, , “ベースPDFのオープンに失敗しました: ” & tempPdfPaths(0)
End If

‘ 2つ目以降のPDFをループで結合
For i = 1 To fileCount – 1
Set insertPDDoc = CreateObject(“AcroExch.PDDoc”)
If Not insertPDDoc.Open(tempPdfPaths(i)) Then
Err.Raise 9999, , “結合用PDFのオープンに失敗しました: ” & tempPdfPaths(i)
End If

‘ basePDDocの末尾に挿入
‘ InsertPages(挿入先ページ番号(0開始), 挿入元PDDoc, 挿入元開始ページ, 挿入枚数, しおり保持フラグ)
Dim lastPageNum As Long
lastPageNum = basePDDoc.GetNumPages()

If Not basePDDoc.InsertPages(lastPageNum – 1, insertPDDoc, 0, insertPDDoc.GetNumPages(), True) Then
Err.Raise 9999, , “PDFのページ結合に失敗しました (Index: ” & i & “)”
End If

insertPDDoc.Close
Set insertPDDoc = Nothing
Next i

‘ 既存の出力ファイルがある場合は削除(エラー防止)
If fso.FileExists(outputPdfPath) Then
fso.DeleteFile outputPdfPath, True
End If

‘ 結合結果を保存
If Not basePDDoc.Save(1, outputPdfPath) Then ‘ 1: PDSaveFull
Err.Raise 9999, , “結合済みPDFの保存に失敗しました。”
End If

‘ クリーンアップ
basePDDoc.Close
Set basePDDoc = Nothing
acroApp.Exit
Set acroApp = Nothing

‘ 5. 中間生成した一時PDFファイルの削除
For i = 0 To fileCount – 1
If fso.FileExists(tempPdfPaths(i)) Then
fso.DeleteFile tempPdfPaths(i), True
End If
Next i

Set fso = Nothing

MsgBox “PDFの結合が正常に完了しました!” & vbCrLf & “出力先: ” & outputPdfPath, vbInformation, “処理完了”
Exit Sub

ErrorHandler:
‘ 異常終了時のクリーンアップとフォールバック
MsgBox “エラーが発生しました。” & vbCrLf & “記述: ” & Err.Description, vbCritical, “致命的エラー”

On Error Resume Next
If Not doc Is Nothing Then doc.Close False
If Not visApp Is Nothing Then visApp.Quit
If Not basePDDoc Is Nothing Then basePDDoc.Close
If Not insertPDDoc Is Nothing Then insertPDDoc.Close
If Not acroApp Is Nothing Then acroApp.Exit

‘ 一時ファイルの残骸掃除
Dim cleanupIdx As Long
For cleanupIdx = 0 To UBound(tempPdfPaths)
If tempPdfPaths(cleanupIdx) <> “” Then
If fso.FileExists(tempPdfPaths(cleanupIdx)) Then fso.DeleteFile tempPdfPaths(cleanupIdx), True
End If
Next cleanupIdx
End Sub

‘ 簡易文字列配列ソート用ヘルパー
Private Sub SortStringArray(ByRef arr() As String)
Dim i As Long, j As Long
Dim temp As String
For i = LBound(arr) To UBound(arr) – 1
For j = i + 1 To UBound(arr)
If arr(i) > arr(j) Then
temp = arr(i)
arr(i) = arr(j)
arr(j) = temp
End If
Next j
Next i
End Sub

—

3. チーフアーキテクトが教える実装の勘所

上記のコードがなぜ「実戦的」なのか、プロフェッショナルとしての設計ポイントを3点解説する。

① `Visio.Application` の完全非表示とイベント抑制

`visApp.Visible = False` とするのは基本中の基本だが、それだけでは不十分だ。
`visApp.AlertsEnabled = False` を指定することで、ファイル読み込み時のリンク切れ警告や、既存PDF上書き確認といった人間を待ち受けるダイアログを完全に根絶している。これにより、完全な無人バッチ処理(タスクスケジューラーからのキック等)が可能になる。

② レイトバインディング(CreateObject)の採用

AcrobatのCOMコンポーネント(`AcroExch.PDDoc`など)は、PCにインストールされているAcrobatのバージョン(DC, 2020, 2024など)やエディションによってタイプライブラリのGUIDが微妙に異なる。
あえて参照設定を行わず、`CreateObject` によるレイトバインディングを採用することで、環境依存による「コンパイルエラー: 型が見つかりません」を完全に回避している。

③ 徹底的なエラーハンドリングとゾンビプロセス防止

COMオブジェクトを操作するVBAにおいて、途中でエラーが起きた場合にVisioやAcrobatのプロセスがタスクマネージャーに残骸として残り続ける(ゾンビ化)のはよくあるバグだ。
このコードでは `On Error GoTo ErrorHandler` を経由させ、例外発生時であっても確実に各オブジェクトの `.Close` と `.Quit`、そして変数の解放 (`Set xxx = Nothing`) を行う。また、HDDを汚さないように、生成した一時PDF (`temp_.pdf`) も必ず最後に掃除するクリーンな設計だ。

—

4. 応用展開:さらなる高みを目指すエンジニアへ

このコードをベースに、現場のニーズに合わせて以下の拡張を行うとさらに強力なツールになる。

  • データベース(Access / SQL Server / Excel管理台帳)との連携

フォルダ内のファイルを全取得するのではなく、DBの「図面発行ステータス = 承認済」のレコードからファイルパスを動的に配列に組み立てて渡す。

  • 図面の表題欄(Shape Data)の動的書き換え

PDF化する直前に、VBAからVisioの特定のシェイプ(図番や改訂番号のプロパティ)を書き換えてからPDFに出力する。

定型業務の自動化は、単なる手抜きではなく「ヒューマンエラーという最大のバグをシステムで排除する」ためのエンジニアリングだ。
ぜひ自社の環境にこのアーキテクチャを組み込み、圧倒的な業務効率化を達成してほしい。

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