【テクニカル・上級編】【上級者向け】プロジェクトのベースライン設定履歴を専用の管理DBへ蓄積し、監査ログとして活用する機能 – Project VBA解析バイブル

スポンサーリンク

【Project VBAを掌握する極限の知見】ベースライン変更履歴の不可改ざん監査ログ基盤の構築

プロジェクトマネジメントにおいて、ベースライン(Baseline)は計画の「不可侵のアンカー」である。しかし、現場の現場における数多のスケジュール変更圧力の前に、このアンカーが密かに上書きされ、過去の遅延やスコープクリープが闇に葬られる光景を、我々は幾度となく目撃してきた。

Project VBA(Microsoft ProjectのVBA環境)標準のベースライン機能は、上書き操作に対して無力である。誰が、いつ、どのバージョンからどのバージョンへ計画を巻き戻したのか。その履歴をトレースする仕組みをアプリケーション層に持たない限り、大規模プロジェクトにおけるガバナンスの維持は絵に描いた餅に過ぎない。

本稿では、Project VBAから外部の堅牢な管理データベース(今回はADOを経由したSQL Server / SQLiteを想定)へベースラインの設定・変更イベントをフックし、改ざん不能な監査ログとして蓄積するアーキテクチャの全貌を解き明かす。

1. アーキテクチャ設計:なぜ標準機能では不十分なのか

Projectのオブジェクトモデルにおいて、`Baseline` コレクションのセットは容易に行える。しかし、以下の課題が常にエンジニアの前に立ち塞がる。

1. イベントフックの欠如: Microsoft ProjectのVBAには、`BeforeBaselineSave` のような直感的なイベントが存在しない。そのため、カスタムのリボンUIやプロシージャ経由で操作をラップする必要がある。
2. Windows APIによるコンテキスト取得: 「誰が(Windowsユーザー名・Active Directoryアカウント)」操作したかを厳密に担保するため、VBAの環境変数だけでなく、Windows APIを叩いてセッション情報を正確に取得する。
3. COMオブジェクトのメモリリーク対策: Project VBAからADOやFileSystemObjectを操作する際、適切な参照解放を行わないと、MSProject.exeのプロセス内にメモリ断片化やCOM参照のリークが蓄積し、長時間の運用で不安定化する。

これらを克服する「極限の設計」をコードに落とし込む。

2. 実装:監査ログ記録エンジン(VBAモジュール)

以下のコードは、単なるラッパーではない。例外処理、Windows APIによるセッション取得、およびADOを用いたトランザクション制御を完結させたプロダクションクオリティのモジュールである。

Option Explicit

‘ ==============================================================================
‘ módulo名: clsBaselineAuditor
‘ 概要: プロジェクトのベースライン変更を検知し、外部DBへ監査ログを強固に記録する
‘ ==============================================================================

‘ Windows API: 現在のログインユーザー名を取得
Private Declare PtrSafe Function GetUserName Lib “advapi32.dll” Alias “GetUserNameA” ( _
ByVal lpBuffer As String, _
ByRef nSize As Long) As Long

‘ Windows API: 現在のコンピュータ名を取得
Private Declare PtrSafe Function GetComputerName Lib “kernel32.dll” Alias “GetComputerNameA” ( _
ByVal lpBuffer As String, _
ByRef nSize As Long) As Long

‘ 定数定義
Private Const DB_CONNECTION_STRING As String = “Provider=SQLOLEDB.1;Server=srv-db01;Database=ProjectGovernance;Trusted_Connection=yes;”

‘ 構造体: 監査ログコンテキスト
Private Type AuditContext
UserName As String
MachineName As String
ProjectName As String
BaselineIndex As Integer
ActionTimestamp As Date
OperationType As String
End Type

Public Sub RecordBaselineAction(ByVal proj As MSProject.Project, ByVal baselineIndex As Integer, ByVal operation As String)
Dim ctx As AuditContext
Dim conn As Object ‘ ADODB.Connection
Dim cmd As Object ‘ ADODB.Command
Dim sql As String

On Error GoTo ErrorHandler

‘ 1. コンテキスト情報の収集
ctx.UserName = GetWindowsUserName()
ctx.MachineName = GetComputerNameString()
ctx.ProjectName = proj.Name
ctx.BaselineIndex = baselineIndex
ctx.ActionTimestamp = Now
ctx.OperationType = operation

‘ 2. ADOコネクションの確立 (遅延バインディングによるバージョン依存性の排除)
Set conn = CreateObject(“ADODB.Connection”)
conn.ConnectionString = DB_CONNECTION_STRING
conn.CommandTimeout = 30
conn.ConnectionTimeout = 15
conn.Open

‘ トランザクション開始
conn.BeginTrans

‘ 3. パラメタライズドクエリによるSQLインジェクション完全防御と監査ログ挿入
sql = “INSERT INTO T_BaselineAuditLog ” & _
“(UserName, MachineName, ProjectFileName, BaselineNumber, OperationType, ActionTime) ” & _
“VALUES (?, ?, ?, ?, ?, ?)”

Set cmd = CreateObject(“ADODB.Command”)
Set cmd.ActiveConnection = conn
cmd.CommandText = sql
cmd.CommandType = 1 ‘ adCmdText

