【テクニカル・上級編】リソースの「割り当て」を一括で「固定工数」から「固定期間」へ変更するメンテナンスツール – Project VBA解析バイブル

スポンサーリンク

Project VBAを掌握する極限の知見:リソース割り当てタイプ一括変換の奥義

長年、Project VBAの最前線に立ち、数多のレガシーシステムを保守し、そして新たな自動化の道を切り拓いてきた者として、今日のテーマは少々深掘りが必要な領域です。プロジェクト管理におけるリソースの割り当てタイプ、特に「固定工数」から「固定期間」への一括変換。これは単なるプロパティ変更の自動化に留まらず、プロジェクトの健全な進行とスケジューリングの安定化に直結する、極めて重要なメンテナンス作業です。

一般的なVBAのリファレンスやUI操作の解説に終始するような記事は、既に多くの場所で見かけるでしょう。しかし、私の使命は、オブジェクトのライフサイクル、パフォーマンスの重み、そしてレガシー環境特有の挙動を知り尽くした者でなければ語り得ない「極限の知見」を、魂を込めて皆様にお届けすることです。

導入:なぜ「割り当てタイプ」の変換が不可欠なのか

プロジェクトの計画フェーズでは、多くのタスクが「固定工数(Fixed Work)」として割り当てられがちです。これは、特定の作業に必要な総工数を見積もり、リソースの稼働率に応じて期間が変動するという、非常に柔軟な考え方に基づいています。しかし、プロジェクトが進行し、具体的なデッドラインが迫るにつれて、その柔軟性が逆に足枷となることがあります。

例えば、あるタスクの進捗が芳しくない場合、固定工数のままだと、そのタスクの期間が自動的に延長され、後続タスクやプロジェクト全体のスケジュールに連鎖的な遅延を引き起こします。ここで必要となるのが、「固定期間(Fixed Duration)」への切り替えです。固定期間にすることで、タスクの完了期限を固定し、リソースの追加や投入工数の増加(またはスコープの調整)によってその期間内に収めるという、より厳格な管理が可能になります。

しかし、Projectの標準UIでは、個々の割り当てに対して手動でこのタイプを変更する必要があります。数百、数千に及ぶ割り当てを持つ大規模プロジェクトでは、これは現実的な選択肢ではありません。ここに、自動化ツールの存在意義があります。

課題の深掘り:Projectオブジェクトモデルとパフォーマンスの罠

Project VBAを用いて割り当てタイプを一括変更する際、最も留意すべきはパフォーマンスと安定性です。単に`Assignment.Type`プロパティをループで変更するだけでは、Projectアプリケーションが内部で頻繁な再計算をトリガーし、処理が極端に遅延したり、最悪の場合、アプリケーションが応答不能に陥ったりする可能性があります。

Projectの割り当てタイプは、`Work` (工数)、`Duration` (期間)、`Units` (ユニット、稼働率) の三つの要素間の関係性を定義します。

  • pjAssignmentFixedWork (固定工数): `Work`が固定されます。`Duration`を変更すると`Units`が調整され、`Units`を変更すると`Duration`が調整されます。
  • pjAssignmentFixedDuration (固定期間): `Duration`が固定されます。`Work`を変更すると`Units`が調整され、`Units`を変更すると`Work`が調整されます。
  • pjAssignmentFixedUnits (固定ユニット): `Units`が固定されます。`Work`を変更すると`Duration`が調整され、`Duration`を変更すると`Work`が調整されます。

`pjAssignmentFixedWork`から`pjAssignmentFixedDuration`への変更は、Projectに`Work = Duration Units`のバランスを再計算させることになります。特に、元の`Work`値が変更後の`Duration`と`Units`にどう影響するか、そのデフォルトの挙動を理解しておくことが重要です。多くの場合、Projectは既存の`Duration`を維持しようとしますが、`Work`がその`Duration`と`Units`に見合わない場合は、`Units`を調整しようとします。この複雑な内部ロジックが、一括処理時のパフォーマンスボトルネックとなるのです。

極限の知見:パフォーマンス最適化と堅牢な設計

この課題に対処するためには、Projectオブジェクトモデルの深い理解と、システムレベルでの最適化を施す必要があります。

1. Projectアプリケーションの制御:再計算と画面更新の停止

最も基本的ながら、最も効果的な最適化です。Projectの内部計算エンジンとUI描画は、VBA処理のボトルネックとなりがちです。

‘ Projectアプリケーションインスタンスの取得
Dim pjApp As MSProject.Application
Set pjApp = MSProject.Application

