【テクニカル・上級編】Project VBAでリソースの顔写真や詳細プロファイルを外部連携する – Project VBA解析バイブル

スポンサーリンク

MS Project VBAを極限まで駆動する:社内DBとリソースプロファイルの完全同期アーキテクチャ

レガシーとモダンが交差する企業のシステム基盤において、Microsoft Projectは依然としてプロジェクト管理の要塞であり続けている。しかし、そのリソース管理機能は単体では硬直的であり、社内の人事データベースやスキル管理システムとの乖離を生みやすい。

特に、リソースの顔写真、専門性、コストレート、稼働率といった詳細プロファイルを、何百人規模のプロジェクト計画へ手動で同期させる作業は、エンジニアリングの観点から見れば悪夢でしかない。

今回は、Project VBAのオブジェクトモデルの深層に切り込み、外部データベース(SQL Server等)からリソース情報を高速かつ安全に取得し、MS Projectのカスタムフィールドへ流し込むための極限のシステム間連携アーキテクチャを解説する。

—

1. Projectリソース管理の構造的課題とアプローチ

MS Projectのリソースオブジェクト(`Resource`)は、Excelのセルとは異なる。COMオブジェクトのライフサイクル管理、プロジェクト全体の再計算(Calculation)のコスト、そしてローカルリソースプールとエンタープライズリソースプール(Enterprise Resource Pool: ERP)の差異を意識しなければ、メモリリークや深刻なパフォーマンス劣化を引き起こす。

今回のアーキテクチャでは、以下の要件を満たすVBAモジュールを構築する。

1. ADO(ActiveX Data Objects)による堅牢なDB接続(コネクションプーリングとタイムアウト制御)
2. プロジェクトの計算エンジンの一時停止(`Calculation`プロパティ操作による爆速化)
3. カスタムフィールド(Text1-30, Cost1-10など)への安全なマッピング
4. Win32 APIを活用した外部画像パスの検証とメモリ最適化

—

2. 実装コード:リソース同期エンジン

以下のコードは、単なるサンプルの域を超え、商用環境のタフな負荷に耐えうるエラーハンドリングとメモリ管理を実装したチーフアーキテクトモデルである。

Option Explicit

‘ Win32 API: 外部サーバーやローカルの画像ファイルが存在するか高速確認するため
Private Declare PtrSafe Function PathFileExists Lib “shlwapi.dll” Alias “PathFileExistsA” (ByVal pszPath As String) As Long

‘ =========================================================================
‘ 処理名: SyncResourceProfilesFromDB
‘ 概要 : 社内DBからリソース情報を取得し、Projectのリソースカスタムフィールドへ同期する
‘ =========================================================================
Public Sub SyncResourceProfilesFromDB()
Dim prj As Project
Set prj = ActiveProject

‘ パフォーマンス最優先: 画面描画と自動再計算を停止
Application.ScreenUpdating = False
Dim originalCalcMode As Long
originalCalcMode = Application.Calculation
Application.Calculation = pjCalculationManual

On Error GoTo ErrorHandler

‘ 1. DB接続設定 (ADO)
Dim conn As Object
Dim rs As Object
Dim connStr As String

Set conn = CreateObject(“ADODB.Connection”)
Set rs = CreateObject(“ADODB.Recordset”)

‘ ※環境に合わせて接続文字列を書き換えてください
connStr = “Provider=SQLOLEDB.1;Data Source=SRV-DB01;Initial Catalog=HR_Master;Integrated Security=SSPI;”
conn.ConnectionTimeout = 15
conn.CommandTimeout = 30
conn.Open connStr

‘ 2. リソース詳細を取得するクエリ (ID, 部署, スキル, 写真パス, 単位コスト)
Dim sql As String
sql = “SELECT EmployeeID, Department, CoreSkills, PhotoPath, StandardRate FROM View_ProjectResources”
rs.Open sql, conn, 1, 1 ‘ adOpenKeyset, adLockReadOnly

If rs.EOF Then
MsgBox “同期対象のデータがデータベースに存在しません。”, vbExclamation, “同期エラー”
GoTo Cleanup
End If

‘ ディクショナリを使ってDBデータをメモリ上にキャッシュ(O(1)検索のため)
Dim dictDB As Object
Set dictDB = CreateObject(“Scripting.Dictionary”)

Do While Not rs.EOF
Dim empID As String
empID = Trim(CStr(rs.Fields(“EmployeeID”).Value))

If Not dictDB.Exists(empID) Then
Dim profile(3) As Variant
profile(0) = rs.Fields(“Department”).Value # Text1に対応
profile(1) = rs.Fields(“CoreSkills”).Value # Text2に対応
profile(2) = rs.Fields(“PhotoPath”).Value # Text3に対応 (外部ビューア用パス)
profile(3) = rs.Fields(“StandardRate”).Value # Cost1に対応 (調整コスト)
dictDB.Add empid, profile
End If
rs.MoveNext
Loop

