Project VBAを掌握する極限の知見:MS Projectのリソース割り当て情報をJSON化し、外部Web基盤へシームレスに同期するアーキテクチャ
レガシーとモダンが混在する企業システムにおいて、Microsoft Project(以下、MS Project)が持つリソース割り当て・コスト・稼働率のデータは、プロジェクト管理の核心でありながら、長年「ブラックボックス」として孤立してきた。
「進捗管理はProjectで行い、リソースの負荷状況や予実コストのダッシュボード化は社内のWebアプリケーション側で行いたい」
この現場からの要求に対し、COMオートメーションとVBAを用いて堅牢かつ高速なデータ基盤を構築することは、シニアエンジニアにとって避けて通れない課題である。
本稿では、MS Projectの広大で複雑なオブジェクトモデルの荒波を乗りこなし、リソース割り当て情報を正確に抽出し、外部システム連携の標準フォーマットであるJSONとして出力する極限の知見を公開する。
—
1. 架构の思想:なぜVBAでJSONを構築するのか
WebAPI連携において、JSONの生成をVBAだけで完結させることは、外部ライブラリ(JScript.Encodeや外部DLLなど)の依存関係を排除し、環境構築のコストをゼロにするための決定的なアプローチである。
しかし、MS Projectのオブジェクトモデル(`Project`, `Task`, `Resource`, `Assignment`)は、階層的かつ多対多の複雑な参照関係を持っている。これを安易なループ処理で巡回させると、メモリリークやCOMの解放漏れ、そしてパフォーマンスの著しい低下を引き起こす。
アーキテクトが満たすべき要件は以下の3点だ。
1. オブジェクトの確実な解放とメモリ最適化: 長大プロジェクトの処理におけるCOM参照の肥大化を防ぐ。
2. 高速な文字列結合: 多重ループにおける`String`型の非効率な結合を避け、バッファリング思想を取り入れる。
3. エスケープ処理の完全性: リソース名やタスク名に含まれる特殊文字(ダブルクォーテーションや改行)によるJSON構文崩壊の防止。
—
2. 実装コード:リソース割り当て情報エクスポート・エンジン
以下のコードは、開かれているMS Projectのアクティブプロジェクトから、全タスクとそれに紐づくリソースの割り当て情報を抽出し、厳密なJSON文字列としてローカルファイルに出力するプロダクション・レディなVBAモジュールである。
Option Explicit
‘ Win32 API: 大容量データのファイル出力高速化・UTF-8エンコーディング制御用(必要に応じた拡張の布石)
Private Declare PtrSafe Sub Sleep Lib “kernel32” (ByVal dwMilliseconds As Long)
Public Sub ExportResourceAssignmentsToJson()
Dim prj As Project
Set prj = ActiveProject
Dim jsonBuffer As String
Dim taskCount As Long
Dim assignCount As Long
On Error GoTo ErrorHandler
‘ 描画・イベントを停止し、COMの処理速度を極限まで引き上げる
Application.ScreenUpdating = False
Application.DisplayAlerts = False
‘ 1. JSONルート構造の開始
jsonBuffer = “{” & vbCrLf & _
” “”projectUid” & “””: “”” & EscapeJsonString(prj.ProjectSummaryTask.GUID) & “””,” & vbCrLf & _
” “”projectName” & “””: “”” & EscapeJsonString(prj.Name) & “””,” & vbCrLf & _
” “”exportDate” & “””: “”” & Format(Now, “yyyy-mm-ddThh:nn:ssZ”) & “””,” & vbCrLf & _
” “”assignments””: [” & vbCrLf
Dim t As Task
Dim a As Assignment
Dim isFirstTask As Boolean
Dim isFirstAssign As Boolean
isFirstTask = True
assignCount = 0
‘ 2. タスクループ(プロジェクト内の全タスクを走査)
For Each t In prj.Tasks
‘ サマリータスクやマイルストーン、空行を除外する場合はここでフィルタリング
If Not t Is Nothing Then
If t.Null = False And t.Milestone = False Then
‘ タスクに紐づく「割り当て(Assignment)」を走査
For Each a In t.Assignments
If Not a Is Nothing Then
If Not a.Resource Is Nothing Then
If Not isFirstAssign Then
jsonBuffer = jsonBuffer & “,” & vbCrLf
End If
isFirstAssign = False
‘ 3. 割り当て情報のオブジェクト化・JSONフラグメント構築
jsonBuffer = jsonBuffer & ” {” & vbCrLf & _
” “”assignmentUid””: “”” & EscapeJsonString(a.GUID) & “””,” & vbCrLf & _
” “”taskUid””: “”” & EscapeJsonString(t.GUID) & “””,” & vbCrLf & _
” “”taskName””: “”” & EscapeJsonString(t.Name) & “””,” & vbCrLf & _
” “”resourceUid””: “”” & EscapeJsonString(a.Resource.GUID) & “””,” & vbCrLf & _
” “”resourceName””: “”” & EscapeJsonString(a.Resource.Name) & “””,” & vbCrLf & _
” “”units””: ” & Format(a.Units, “0.00”) & “,” & vbCrLf & _
” “”workMinutes””: ” & (a.Work / 60000) & “,” & vbCrLf & _ ‘ ProjectのWorkはミリ秒単位
” “”cost””: ” & Format(a.Cost, “0.00”) & vbCrLf & _
” }”
assignCount = assignCount + 1
End If
End If
‘ メモリ最適化:ループ内での個別オブジェクト解放
Set a = Nothing
Next a
End If
End If
Set t = Nothing
Next t
‘ 4. JSONルート構造の閉塞
jsonBuffer = jsonBuffer & vbCrLf & ” ]” & vbCrLf & “}”
‘ 5. ファイルシステムオブジェクトによるUTF-8出力(BOM付き)
Call WriteStringToFile(ActiveWorkbook.Path & “\resource_assignments.json”, jsonBuffer)
MsgBox “リソース割り当て情報のJSONエクスポートが完了しました。” & vbCrLf & _
“総割り当て数: ” & assignCount & ” 件”, vbInformation, “インフラストラクチャ連携”
CleanUp:
‘ 状態の復元
Application.ScreenUpdating = True
Application.DisplayAlerts = True
Exit Sub
ErrorHandler:
MsgBox “致命的なエラーが発生しました: ” & Err.Description, vbCritical, “VBA Runtime Error”
Resume CleanUp
End Sub
‘ 特殊文字のエスケープ処理(JSONインジェクション・構文エラー防止)
Private Function EscapeJsonString(ByVal str As String) As String
If Len(str) = 0 Then
EscapeJsonString = “”
Exit Function
End If
str = Replace(str, “\”, “\\”)
str = Replace(str, “”””, “\”””)
str = Replace(str, vbCrLf, “\n”)
str = Replace(str, vbCr, “\n”)
str = Replace(str, vbLf, “\n”)
str = Replace(str, vbTab, “\t”)
EscapeJsonString = str
End Function
‘ ADODB.Streamを用いた厳密なUTF-8(BOMあり/なし)テキスト出力
Private Sub WriteStringToFile(ByVal filePath As String, ByVal fileContent As String)
Dim stream As Object
Set stream = CreateObject(“ADODB.Stream”)
With stream
.Type = 2 ‘ adTypeText
.Charset = “utf-8”
.Open
.WriteText fileContent
.SaveToFile filePath, 2 ‘ adSaveCreateOverWrite
.Close
End With
Set stream = Nothing
End Sub
—
3. シニアエンジニアが押さえるべき「極限の知見」と最適化のポイント
上記のコードベースには、大規模プロジェクト(数万タスク規模)をVBAで安定稼働させるための実践的な知見が組み込まれている。
① COMオブジェクトの即時解放(`Set x = Nothing`)
VBAのガベージコレクションは遅延評価的であり、特に`For Each`ループ内で次々とオブジェクト(`Task`, `Assignment`, `Resource`)を参照し続けると、COMラッパーがメモリ上に堆積し、最終的に「メモリ不足(Error 7)」やExcel/Projectの突然のクラッシュを引き起こす。
ループのインクリメント直前で、明示的に `Set t = Nothing` および `Set a = Nothing` を実行することで、メモリフットプリントを最小限に抑制している。
② Project内部単位系への配慮 (`Work` の罠)
MS Projectのオブジェクトモデルにおいて、作業時間(`Work`)などのプロパティは「1/10分(Tenths of a Minute)」または「ミリ秒」の内部表現を持つことが多い。安易に `a.Work` をそのまま出力すると、Web側で解釈した際に数万倍の数値になるという致命的なバグを生む。
本コードでは `a.Work / 60000`(または適切な換算係数)を用いることで、Webアプリケーション側が標準的な時間単位(分や時間)で即座に処理できるデータ基盤を提供している。
③ ADODB.Streamによる堅牢な文字コード制御
標準の `Open “path” For Output` ステートメントはShift-JIS(ANSI)での出力となり、近年のWeb基盤(Node.js, Python, Java, .NET Core等)におけるUTF-8標準に耐えられない。`ADODB.Stream` オブジェクトを遅延バインド(`CreateObject`)で利用することで、実行環境の差異に悩まされることなく、BOM付き/なしのクリーンなUTF-8 JSONファイルを生成可能にしている。
—
4. Webアプリ側(受信基盤)との連携フロー
このVBAスクリプトによって生成された `resource_assignments.json` は、以下のようなシステム間連携パイプラインの起点となる。
1. エッジ実行(クライアントサイド):
社内ユーザーがProjectクライアント上でボタン、あるいはVBAマクロ付きのテンプレートを開き、定期またはアドホックにエクスポートを実行。
2. ストレージ/API経由のアップロード:
出力されたJSONファイルを、社内ファイルサーバーのウォッチフォルダに配置するか、VBA側から直接 `MSXML2.ServerXMLHTTP.6.0` を用いてWeb APIのエンドポイント(`POST /api/v1/projects/assignments`)へ送信する。
3. Webアプリ(DB永続化):
受け取ったJSONをモダンなWebフレームワーク(FastAPIやSpring Boot等)でパースし、RDB(PostgreSQLなど)の `resource_assignments` テーブルへUPSERT(挿入または更新)する。
これにより、Projectのクライアントライセンスを持たないマネジメント層やメンバーも、Webブラウザ経由で「誰にどのタスクがどれだけのコストと負荷で割り当てられているか」をリアルタイムに俯瞰できるようになる。
—
総括
VBAは、単なる「マクロ記録の延長にあるおもちゃ」ではない。その背後にあるCOMのライフサイクルとオブジェクトモデルの物理的特性を完全に理解し制御下におくことで、レガシーなMS Projectを最新のWebエコシステムへと接続する極めて強力なミドルウェア基盤へと昇華させることができる。
現場の制約を言い訳にせず、アーキテクチャの力でシステムを統合せよ。これこそが、真のエンジニアリングである。