‘ 最適化の開始
With pjApp
.ScreenUpdating = False ‘ 画面更新を停止し、UI描画のオーバーヘッドをなくす
.Calculation = pjManual ‘ 自動計算を停止し、手動計算に切り替える。処理中の再計算を抑制。
.EnableEvents = False ‘ イベントを停止し、VBAイベントハンドラ等の不必要な起動を防ぐ
End With

処理が完了したら、必ず元の状態に戻すことを忘れてはなりません。

2. オブジェクトのライフサイクル管理とメモリ最適化

VBAにおけるオブジェクト参照は、適切な管理を怠るとメモリリークやパフォーマンス低下の原因となります。特にProjectオブジェクトモデルは階層が深く、多くのオブジェクトを生成・参照するため、明示的な解放が不可欠です。

‘ 各オブジェクトを丁寧にNothingに設定することで、参照カウントを減らし、メモリを解放する
Set objAssignment = Nothing
Set objTask = Nothing
Set objProject = Nothing
Set pjApp = Nothing

ループ内で大量のオブジェクトを扱う場合、各イテレーションの終わりにオブジェクトを`Nothing`に設定する習慣をつけましょう。

3. Windows APIによるUI応答性の維持(DoEventsの限界と代替案)

長時間にわたる処理中、VBAの単一スレッドの特性上、UIがフリーズしたように見えることがあります。`DoEvents`は簡易的な解決策として知られていますが、再入りの問題や、頻繁な呼び出しによるオーバーヘッドといった限界があります。

より洗練されたアプローチとして、Windows APIを利用した進捗表示や、必要に応じてスレッドのコントロールを検討します。ただし、割り当てタイプ変更のような比較的単純な処理では、`DoEvents`を適切に配置し、`Application.StatusBar`で進捗を表示するだけでも十分なケースが多いでしょう。

DoEventsの利用とStatusBarでの進捗表示

Dim lCount As Long
Dim lTotal As Long
lTotal = ActiveProject.Assignments.Count

For Each objAssignment In ActiveProject.Assignments
lCount = lCount + 1
‘ 進捗表示
pjApp.StatusBar = “処理中: ” & Format(lCount / lTotal, “0.0%”) & ” – 割り当てID: ” & objAssignment.ID

‘ DoEventsを呼び出し、UI応答性を維持
‘ ただし、処理内容が重い場合は頻繁な呼び出しは避ける
If lCount Mod 100 = 0 Then ‘ 例: 100件ごとにDoEventsを呼ぶ
DoEvents
End If
‘ … 割り当て処理 …
Next objAssignment

`DoEvents`の頻度を調整することで、パフォーマンスとUI応答性のバランスを取ることが重要です。

4. 堅牢なエラーハンドリングとロギング

予期せぬエラーは、大規模な自動化ツールの信頼性を著しく低下させます。特にレガシー環境では、ファイルの破損、不正なデータ、APIのバージョン不一致など、様々な問題が発生し得ます。徹底したエラーハンドリングと、問題発生時の詳細なロギングは不可欠です。

On Error GoTo ErrorHandler

‘ … 処理本体 …

Exit Sub

ErrorHandler:
‘ エラーが発生した際の処理
MsgBox “エラーが発生しました: ” & Err.Description & ” (コード: ” & Err.Number & “)”, vbCritical
‘ 詳細なログをファイルに出力するなど、運用時の保守性を高める
‘ 例: LogErrorToTextFile Err.Description, Err.Number, “ModuleName.ProcedureName”
‘ 処理中に変更された内容をロールバックするロジックも検討する
Resume Cleanup ‘ クリーンアップルーチンへジャンプ

エラー発生時に、変更をロールバックする仕組み(例:変更前の割り当てタイプを一時的に記録しておき、エラー時に元に戻す)を実装できれば、さらに信頼性が向上します。

実装例:リソース割り当てタイプ一括変換ツール

以下に、上記の知見に基づいたVBAコードの骨格を示します。このコードは、アクティブなプロジェクト内の全ての割り当てを走査し、条件に合致する「固定工数」の割り当てを「固定期間」に変換します。

Option Explicit

‘ Windows API宣言 (今回はDoEventsで対応するが、より高度な制御が必要な場合の参考)
‘ Private Declare Sub Sleep Lib “kernel32” (ByVal dwMilliseconds As Long)

Public Sub ConvertAssignmentType_FixedWorkToFixedDuration()
‘ Projectアプリケーションオブジェクト
Dim pjApp As MSProject.Application
‘ 現在アクティブなプロジェクトオブジェクト
Dim pjActiveProject As MSProject.Project
‘ 各タスクオブジェクト
Dim objTask As MSProject.Task
‘ 各割り当てオブジェクト
Dim objAssignment As MSProject.Assignment

