【テクニカル・上級編】【中級者向け】FSOを活用したプロジェクトファイル検索と一括オープンツール – Project VBA解析バイブル

スポンサーリンク

Project VBAの深淵:FSOを用いたプロジェクト一括操作の極致

プロジェクト管理において、Project VBAは強力だが、そのメモリ管理とオブジェクトのライフサイクルは、まさに「地雷原」である。数千のMPPファイルを処理する際、安易なループ処理はメモリリークを招き、最悪の場合、Projectアプリケーションのハングアップを引き起こす。

本稿では、FileSystemObject (FSO) を駆使し、再帰処理によるファイル検索、そしてメモリ消費を最小限に抑えた「堅牢な一括処理アーキテクチャ」の設計思想を解き明かす。

1. なぜ「FSO」なのか:Win32 APIとの距離感

VBA標準の `Dir` 関数は、再帰処理においてスタックを汚しやすく、マルチスレッドに近い複雑なタスクには向かない。また、Windows API (`FindFirstFile` / `FindNextFile`) を直接叩くアプローチは確かに高速だが、保守性を犠牲にする。

我々アーキテクトがFSOを選ぶ理由は、その「オブジェクト指向的な階層構造」と、COMインターフェースによる安定性にある。メモリリークを恐れるのであれば、重要なのは「いかに早く書くか」ではなく、「いかに確実かつ局所的にメモリを解放するか」だ。

2. 堅牢なファイル検索・オープンエンジンの実装

以下のコードは、再帰的探索とProjectのアプリケーションインスタンスを制御するテンプレートである。ポイントは、`DoEvents`によるメッセージポンプの解放と、`Set = Nothing`によるオブジェクトの生存期間(ライフサイクル)の厳格な管理だ。

Option Explicit

‘ 参照設定: Microsoft Scripting Runtime (scrrun.dll)
‘ 参照設定: Microsoft Project 16.0 Object Library

Public Sub BatchProcessProjectFiles(ByVal folderPath As String)
Dim fso As Object
Set fso = CreateObject(“Scripting.FileSystemObject”)

Dim rootFolder As Object
Set rootFolder = fso.GetFolder(folderPath)

‘ 再帰処理開始
RecursiveFileProcess rootFolder, fso

‘ 明示的解放
Set rootFolder = Nothing
Set fso = Nothing
End Sub

Private Sub RecursiveFileProcess(ByVal folder As Object, ByVal fso As Object)
Dim file As Object
Dim subFolder As Object
Dim projApp As Object

‘ サブフォルダを再帰的に探索
For Each subFolder In folder.SubFolders
RecursiveFileProcess subFolder, fso
Next

‘ MPPファイルのみを処理
For Each file In folder.Files
If LCase(fso.GetExtensionName(file.Path)) = “mpp” Then
Set projApp = GetObject(, “MSProject.Application”)
‘ インスタンスがなければ新規作成(環境に応じて分岐)
If projApp Is Nothing Then Set projApp = CreateObject(“MSProject.Application”)

ProcessSingleProject file.Path, projApp

‘ 処理後のメモリ最適化
DoEvents
End If
Next
End Sub

Private Sub ProcessSingleProject(ByVal filePath As String, ByRef app As Object)
Dim prj As Project
On Error Resume Next

‘ ReadOnlyで開き、メモリへの負荷を最小限に抑える
Set prj = app.Projects.Open(filePath, ReadOnly:=True)

If Not prj Is Nothing Then
‘ ここにビジネスロジックを記述(ベースライン設定やデータ抽出など)
Debug.Print “Processing: ” & prj.Name

‘ 処理終了後、即座にクローズ
prj.Close pjDoNotSave
Set prj = Nothing
End If

On Error GoTo 0
End Sub

3. シニアエンジニアが意識すべき「隠れたコスト」

メモリの断片化と再起動の戦略

Project VBAのCOMオブジェクトは、連続してファイルを開閉し続けると、ヒープ領域にゴミが蓄積される。100ファイルを超えるバッチ処理を行う場合、`ProcessSingleProject` の中だけで完結させず、一定回数(例えば50ファイル)ごとにアプリケーションを `Quit` させ、プロセスを再起動する「リフレッシュ戦略」を組み込むべきだ。

ベースライン設定の罠

ベースラインをプログラムから設定する際、`BaselineSave` メソッドを多用すると、undoスタックが肥大化する。大量の変更を行う場合は、`Application.ScreenUpdating = False` を徹底し、最後に `CalculateAll` を呼び出すことで、描画コストと再計算コストを劇的に削減できる。

レガシー環境でのAPI活用

もし、ネットワークドライブ上の不安定なファイルにアクセスする場合、`WNetGetConnection` を使用してパスをUNC形式に正規化することを推奨する。ドライブレターに依存する処理は、環境移行時に必ず破綻する。

結論:コードは「状態」を管理する装置である

我々が書くコードは、単なる命令列ではない。Projectという巨大なCOMサーバーの「状態」を制御する管理術である。FSOでファイルを列挙し、Projectのオブジェクトモデルを流麗に使いこなす。この時、最も重要なのは「自分が作り出したオブジェクトを、責任を持って殺すこと」だ。

メモリリークを恐れるな。オブジェクトの生存期間を視覚化し、明示的に制御する。それこそが、伝説的なシステムを維持するための唯一の道である。

貴殿のシステムにおいて、このアーキテクチャが安定した自動化の一助とならんことを願う。次回の深掘りでは、プロジェクト間依存関係の動的解決と、XML形式を用いた高速エクスポート手法について触れる予定だ。

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