MS Projectの「ブラックボックス」を解体する:カスタムフィールド監査ツールの開発
こんにちは。プロジェクト管理の現場で、複雑に絡み合ったスケジュールやリソース管理に頭を抱えていませんか?
MS Projectを使い込んでいると、必ずと言っていいほど直面する問題があります。それは「カスタムフィールドの肥大化とブラックボックス化」です。
「この数式、誰がいつ作ったんだっけ?」「このフィールド、実はもう誰も使っていないのに、計算負荷だけを上げているんじゃないか?」……。
マクロの記録ボタンを押して、操作をなぞるだけでは決して辿り着けない、「オブジェクトの構造を俯瞰する」というエンジニアリングの視点。今日はその視点を持って、プロジェクトの健全性を保つための「カスタムフィールド監査ツール」を一緒に作っていきましょう。
ここをクリアすれば、Project VBAの基本はバッチリですよ。
1. なぜ「監査」が必要なのか?
MS Projectにおけるカスタムフィールドは、非常に強力な武器です。しかし、その強力さゆえに、以下のような「管理不全」を引き起こします。
- 数式の迷宮化: 複雑なネスト(入れ子)になった数式が、メンテナンス不能になる。
- リソースの浪費: 使われていないフィールドに重い数式が入っていると、プロジェクトを開くたびに計算負荷がかかり、動作が重くなる。
- データの不整合: 似たような名前のフィールドが複数存在し、どのデータが正しいのか分からなくなる。
これらを防ぐには、「今、何が、どのように使われているのか」を一覧化して可視化することが不可欠なのです。
2. 構造を知る:TaskとResourceの二重構造
VBAコードを書く前に、MS Projectの「設計図」を頭に叩き込みましょう。カスタムフィールドは、大きく分けて2つの領域に存在します。
| 領域 | 対象オブジェクト | 特徴 |
|---|---|---|
| Task Custom Fields | タスク(作業) | 作業期間、コスト、進捗率など「作業そのもの」に関する属性 |
| Resource Custom Fields | リソース(資源) | 氏名、スキル、単価、役割など「人や設備」に関する属性 |
ここでの重要なポイントは、「Taskのカスタムフィールドを調べても、Resourceのカスタムフィールドは見つからない」ということです。監査ツールを作る際は、この両方のコレクションを個別にスキャンする必要があります。
3. 実装:カスタムフィールド監査ツール
それでは、プロジェクト内のすべてのカスタムフィールドの名前、型、数式、そして「実際に使用されているか」を抽出し、Excelへ書き出すコードを実装しましょう。
「マクロの記録」では、Excelへの書き出し操作は記録できても、オブジェクトのプロパティをループで回る論理構造は作れません。ここからが真のプログラミングの始まりです。
Option Explicit
''' <summary>
''' プロジェクト内のカスタムフィールド使用状況をExcelに書き出す監査ツール
''' </summary>
Sub AuditCustomFields()
Dim proj As Project
Set proj = ActiveProject
Dim xlApp As Object
Dim xlWB As Object
Dim xlSheet As Object
Dim row As Long
' --- 1. Excelの準備 ---
' 監査結果をExcelに出力することで、分析しやすくします
On Error Resume Next
Set xlApp = GetObject(, "Excel.Application")
If xlApp Is Nothing Then
Set xlApp = CreateObject("Excel.Application")
End If
On Error GoTo 0
xlApp.Visible = True
Set xlWB = xlApp.Workbooks.Add
Set xlSheet = xlWB.Sheets(1)
' ヘッダーの作成
With xlSheet
.Cells(1, 1).Value = "Type (Task/Resource)"
.Cells(1, 2).Value = "Field Name"
.Cells(1, 3).Value = "Data Type"
.Cells(1, 4).Value = "Formula"
.Cells(1, 5).Value = "Is In Use?"
.Range("A1:E1").Font.Bold = True
.Range("A1:E1").Interior.Color = RGB(200, 200, 200)
End With
row = 2
' --- 2. Task Custom Fields のスキャン ---
Debug.Print "Scanning Task Custom Fields..."
Call ScanFields(proj.TaskCustomFields, "Task", xlSheet, row)
' --- 3. Resource Custom Fields のスキャン ---
Debug.Print "Scanning Resource Custom Fields..."
Call ScanFields(proj.ResourceCustomFields, "Resource", xlSheet, row)
' --- 4. 後処理 ---
xlSheet.Columns("A:E").AutoFit
MsgBox "監査が完了しました!Excelを確認してください。", vbInformation
End Sub
''' <summary>
''' 各フィールドをループしてExcelに書き込む共通処理
''' </summary>
Private Sub ScanFields(ByRef fieldColl As Object, ByVal category As String, _
ByRef xlSheet As Object, ByRef row As Long)
Dim i As Integer
Dim fld As Object
' fieldColl は TaskCustomFields または ResourceCustomFields
For i = 1 To fieldColl.Count
Set fld = fieldColl(i)
' Excelへの書き出し
xlSheet.Cells(row, 1).Value = category
xlSheet.Cells(row, 2).Value = fld.Name
xlSheet.Cells(row, 3).Value = GetFieldTypeString(fld.Type)
xlSheet.Cells(row, 4).Value = fld.Formula
' UseFieldプロパティがTrueなら、プロジェクト内のどこかで使われている
If fld.UseField Then
xlSheet.Cells(row, 5).Value = "YES"
xlSheet.Cells(row, 5).Font.Color = RGB(0, 128, 0) ' 緑色
Else
xlSheet.Cells(row, 5).Value = "NO"
xlSheet.Cells(row, 5).Font.Color = RGB(255, 0, 0) ' 赤色
End If
row = row + 1
Next i
End Sub
''' <summary>
''' 数値(0)などの定数を読みやすい文字列に変換するヘルパー関数
''' </summary>
Private Function GetFieldTypeString(ByVal fieldType As Integer) As String
Select Case fieldType
Case 0: GetFieldTypeString = "Text"
Case 1: GetFieldTypeString = "Flag"
Case 2: GetFieldTypeString = "Number"
Case 3: GetFieldTypeString = "Date"
Case 4: GetFieldTypeString = "Duration"
Case 5: GetFieldTypeString = "Cost"
Case Else: GetFieldTypeString = "Unknown"
End Select
End Function
4. 伝説のエンジニアによる「ここが肝心」解説
コードを書き終えたら、その背後にある「なぜこう書いたのか」というロジックを理解しましょう。これが、ただのプログラマーとエンジニアを分ける境界線です。
① `UseField` プロパティの威力
このコードの心臓部は `fld.UseField` です。
カスタムフィールドは、「定義されているだけ」の状態と、「実際にタスクやリソースの行に割り当てられている」状態があります。
`UseField` をチェックすることで、「定義はされているが、誰も使っていない幽霊フィールド」を瞬時にあぶり出すことができます。これは、プロジェクトの軽量化において極めて重要な指標です。
② 共通処理への集約(DRY原則)
`ScanFields` というサブプロシージャを作りました。これはプログラミングの鉄則 DRY (Don’t Repeat Yourself: 同じことを繰り返すな) に基づいています。
Task用とResource用で似たようなコードを2回書くのではなく、引数(`fieldColl`)を渡すことで、一つのロジックで両方を処理しています。これにより、将来「新しいフィールドタイプが増えた」としても、修正箇所は一箇所で済みます。
③ 型の変換(Enumの解釈)
MS ProjectのAPIは、データの型を「0, 1, 2…」といった整数(Enum)で返してきます。しかし、人間が「Type 2」と言われてもピンときませんよね? `GetFieldTypeString` 関数を用意することで、人間にとって意味のある「Number」という言葉に翻訳しています。「コンピュータが理解できる言葉を、人間が理解できる言葉に翻訳する」。これは優れたツール開発の基本です。
5. 陥りやすいエラーと対策
このツールを運用する際、以下の点に注意してください。
- 「数式が空っぽ」問題:
`fld.Formula` が空の場合、エラーにはなりませんが、出力結果が空白になります。これは「手入力用のフィールド」であることを意味します。
- Excelの参照設定:
上記のコードでは `CreateObject(“Excel.Application”)` を使った「実行時バインディング(Late Binding)」を採用しています。これにより、Excelのバージョンが違ってもエラーが出にくい、移植性の高いコードにしています。
- 権限エラー:
プロジェクトファイルが「読み取り専用」であったり、他のユーザーが排他的に開いている場合、操作に失敗することがあります。
最後に
カスタムフィールドの監査ができるようになると、プロジェクト管理の「見えないコスト」をコントロールできるようになります。
「動けばいい」というマクロの段階から、「管理しやすく、メンテナンスしやすい仕組みを作る」というエンジニアリングの段階へ。あなたは今、その大きな一歩を踏み出しました。
ここをクリアすれば、Project VBAの基本はバッチリですよ。次は、この監査結果をもとに「使われていないフィールドを自動削除する」といった、より高度な自動化に挑戦してみましょう!
応援しています。
