【極限の堅牢性】ネットワーク保存の恐怖を克服する。Windows APIを活用した「絶対に壊さない」PowerPoint保存術
こんにちは。自動化エンジニアの現場へようこそ。
「マクロを組んで、大量のプレゼン資料をNAS(ネットワーク上のストレージ)に一括保存する仕組みを作った。完璧だ!」……そう思っていた矢先、「保存中にネットワークが瞬断した」「書き込み中にディスク容量が足りなくなった」という理由で、作成したファイルが次々と破損(ファイルが壊れて開けない状態)していく。
そんな悪夢に震えたことはありませんか?
実は、これこそが「マクロの記録」レベルから脱却し、真の「業務自動化エンジニア」へとステップアップするための、非常に重要な壁なのです。
今日は、Windows APIという強力な武器を使い、保存先のストレージ状態を事前に「予知」して、エラーを未然に防ぐプロ仕様の堅牢な保存ロジックを伝授します。ここをマスターすれば、あなたの作るツールは「動けばいい」レベルから「現場で信頼される」レベルへと進化します。
なぜ、標準のVBAだけでは「不十分」なのか?
通常、VBAでファイルを保存する際は `ActivePresentation.SaveAs` を使います。しかし、これには大きな弱点があります。
1. 「事後報告」であること: 保存処理を開始してから、エラー(ディスクフルや切断)が発生して初めて「失敗しました」と教えてくれます。
2. 「中途半端な書き込み」のリスク: PowerPointのファイル形式(.pptx)は、実は内部的に「複数のXMLファイルをまとめたZIP形式」の構造をしています。保存中にエラーが起きると、このZIP構造が不完全なまま閉じられてしまい、ファイル自体が修復不可能な状態で破損するのです。
プロのエンジニアは、「実行する前に、実行できる環境かどうかを判定する」という思想(Pre-flight Check)を必ず持ちます。そのために、Windows OSが直接持っているストレージ情報にアクセスする「Windows API」を使用します。
武器の準備:Windows API `GetDiskFreeSpaceEx`
今回使用するのは、`GetDiskFreeSpaceEx` というWindows APIです。これは、指定したパス(ネットワークドライブを含む)の「合計容量」「空き容量」「ユーザーに割り当てられた利用可能な容量」を、バイト単位で正確に取得できる関数です。
VBAの標準機能では、ネットワークドライブの正確な空き容量を取得するのは至難の業ですが、これを使えば一発です。
実装コード:堅牢な保存シーケンス
それでは、実際に使えるコードを見ていきましょう。64bit版のOfficeでも動作するように `PtrSafe` 属性を付与した、現代的な設計にしています。
Option Explicit
' ===========================================================================
' Windows APIの宣言
' GetDiskFreeSpaceEx: 指定したディレクトリの空き容量を取得する
' ===========================================================================
If VBA7 Then
' 64bit/32bit両対応の宣言
Private Declare PtrSafe Function GetDiskFreeSpaceEx Lib "kernel32" Alias "GetDiskFreeSpaceExA" ( _
ByVal lpDirectoryName As String, _
ByVal lpFreeBytesAvailableToCaller As Currency, _
ByVal lpTotalNumberOfBytes As Currency, _
ByVal lpTotalNumberOfBytesAvailable As Currency) As Long
Else
' 古い32bit Office用の宣言
Private Declare Function GetDiskFreeSpaceEx Lib "kernel32" Alias "GetDiskFreeSpaceExA" ( _
ByVal lpDirectoryName As String, _
ByVal lpFreeBytesAvailableToCaller As Currency, _
ByVal lpTotalNumberOfBytes As Currency, _
ByVal lpTotalNumberOfBytesAvailable As Currency) As Long
End If
' ===========================================================================
' メイン処理:安全な保存を実行する
' ===========================================================================
Sub RobustSaveProcedure()
Dim targetPath As String
Dim targetFolder As String
Dim requiredSpace As Double
Dim availableSpace As Double
Dim safetyBuffer As Double
' --- 設定エリア ---
' 保存したいフルパスを指定
targetPath = "Z:\SharedFolder\Project_Presentation.pptx" ' ネットワークドライブの例
' 安全のためのバッファ(例:50MB。ファイルサイズに加えてこれだけの余裕を持つ)
safetyBuffer = 50 * 1024 * 1024
' ------------------
' 1. 保存先フォルダの抽出
targetFolder = Left(targetPath, InStrRev(targetPath, "\"))
On Error GoTo ErrorHandler
' 2. 現在のプレゼンテーションの概算サイズを取得(メモリ上のサイズ)
' ※厳密なファイルサイズではないが、判断材料として使用
requiredSpace = ActivePresentation.Saved = True Then 0 Else ActivePresentation.FullSize ' (概念的な考え方)
' 実際には、保存後のファイルサイズを予測するのは難しいため、
' 「現在のファイルサイズ + バッファ」を基準にします。
' ここでは単純化のため、ファイルサイズを直接取得するロジックを想定。
requiredSpace = GetCurrentFileSize(targetPath) + safetyBuffer
' 3. APIを使って空き容量を確認
availableSpace = GetFreeSpace(targetFolder)
Debug.Print "要求容量(バッファ込): " & Format(requiredSpace / 1024 / 1024, "0.00") & " MB"
Debug.Print "利用可能容量: " & Format(availableSpace / 1024 / 1024, "0.00") & " MB"
' 4. 検証:空き容量が足りているか?
If availableSpace < requiredSpace Then
MsgBox "【警告】保存先の空き容量が不足しています!" & vbCrLf & _
"必要目安: " & Format(requiredSpace / 1024 / 1024, "0.00") & " MB" & vbCrLf & _
"現在の空き: " & Format(availableSpace / 1024 / 1024, "0.00") & " MB", _
vbCritical, "保存中止"
Exit Sub
End If
' 5. 保存実行
Application.StatusBar = "保存中... しばらくお待ちください。"
ActivePresentation.SaveAs targetPath
Application.StatusBar = False
MsgBox "保存が正常に完了しました。", vbInformation, "成功"
Exit Sub
ErrorHandler:
Application.StatusBar = False
MsgBox "予期せぬエラーが発生しました: " & Err.Description, vbCritical, "エラー"
End Sub
' ===========================================================================
' 補助関数:空き容量を取得する
' ===========================================================================
Private Function GetFreeSpace(ByVal folderPath As String) As Double
Dim freeBytes As Currency
Dim totalBytes As Currency
Dim totalAvailable As Currency
Dim result As Long
' API呼び出し
result = GetDiskFreeSpaceEx(folderPath, freeBytes, totalBytes, totalAvailable)
If result <> 0 Then
' Currency型をDoubleに変換して返す
GetFreeSpace = CDbl(freeBytes)
Else
' APIが失敗した場合は0を返す(パスが不正、ネットワーク切断など)
GetFreeSpace = 0
End If
End Function
' ===========================================================================
' 補助関数:既存ファイルのサイズを取得する
' ===========================================================================
Private Function GetCurrentFileSize(ByVal filePath As String) As Double
Dim fso As Object
Set fso = CreateObject("Scripting.FileSystemObject")
If fso.FileExists(filePath) Then
GetCurrentFileSize = fso.GetFile(filePath).Size
Else
GetCurrentFileSize = 0
End If
Set fso = Nothing
End Function
ここがプロの視点!解説と重要ポイント
このコードをただコピーするだけでなく、なぜこのように書いたのか、その「意図」を理解してください。
1. `Currency` 型による精度の担保
APIの引数に `Currency` 型を使っていることに気づきましたか?
容量は「バイト」単位で扱われます。数ギガバイト、数テラバイトという巨大な数値を扱う際、通常の `Integer` や `Long` では桁溢れ(オーバーフロー)を起こしてしまいます。`Currency` 型は非常に大きな整数を正確に保持できるため、ストレージ容量の計算には最適なのです。
2. 「安全バッファ」の設計
コード内の `safetyBuffer` が、エンジニアとしての腕の見せ所です。
「ファイルサイズが100MBだから、空き容量が100MBあればいい」と考えるのは初学者です。プロは、「OSの作業領域や、書き込み中のテンポラリファイル作成のために、必ず余裕(バッファ)を持たせる」ように設計します。今回は50MBとしていますが、環境に合わせて調整してください。
3. ネットワーク切断への耐性
もしネットワークが切断されていた場合、`GetDiskFreeSpaceEx` はエラー(結果が0)を返します。このコードでは、その場合に `availableSpace` を `0` として扱うことで、「容量不足」として安全に処理を中断させています。これにより、壊れるリスクのある `SaveAs` を実行させない仕組みになっています。
4. 64bit対応 (`#If VBA7`)
現代のビジネス環境では、Officeは64bit版が主流です。`Declare PtrSafe` を使うことで、32bit/64bitどちらの環境でもエラーなく動作する「ポータビリティ(移植性)」の高いコードになっています。
まとめ:一歩先の自動化へ
いかがでしたでしょうか?
「マクロの記録」で得た知識は、いわば「歩き方」を覚えた段階です。しかし、今回学んだように、「システムの裏側(API)を覗き、リスクを事前に予測し、安全策を講じる」という思考プロセスこそが、プロフェッショナルなエンジニアに求められる本質です。
この「堅牢な設計」という考え方を、PowerPoint以外のExcelやAccessのマクロにも応用してみてください。あなたの作るツールが、同僚やクライアントから「この人のツールは絶対に壊れないから安心だ」と信頼されるようになるはずです。
ここをクリアすれば、PowerPoint VBAの基本はバッチリですよ。自信を持って、次の自動化に挑戦してくださいね!
