【編集工数監査】総編集時間抽出マクロによるプレゼンテーション・ガバナンスの極限自動化
企業内におけるPowerPointの利用実態は、多くの場合「ブラックボックス」となっている。提出されたスライドの裏側で、どれだけの人間が、何時間を費やしてその成果物にたどり着いたのか。マネジメント層やシステム管理者が把握する手段は、長らく属人的な「自己申告」に依存してきた。
しかし、PowerPointのファイル本体には、OSレベルおよびアプリケーション層のメタデータとして、「総編集時間(Total Editing Time)」、「作成日時」、「最終更新者」などの極めて重要な監査証跡が刻印されている。
本稿では、PowerPoint VBAのオブジェクトモデルの深部をハックし、`BuiltInDocumentProperties`から正確な工数データを抽出し、プロジェクト進捗管理用のCSVログとして自動出力する、シニアエンジニア向けの実戦的監査スクリプトを提示する。
—
1. オブジェクトモデルの罠:`BuiltInDocumentProperties` の本質
PowerPoint VBAにおいて、ドキュメントのメタデータにアクセスする際、多くの初学者は `Presentation.BuiltInDocumentProperties` を安易に呼び出す。しかし、ここにはOffice VBA特有の「COMの参照リーク」と「型安全性の欠如」という重大な罠が潜んでいる。
COMオブジェクトのライフサイクル管理
VBAの裏側では、`Application` から `Presentations`、そして個々の `Presentation` へとCOMのポインタが多重にチェーンされている。特にプロパティコレクションの走査において、変数の解放(`Set obj = Nothing`)を怠ると、ExcelやPowerPointのプロセス内にゾンビオブジェクトが残留し、メモリリークやファイルのロック不具合を引き起こす。
また、`BuiltInDocumentProperties` の各インデックスやプロパティ名は、ドキュメントのステータス(保存状態やOfficeの言語バージョン)によって動的にエラー(Type Mismatchなど)を吐く。実戦的なコードでは、必ず `On Error Resume Next` による防御的プログラミングと、厳密な型キャストが要求される。
—
2. 実装:編集工数監査・CSV自動生成エンジン
以下に、指定フォルダ内のPowerPointファイルを再帰的あるいは一括で走査し、総編集時間(分単位)、スライド枚数、最終更新者を抽出し、UTF-8(BOM付き)のCSVログとして出力するプロダクションコードを示す。
Option Explicit
‘ —————————————————————–
‘ 監査ログ出力メインプロシージャ
‘ —————————————————————–
Public Sub AuditPresentationWorkload()
Dim fso As Object
Dim targetFolder As Object
Dim targetFile As Object
Dim appPPT As PowerPoint.Application
Dim targetPres As PowerPoint.Presentation
Dim csvFilePath As String
Dim fileNum As Integer
Dim folderPath As String
‘ 監査対象のルートフォルダを指定(環境に合わせて変更すること)
folderPath = “C:\ProjectA\Deliverables\”
Set fso = CreateObject(“Scripting.FileSystemObject”)
If Not fso.FolderExists(folderPath) Then
MsgBox “指定されたフォルダが存在しません: ” & folderPath, vbCritical, “パス異常”
Exit Sub
End If
Set targetFolder = fso.GetFolder(folderPath)
‘ 出力先CSVパスの設定(デスクトップにタイムスタンプ付きで出力)
csvFilePath = CreateObject(“WScript.Shell”).SpecialFolders(“Desktop”) & _
“\PPT_Workload_Audit_” & Format(Now, “yyyymmdd_hhnnss”) & “.csv”
fileNum = FreeFile
Open csvFilePath For Output As #fileNum
‘ CSVヘッダーの書き込み
Print #fileNum, “FileName,TotalEditingTime_Min,SlideCount,LastAuthor,LastSaveTime,FilePath”
‘ PowerPointインスタンスの非表示起動(パフォーマンスとUXの最適化)
Set appPPT = New PowerPoint.Application
appPPT.Visible = msoFalse
‘ フォルダ内ファイルの走査
For Each targetFile In targetFolder.Files
If LCase(fso.GetExtensionName(targetFile.Name)) = “pptx” Or _
LCase(fso.GetExtensionName(targetFile.Name)) = “ppt” Then
On Error GoTo ErrorHandler
‘ 読み取り専用でオープン(ファイルロックの回避)
Set targetPres = appPPT.Presentations.Open(FileName:=targetFile.Path, _
ReadOnly:=msoTrue, _
Untitled:=msoFalse, _
WithWindow:=msoFalse)
‘ メタデータの抽出と出力
Call WriteAuditRecord(fileNum, targetPres, targetFile)
‘ オブジェクトの明示的クローズと解放
targetPres.Close
Set targetPres = Nothing
On Error GoTo 0
End If
50:
Next targetFile
‘ クリーンアップ
Close #fileNum
appPPT.Quit
Set appPPT = Nothing
Set targetFolder = Nothing
Set fso = Nothing
MsgBox “監査ログの生成が完了しました。” & vbCrLf & “出力先: ” & csvFilePath, vbInformation, “監査完了”
Exit Sub
ErrorHandler:
‘ 破損ファイルやパスワード保護ファイルによる異常終了をブロック
Print #fileNum, “””” & targetFile.Name & “””,ERROR,ERROR,ERROR,ERROR,” & “””” & targetFile.Path & “”””
If Not targetPres Is Nothing Then
targetPres.Close
Set targetPres = Nothing
End If
Resume 50
End Sub
‘ —————————————————————–
‘ メタデータ抽出およびCSVレコード書き込みサブルーチン
‘ —————————————————————–
Private Sub WriteAuditRecord(ByVal fNum As Integer, ByVal pres As PowerPoint.Presentation, ByVal fileObj As Object)
Dim editingTime As Double
Dim slideCount As Long
Dim lastAuthor As String
Dim lastSaveTime As String
On Error Resume Next
‘ 総編集時間の取得 (BuiltInDocumentProperties(10) が “Total Editing Time”)
‘ 単位はミリ秒または分数で返るため、環境依存を考慮して安全に取得
editingTime = pres.BuiltInDocumentProperties(wdPropertyTimeTotal).Value
‘ 注: プロパティIDが存在しない場合のフォールバック
If Err.Number <> 0 Then
editingTime = 0
Err.Clear
End If
‘ スライド総数
slideCount = pres.Slides.Count
‘ 最終保存者
lastAuthor = pres.BuiltInDocumentProperties(wdPropertyAuthor).Value
If Err.Number <> 0 Then lastAuthor = “Unknown”
Err.Clear
‘ 最終保存日時
lastSaveTime = Format(pres.BuiltInDocumentProperties(wdPropertyLastSaveTime).Value, “yyyy/mm/dd hh:nn:ss”)
If Err.Number <> 0 Then lastSaveTime = “Unknown”
Err.Clear
On Error GoTo 0
‘ CSV行の構築(カンマやダブルクォーテーションのエスケープを考慮)
Print #fNum, Join(Array( _
“””” & fileObj.Name & “”””, _
Int(editingTime), _
slideCount, _
“””” & lastAuthor & “”””, _
“””” & lastSaveTime & “”””, _
“””” & fileObj.Path & “””” _
), “,”)
End Sub
—
3. チーフアーキテクトが解説するコードの急所
A. `ReadOnly:=msoTrue` と `WithWindow:=msoFalse` による無駄な描画コストの排除
大規模なプロジェクトでは数百ファイルのプレゼンテーションを一括処理することがある。ウィンドウを表示したまま(`WithWindow:=msoTrue`)実行すると、PowerPointのGUIレンダリング処理が走り、実行速度が数倍〜数十倍に低下する。
さらに、ファイルが他者に編集中である場合に備え、必ず `ReadOnly:=msoTrue` を指定することで、共有違反(Permission Denied)によるマクロのクラッシュを防ぐ。
B. `BuiltInDocumentProperties` のインデックスの闇
Officeのバージョンや言語パック(日本語版/英語版)によって、組み込みドキュメントプロパティの `.Item(“名前”)` によるアクセスは不安定になることがある。定数(`wdPropertyTimeTotal` 等、Wordの定数と共通)を用いるか、エラーハンドリングを厳密に挟むことで、レガシーな `.ppt` ファイルから最新の `.pptx` ファイルまで、パースエラーを起こさずに吸収する設計としている。
※なお、総編集時間の正確な単位はOfficeの仕様上「分(Minutes)」で格納される。
C. 堅牢なエラーハンドリング(防御的プログラニング)
組織内で共有されるプレゼンテーションの中には、パスワード保護されたファイルや、ファイル構造自体が破損しているものが混入している。これらを処理した瞬間にVBAは実行時エラーで停止する。
本スクリプトでは `On Error GoTo ErrorHandler` をループ内に配置し、異常ファイルを検知した場合はログに `ERROR` を出力した上で、即座に次のファイルへフォワードする無停止アーキテクチャ(Fault-Tolerant Design)を採用している。
—
4. システム間連携への展開:PowerShellやCI/CDパイプラインへの統合
このVBAマクロ単体でもローカル監査には十分機能するが、シニアエンジニアであればこれを「自動化パイプライン」の一部として組み込む視点を持つべきだ。
例えば、WindowsタスクスケジューラからVBAを直接キックする代わりに、PowerShellからCOM経由でPowerPointを操作し、同様のメタデータを抽出してSQLデータベースやREST APIへ直接送信する構成へと昇華させれば、人間の手動介入を完全に排除した「全社工数ガバナンス・ダッシュボード」が完成する。
PowerShellによるPowerPointメタデータ抽出の概念スクリプト
$ppt = New-Object -ComObject PowerPoint.Application
$pres = $ppt.Presentations.Open(“C:\ProjectA\Deliverables\spec.pptx”, $true, $true, $false)
$editTime = $pres.BuiltInDocumentProperties.Item(10).Value
[System.Runtime.InteropServices.Marshal]::ReleaseComObject($pres) | Out-Null
$ppt.Quit()
[System.Runtime.InteropServices.Marshal]::ReleaseComObject($ppt) | Out-Null
[GC]::Collect()
VBAであれPowerShellであれ、本質は「Officeドキュメントの内部構造(メタデータ)を信頼し、客観的なファクトベースでプロジェクト管理を行う」ことにある。属人化しがちなドキュメント作成工数の可視化に、本アーキテクチャをぜひ導入してほしい。