rs.Close

‘ 3. Project側のリソースを走査し、マッチするものを更新
Dim r As Resource
Dim updatedCount As Long
updatedCount = 0

For Each r In prj.Resources
If Not r Is Nothing Then
‘ リソースの「コード (Code)」または「イニシャル」を社員ID(EmployeeID)と見なす
Dim resourceKey As String
resourceKey = Trim(r.Code)

If dictDB.Exists(resourceKey) Then
Dim data As Variant
data = dictDB(resourceKey)

‘ カスタムフィールドへのマッピング
‘ Text1: 部署, Text2: スキル, Text3: 顔写真パス
r.Text1 = CStr(data(0))
r.Text2 = CStr(data(1))

‘ 外部ファイルパスの存在確認 (Win32 API)
Dim imgPath As String
imgPath = CStr(data(2))
If PathFileExists(imgPath) = 1 Then
r.Text3 = imgPath
Else
r.Text3 = “[NotFound]”
End If

‘ コスト情報の同期 (Cost1)
r.Cost1 = CDbl(data(3))

updatedCount = updatedCount + 1
End If
End If
Next r

‘ コミットとログ出力
MsgBox “リソースプロファイルの同期が完了しました。” & vbCrLf & _
“更新件数: ” & updatedCount & ” 件”, vbInformation, “同期完了”

Cleanup:
‘ 4. オブジェクトの明示的解放 (メモリ最適化の極意)
On Error Resume Next
If Not rs Is Nothing Then
If rs.State = 1 Then rs.Close
Set rs = Nothing
End If
If Not conn Is Nothing Then
If conn.State = 1 Then conn.Close
Set conn = Nothing
End If
Set dictDB = Nothing

‘ 計算モードと描画の復元
Application.Calculation = originalCalcMode
Application.ScreenUpdating = True
Exit Sub

ErrorHandler:
MsgBox “予期せぬエラーが発生しました。” & vbCrLf & _
“Error No: ” & Err.Number & vbCrLf & _
“Description: ” & Err.Description, vbCritical, “致命的エラー”
Resume Cleanup
End Sub

—

3. チーフアーキテクトが解説する「極限の知見」

上記のコードを実運用に耐えうるものにするため、シニアエンジニアが押さえておくべきアーキテクチャ上のポイントを深掘りする。

① スクリプティング・ディクショナリ(Dictionary)によるO(1)最適化

MS Projectの `Resources` コレクションは、数千件規模になるとループ処理の負荷が指数関数的に跳ね上がる。データベースから取得したレコードを毎回リソースと比較するような$O(N \times M)$のアルゴリズムは、システムをフリーズさせる原因となる。
一度DBの全レコードを `Scripting.Dictionary` にハッシュ展開し、キー(社員ID)ベースで一瞬でルックアップする構造($O(N + M)$)に昇華させることが、大規模プロジェクトを扱う上での絶対条件である。

② 計算エンジン(Calculation)の強制停止によるパフォーマンス劇的改善

MS Projectは、リソースのコスト(`Cost1` や標準単価)が変更されるたびに、プロジェクト全体のスケジュールとコストの再計算チェーンを走らせる。
これを防ぐため、処理の冒頭で `Application.Calculation = pjCalculationManual` を指定し、全リソースの書き込みが一通り終わるまでプロジェクトの再計算をロックする。これにより、処理時間が数分から数秒へと劇的に短縮される。

③ Win32 API (`PathFileExists`) によるI/Oフリーズの回避

リソースの顔写真やプロファイルドキュメントのパスがネットワークドライブ(UNCパスなど)を指している場合、存在しないパスにアクセスするとVBAの処理がタイムアウトまで数秒間フリーズする。
`shlwapi.dll` の `PathFileExists` APIを事前に挟むことで、OSレベルで瞬時にファイルの存在を判定し、ネットワークI/Oの無駄な待機時間を排除している。

④ COMオブジェクトの厳格なライフサイクル管理

VBAにおけるADOオブジェクト(Connection / Recordset)の解放漏れは、Officeアプリケーションのプロセス肥大化(メモリリーク)を招く。`On Error GoTo Cleanup` パターンを徹底し、例外発生時であっても確実に `State` を確認してクローズとインスタンス破棄を行う実装が不可欠である。

—

総括

Project VBAは、単なるマクロ記述言語ではない。それは、Microsoft Projectという巨大なデスクトップERPのエンジンルームに直接アクセスするための、強力かつ危険な特権インターフェースである。

リソース管理と外部システム連携の精度を高めることは、単なるデータの流し込みにとどまらず、プロジェクト計画の信頼性そのものを底上げする。今回提示したアーキテクチャをベースに、堅牢でスケーラブルなリソース管理基盤を構築してほしい。

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