‘ 処理対象の割り当て数と処理済み割り当て数をカウント
Dim lTotalAssignments As Long
Dim lProcessedCount As Long
Dim lConvertedCount As Long

‘ プロジェクトの安定性を確保するためのフラグ
Dim bAppScreenUpdating As Boolean
Dim bAppCalculation As Long
Dim bAppEnableEvents As Boolean

On Error GoTo ErrorHandler

‘ 1. Projectアプリケーションインスタンスの取得
‘ CreateObjectではなくGetObjectを使用することで、既に開いているProjectインスタンスを利用
‘ これにより、ユーザーが開いているProjectファイルに対して直接操作できる
Set pjApp = GetObject(, “MSProject.Application”)
If pjApp Is Nothing Then
MsgBox “Microsoft Projectアプリケーションが起動していません。”, vbCritical
Exit Sub
End If

‘ アクティブなプロジェクトがなければエラー
If pjApp.Projects.Count = 0 Then
MsgBox “開いているプロジェクトがありません。”, vbCritical
Exit Sub
End If
Set pjActiveProject = pjApp.ActiveProject

‘ ユーザーに操作確認を促す(重要: 処理前に必ずバックアップを推奨)
If MsgBox(“現在のプロジェクトの割り当てタイプを一括で『固定工数』から『固定期間』へ変更します。” & vbCrLf & _
“この操作は元に戻せません。プロジェクトファイルのバックアップは取りましたか?”, _
vbYesNo + vbExclamation, “割り当てタイプ一括変換の確認”) = vbNo Then
Exit Sub
End If

‘ 2. アプリケーションの状態を最適化
‘ 現在の設定を保存し、処理後に復元するために必要
With pjApp
bAppScreenUpdating = .ScreenUpdating
bAppCalculation = .Calculation
bAppEnableEvents = .EnableEvents

.ScreenUpdating = False ‘ 画面更新を停止
.Calculation = pjManual ‘ 自動計算を停止
.EnableEvents = False ‘ イベントを停止
.StatusBar = “割り当てタイプ変換処理を開始します…”
End With

lTotalAssignments = pjActiveProject.Assignments.Count
lProcessedCount = 0
lConvertedCount = 0

‘ 3. 全ての割り当てを走査し、条件に合致するものを変換
For Each objAssignment In pjActiveProject.Assignments
lProcessedCount = lProcessedCount + 1

‘ 進捗をステータスバーに表示し、UI応答性を維持 (DoEventsの適切な利用)
‘ ProgressBarを実装する場合はWindows APIを利用するが、今回は簡潔にStatusBarで対応
If lProcessedCount Mod 50 = 0 Or lProcessedCount = lTotalAssignments Then
pjApp.StatusBar = “処理中: ” & Format(lProcessedCount / lTotalAssignments, “0.0%”) & _
” (” & lProcessedCount & “/” & lTotalAssignments & “) – 割り当てID: ” & objAssignment.ID
DoEvents ‘ UIを更新し、アプリケーションがフリーズしないようにする
End If

‘ 無効な割り当てやサマリータスクの割り当てはスキップ
If objAssignment Is Nothing Then GoTo NextAssignment
If objAssignment.Task Is Nothing Then GoTo NextAssignment
If objAssignment.Task.Summary Then GoTo NextAssignment ‘ サマリータスクの割り当ては対象外

‘ 現在の割り当てタイプが「固定工数」の場合のみ変換
If objAssignment.Type = pjAssignmentFixedWork Then
‘ 割り当てタイプを「固定期間」に設定
‘ この変更により、Project内部で再計算がトリガーされるが、
‘ pjManual設定により、この時点ではスケジュールへの影響は最小限に抑えられる
objAssignment.Type = pjAssignmentFixedDuration
lConvertedCount = lConvertedCount + 1
End If

NextAssignment:
Next objAssignment

‘ 4. 処理完了後の後処理とメッセージ
MsgBox lConvertedCount & ” 件の割り当てが『固定工数』から『固定期間』へ変換されました。”, vbInformation

Cleanup:
‘ 5. アプリケーションの状態を元の設定に戻す
With pjApp
.ScreenUpdating = bAppScreenUpdating
.Calculation = bAppCalculation
.EnableEvents = bAppEnableEvents
.StatusBar = False ‘ ステータスバーをクリア
End With