‘ パラメータの明示的追加(型安全性の確保)
cmd.Parameters.Append cmd.CreateParameter(“p1”, 200, 1, 100, ctx.UserName) # adVarChar
cmd.Parameters.Append cmd.CreateParameter(“p2”, 200, 1, 100, ctx.MachineName) # adVarChar
cmd.Parameters.Append cmd.CreateParameter(“p3”, 200, 1, 255, ctx.ProjectName) # adVarChar
cmd.Parameters.Append cmd.CreateParameter(“p4”, 3, 1, , ctx.BaselineIndex) # adInteger
cmd.Parameters.Append cmd.CreateParameter(“p5”, 200, 1, 50, ctx.OperationType) # adVarChar
cmd.Parameters.Append cmd.CreateParameter(“p6”, 135, 1, , ctx.ActionTimestamp) # adDBTimeStamp

cmd.Execute

‘ コミット
conn.CommitTrans

GoTo CleanUp

ErrorHandler:
‘ ロールバックの実行
If Not conn is Nothing Then
If conn.State = 1 Then conn.RollbackTrans
End If

‘ 致命的エラーのログ出力(ローカルイベントビューアまたはファイルへフォールバック)
MsgBox “監査ログの記録に失敗しました。セキュリティポリシーに基づき処理を中断します。” & vbCrLf & _
“エラー詳細: ” & Err.Description, vbCritical, “Governance System Error”

CleanUp:
‘ 徹底的なオブジェクトの解放(メモリリークの根絶)
On Error Resume Next
If Not cmd Is Nothing Then Set cmd = Nothing
If Not conn Is Nothing Then
If conn.State = 1 Then conn.Close
Set conn = Nothing
End If
On Error GoTo 0
End Sub

Private Function GetWindowsUserName() As String
Dim buffer As String
Dim size As Long
buffer = String(255, 0)
size = Len(buffer)
If GetUserName(buffer, size) <> 0 Then
GetWindowsUserName = Left(buffer, size – 1)
Else
GetWindowsUserName = “UNKNOWN”
End If
End Function

Private Function GetComputerNameString() As String
Dim buffer As String
Dim size As Long
buffer = String(255, 0)
size = Len(buffer)
If GetComputerName(buffer, size) <> 0 Then
GetComputerNameString = Left(buffer, size – 1)
Else
GetComputerNameString = “UNKNOWN”
End If
End Function

3. シニアエンジニアが押さえるべき実装の急所

上記のコードベースには、レガシーかつ巨大なVBA環境を運用する上で必須となる「防衛的プログラミング」の思想が組み込まれている。

① 遅延バインディング(Late Binding)の徹底

コード内では `CreateObject(“ADODB.Connection”)` を用いている。参照設定(`Microsoft ActiveX Data Objects 2.x Library`)をあえて行わないことで、クライアントPCごとのDLLのバージョンの差異(Type Libraryの不一致)に起因する「コンパイルエラー:プロジェクトまたはライブラリが見つかりません」を完全に排除している。

② パラメタライズドクエリによる堅牢性

SQL文を文字列結合で組み立てる素朴なVBAコードは、プロジェクトファイル名に意図的なSQLインジェクション文字列(例: `’); DROP TABLE Projects; –`)が仕込まれた場合に脆弱性を露出する。ADOの `Command` オブジェクトと `CreateParameter` を用いて型を厳密に定義し、悪意ある入力を無効化している。

③ 容赦なきCOMオブジェクトの解放

VBAのガベージコレクションは非常に緩慢である。特にProjectのように長時間の常駐・マルチドキュメントオープンを前提とするアプリでは、`Set conn = Nothing` を怠ると、わずか数十回のベースライン設定操作で数百MBのメモリリークを引き起こす。`CleanUp` ラベルによる確実なデストラクタ的処理は生命線である。

4. 運用とガバナンスへの昇華

このモジュールを組み込んだ後、プロジェクトマネージャーが標準の「プロジェクトの保存」や「ベースラインの設定」ボタンを直接押させないフローを強制する。リボンUI(XMLカスタマイズ)を書き換え、ベースライン設定アクションを独自のVBAプロシージャ(例: `SecureSetBaseline`)へとルーティングするのだ。

Public Sub SecureSetBaseline()
Dim auditor As clsBaselineAuditor
Dim targetBaseline As Integer

‘ 例としてベースライン0(Baseline)を設定する場合
targetBaseline = pjBaseline

‘ 1. MS Project標準のベースライン設定ダイアログまたは自動処理
ActiveProject.SetBaseline Baseline:=targetBaseline

‘ 2. 監査ログ基盤への記録
Set auditor = New clsBaselineAuditor
auditor.RecordBaselineAction ActiveProject, targetBaseline, “OVERWRITE_BASELINE”

Set auditor = Nothing

MsgBox “ベースラインが正常に設定され、ガバナンスDBへ監査ログが記録されました。”, vbInformation, “監査システム”
End Sub

総括

VBAは「手軽なスクリプト言語」と侮られがちだが、Windows APIの直叩き、厳格なメモリ管理、そして外部DBとのトランザクション連携を実装するならば、それは立派な「エンタープライズ・インテグレーション・レイヤー」となる。

ベースラインの改ざんという、組織のコンプライアンスを揺るがすリスクをコードの力で完全に封じ込めること。それこそが、レガシーシステムを極限まで極めたシニアアーキテクトに課された使命である。

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