‘ オブジェクトの明示的な解放 (参照カウントを0にし、メモリを解放)
Set objAssignment = Nothing
Set objTask = Nothing
Set pjActiveProject = Nothing
Set pjApp = Nothing
Exit Sub

ErrorHandler:
MsgBox “エラーが発生しました:” & vbCrLf & _
“エラー番号: ” & Err.Number & vbCrLf & _
“説明: ” & Err.Description & vbCrLf & _
“処理は中断されます。”, vbCritical
GoTo Cleanup ‘ エラー時も必ずクリーンアップ処理を行う
End Sub

コード解説の深掘り:

  • `GetObject(, “MSProject.Application”)`: `CreateObject`ではなく`GetObject`を使用しているのは、既にProjectが起動している場合、そのインスタンスを再利用するためです。これにより、ユーザーが作業中のProjectファイルに対して直接操作が可能となり、不要なProjectプロセスの起動を防ぎます。
  • `pjApp.Projects.Count = 0`: Projectが起動していても、開いているプロジェクトファイルがない場合は処理を中断します。これは、操作対象が存在しない場合の堅牢性確保です。
  • `pjApp.ScreenUpdating = False`, `.Calculation = pjManual`, `.EnableEvents = False`: これらはProject VBAにおける「三種の神器」とも言うべきパフォーマンス最適化の基本です。特に`.Calculation = pjManual`は、割り当てタイプ変更時にProjectが不必要に全体スケジュールを再計算することを防ぎ、処理速度を劇的に向上させます。
  • `pjApp.StatusBar`と`DoEvents`: 長時間処理におけるUI応答性の確保は、ユーザー体験の観点から非常に重要です。`StatusBar`で進捗を可視化し、適度な頻度で`DoEvents`を呼び出すことで、アプリケーションがフリーズしていると誤解されることを防ぎます。`DoEvents`の呼び出し頻度は、プロジェクトの規模と処理の重さに応じて調整が必要です。
  • `objAssignment.Task.Summary`チェック: サマリータスク(集計タスク)に割り当てられたリソースは、実際には子タスクの集計結果であり、直接タイプを変更すると予期せぬスケジューリングの歪みを生む可能性があります。そのため、明示的にスキップしています。
  • エラーハンドリングと`Cleanup`ラベル: `On Error GoTo ErrorHandler`と`GoTo Cleanup`の組み合わせは、VBAにおける堅牢なエラー処理の典型です。どのようなエラーが発生しても、必ず最適化設定を元に戻し、オブジェクトを解放することで、アプリケーションの状態をクリーンに保ちます。

運用と保守の哲学:レガシー環境における真価

この種のツールは、一度作成したら終わりではありません。プロジェクト管理の要件、Projectのバージョンアップ、Office環境の変化(32bit/64bit Officeの混在など)に応じて、常にメンテナンスが必要となります。

  • Projectバージョンの互換性: Project 2010, 2013, 2016, 2019, M365など、異なるバージョン間でのオブジェクトモデルの差異は小さいことが多いですが、稀にプロパティの挙動や定数の値が変更されることがあります。`Microsoft Project xx.0 Object Library`の参照設定が、使用する環境に合致しているか常に確認が必要です。
  • テスト環境の重要性: 本番データでの実行は常にリスクを伴います。必ず、本番プロジェクトファイルのコピーやテスト用プロジェクトで十分に検証を行ってから適用してください。
  • ドキュメント化: どのような条件で、どのような変更を行うツールなのか、詳細なドキュメントを残すことが、将来の保守性を高めます。特に、変更後の`Work`, `Duration`, `Units`の具体的な挙動について、Projectのヘルプだけでは不十分な場合があるため、実測値に基づいた知見を記録しておくべきです。

結び:自動化がもたらす戦略的優位性

プロジェクト管理の現場において、このような自動化ツールは単なる時短に留まりません。それは、管理者が手作業では実現不可能な規模と速度でプロジェクトの健全性を維持し、戦略的な意思決定に集中できる環境を提供するものです。割り当てタイプの戦略的な一括変更は、遅延プロジェクトをタイムボックス化し、リソースの再配分を促し、最終的にはプロジェクトの成功確率を飛躍的に向上させるための、強力な武器となるでしょう。

Project VBAを深く理解し、そのオブジェクトモデルの挙動を掌握する。そして、パフォーマンス、安定性、保守性といったシステム全体の品質を追求する。これこそが、伝説的なチーフアーキテクトが長年培ってきた「極限の知見」であり、皆様が日々の業務自動化に活かすべき真髄なのです。

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