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

スポンサーリンク

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

諸君、開発プロジェクトの現場で日々奮闘している諸君に問う。君たちのプロジェクトは、計画通りに進捗しているか?そして、その進捗に合わせて、割り当ての「型」を適切に調整できているか?

多くのプロジェクトマネージャーが直面する課題の一つに、リソース割り当ての柔軟性がある。初期計画フェーズでは、タスクにかかる「工数」を見積もり、それに基づいてスケジュールを組むことが多いだろう。しかし、プロジェクトが進行し、特定のタスクが「この期間内に絶対に完了させなければならない」という固定的な制約を持つようになる瞬間が必ず来る。

そこで本稿では、Project VBAを駆使し、リソースの「割り当てタイプ」を一括で「固定工数」から「固定期間」へと変更するメンテナンスツールの開発について、単なるコードの羅列ではない、真に生産的で堅牢な設計思想と共に解説する。私がこれまで見てきた「とりあえず動けばいい」という安易なアプローチが、いかにプロジェクトに負の遺産を残してきたか、そしてそれをどう回避すべきか、魂を込めて伝授しよう。

なぜ「割り当てタイプ」の変更が必要なのか?

Projectの割り当てタイプは、タスクの期間、工数、単位の相互関係を定義する重要なプロパティだ。

  • 固定工数 (Fixed Work): 作業の総工数が固定され、リソースの追加や削除によってタスクの期間が変動する。初期計画段階で、必要な「作業量」を重視する場合に有効だ。
  • 固定期間 (Fixed Duration): タスクの期間が固定され、期間内に作業を完了させるために必要な工数やリソースの単位が調整される。プロジェクトが佳境に入り、納期厳守が絶対となるフェーズで、スケジュールの安定化を図る場合に必須となる。

プロジェクトのライフサイクルを通じて、これらのタイプを適切に切り替えることで、スケジュールの予測可能性を高め、リスクを最小限に抑えることができる。しかし、GUIで一つずつ変更する作業は、想像を絶する苦痛と非効率の極みだ。

愚直なループ処理の落とし穴:パフォーマンスと堅牢性の罠

「VBAで一括処理?簡単だ。Assignmentsコレクションをループして、Typeプロパティを変えればいい。」

もしそう考えているなら、それは危険な兆候だ。Projectオブジェクトモデルへの安易なアクセスは、想像以上にパフォーマンスを劣化させ、大規模なプロジェクトファイルでは致命的な遅延を引き起こす。そして、適切なエラーハンドリングやトランザクション管理なしに実行された一括処理は、最悪の場合、プロジェクトファイルを破損させるリスクすら孕んでいる。

私が「伝説的」と称される所以は、オブジェクトのライフサイクルとパフォーマンスの重みを骨の髄まで理解しているからだ。ProjectのGUI上でのたった一つの操作が、内部でどれほどの計算とオブジェクトへのアクセスを伴うか、君は意識しているか?VBAでその操作を自動化する際、その内部挙動を意識せずにただメソッドを呼び出すだけでは、システムへの負荷は計り知れないものになる。

問題点と、私からの警告

1. オブジェクト参照のオーバーヘッド: `ActiveProject.Assignments`コレクションの各要素(`Assignment`オブジェクト)へのアクセスは、決して軽くはない。Projectは内部で複雑なデータ構造と計算エンジンを抱えており、VBAからの各プロパティアクセスは、その内部構造を一時的に露出させ、処理を挟む。
2. 自動再計算の嵐: 割り当てタイプを変更するたびに、Projectはスケジュール全体を再計算しようとする。これがループ内で数千回繰り返されたらどうなるか?君のPCはフリーズし、プロジェクトメンバーは君を罵倒するだろう。
3. Undoスタックの肥大化: デフォルトでは、VBAによる変更もUndoスタックに積まれる。大量の変更を行うと、Undoスタックがメモリを圧迫し、Projectの動作を不安定にさせる。
4. エラーハンドリングの欠如: ネットワークドライブ上のファイル、読み取り専用ファイル、予期せぬデータ不整合。これらが発生した際に、処理が途中で中断し、プロジェクトファイルが中途半端な状態になるリスクを考慮しているか?

堅牢なメンテナンスツール構築のための設計思想

最高のツールとは、単に機能するだけでなく、いかなる状況下でも安定し、高速であり、かつ保守が容易であるものだ。以下に、そのための設計思想を示す。

1. パフォーマンス最適化の徹底

  • 画面更新の停止: `Application.ScreenUpdating = False`
  • 自動計算の停止: `Application.Calculation = pjManual`
  • Undoスタックの無効化: `Application.Undo = False` これは極めて重要だ。
  • オブジェクトへの最小限のアクセス: 必要なプロパティにのみアクセスし、不要なオブジェクト生成を避ける。

2. 厳格なエラーハンドリングとトランザクション管理

一括変更は、一種のトランザクションと捉えるべきだ。全ての変更が成功するか、そうでなければ元の状態に戻せる(ロールバックできる)ことが理想だが、Project VBAでは完全なロールバックは難しい場合が多い。そのため、以下の対策を講じる。

  • 事前確認: 処理開始前に、ユーザーにファイルの保存を促す。
  • エラー発生時のクリーンアップ: 処理中にエラーが発生した場合でも、画面更新や自動計算の設定を元に戻し、Projectを正常な状態に復帰させる。
  • 変更内容のログ: 大規模な変更の場合、何を変更したかをログファイルに出力すると、後々の監査や問題特定に役立つ。今回はシンプルにするが、この視点は常に持っておくべきだ。

3. ユーザーへのフィードバック

処理が長時間に及ぶ場合、ユーザーは「フリーズしたのでは?」と不安になる。ステータスバーを使って、現在の処理状況をリアルタイムで表示する。

4. 保守性と拡張性

  • 明確な変数名とコメント: コードの意図が誰にでも理解できるように記述する。
  • マジックナンバーの排除: 定数を使用し、意図を明確にする。
  • モジュール化: 複雑な処理は、関数やサブルーチンに分割する。

実践コード:リソース割り当てタイプ一括変更ツール

それでは、上記の設計思想に基づいた、堅牢で高速、そして保守性の高いProject VBAコードを提示する。このコードは、君たちのプロジェクト管理を確実に次のレベルへと引き上げるだろう。

Option Explicit

‘=======================================================================================
‘ モジュール名: modAssignmentTypeChanger
‘ 目的: アクティブなProjectファイル内の全てのリソース割り当てのタイプを
‘ 「固定工数 (pjFixedWork)」から「固定期間 (pjFixedDuration)」へ一括変換するツール。
‘ 堅牢なエラーハンドリング、パフォーマンス最適化、ユーザーフィードバックを考慮した設計。
‘=======================================================================================

Sub ChangeAllAssignmentsToFixedDuration()
‘———————————————————————————–
‘ 変数宣言
‘———————————————————————————–
Dim objProject As Project ‘ 現在アクティブなProjectオブジェクト
Dim objAssignment As Assignment ‘ 各リソース割り当てオブジェクト
Dim lngCount As Long ‘ 処理済み割り当てのカウンター
Dim lngTotalAssignments As Long ‘ 全割り当ての総数
Dim blnScreenUpdating As Boolean ‘ 元のScreenUpdating設定を保持
Dim blnCalculation As Boolean ‘ 元のCalculation設定を保持 (ProjectではpjManual/pjAutomatic)
Dim blnUndo As Boolean ‘ 元のUndo設定を保持

‘———————————————————————————–
‘ エラーハンドリングの開始
‘ 処理中にエラーが発生した場合、CleanUpラベルへジャンプし、
‘ 環境設定を元に戻して異常終了を防ぐ。
‘———————————————————————————–
On Error GoTo CleanUp

‘———————————————————————————–
‘ 事前チェックとユーザー確認
‘———————————————————————————–
Set objProject = ActiveProject
If objProject Is Nothing Then
MsgBox “アクティブなプロジェクトファイルが開かれていません。”, vbCritical, “エラー”
Exit Sub
End If

‘ ファイルが読み取り専用かどうかを確認
If objProject.ReadOnly Then
MsgBox “プロジェクトファイルが読み取り専用のため、変更できません。”, vbCritical, “エラー”
Exit Sub
End If

‘ ユーザーに操作の確認を求める
If MsgBox(“現在開いているプロジェクトの全てのリソース割り当てタイプを” & vbCrLf & _
“「固定工数」から「固定期間」へ変更します。” & vbCrLf & _
“この操作は元に戻すことができません。続行しますか?” & vbCrLf & vbCrLf & _
“注意: 処理前にプロジェクトファイルを保存することを強く推奨します。“, _
vbYesNo + vbExclamation, “割り当てタイプ一括変更の確認”) = vbNo Then
MsgBox “処理はキャンセルされました。”, vbInformation, “キャンセル”
Exit Sub
End If

‘———————————————————————————–
‘ パフォーマンス最適化のための設定変更
‘ これらの設定は処理速度を劇的に向上させる。
‘———————————————————————————–
With Application
‘ 現在のApplication設定を保持
blnScreenUpdating = .ScreenUpdating
blnCalculation = (.Calculation = pjManual) ‘ pjManualならTrue
blnUndo = .Undo

‘ 画面更新を停止(描画処理をスキップし、処理速度向上)
.ScreenUpdating = False
‘ 自動計算を停止(変更のたびに再計算されるのを防ぎ、処理速度向上)
.Calculation = pjManual
‘ Undo機能を無効化(大量の変更によるメモリ消費とパフォーマンス劣化を防ぐ)
‘ ※これにより、このVBA処理はUndoで元に戻せなくなるため、事前の保存確認が重要。
.Undo = False

‘ ステータスバーを初期化
.StatusBar = “処理を開始しています…”
End With

‘———————————————————————————–
‘ 全割り当て数の取得と初期化
‘———————————————————————————–
lngTotalAssignments = objProject.Assignments.Count
lngCount = 0

If lngTotalAssignments = 0 Then
MsgBox “このプロジェクトにはリソース割り当てが存在しません。”, vbInformation, “情報”
GoTo CleanUp ‘ 処理する割り当てがないため、クリーンアップへ
End If

‘———————————————————————————–
‘ 各リソース割り当ての処理
‘———————————————————————————–
For Each objAssignment In objProject.Assignments
‘ 進捗カウンターをインクリメント
lngCount = lngCount + 1

‘ Null割り当て(例: 期間のないタスクなど)はスキップ
If objAssignment Is Nothing Then
Debug.Print “Warning: Null Assignment found and skipped.”
GoTo NextAssignment
End If

‘ リソース割り当てタイプが既に「固定期間」でなければ変更
If objAssignment.Type <> pjFixedDuration Then
objAssignment.Type = pjFixedDuration
‘ Debug.Print “割り当てID: ” & objAssignment.ID & ” を固定期間に変更しました。”
End If

‘ ステータスバーで進捗を表示 (100割り当てごとに更新してパフォーマンスへの影響を最小限に)
If (lngCount Mod 100 = 0 Or lngCount = lngTotalAssignments) Then
Application.StatusBar = “割り当てタイプ変更中: ” & lngCount & ” / ” & lngTotalAssignments & _
” (” & Format(lngCount / lngTotalAssignments, “0%”) & “) 完了”
End If

NextAssignment:
Next objAssignment

‘———————————————————————————–
‘ 処理完了メッセージ
‘———————————————————————————–
MsgBox “全てのリソース割り当てタイプを「固定期間」へ変更しました。”, vbInformation, “完了”

CleanUp:
‘———————————————————————————–
‘ エラー処理と環境設定の復元
‘ エラーが発生した場合はここでメッセージを表示
‘———————————————————————————–
If Err.Number <> 0 Then
MsgBox “エラーが発生しました: ” & Err.Description & ” (コード: ” & Err.Number & “)” & vbCrLf & _
“処理は中断されました。プロジェクトの状態を確認してください。”, vbCritical, “エラー”
End If

‘———————————————————————————–
‘ パフォーマンス最適化設定を元に戻す
‘———————————————————————————–
With Application
If Not blnScreenUpdating Then .ScreenUpdating = blnScreenUpdating ‘ 元がFalseの場合のみ復元(TrueならDefaultなので変更不要)
If Not blnCalculation Then .Calculation = pjAutomatic ‘ pjManualならpjAutomaticに戻す
If Not blnUndo Then .Undo = blnUndo ‘ 元がFalseの場合のみ復元
.StatusBar = False ‘ ステータスバーをクリア
End With

‘ オブジェクト変数の解放 (参照を解除)
Set objAssignment = Nothing
Set objProject = Nothing

End Sub

コード利用のヒント

1. VBAエディタの起動: Projectを開き、`Alt + F11`キーでVBAエディタを開きます。
2. 標準モジュールの挿入: 左側のプロジェクトエクスプローラーで、該当プロジェクトを右クリック → 挿入(I) → 標準モジュール(M) を選択します。
3. コードの貼り付け: 新しく開いたモジュールウィンドウに上記のコードを全て貼り付けます。
4. マクロの実行: Projectに戻り、`Alt + F8`キーを押してマクロダイアログを開きます。`ChangeAllAssignmentsToFixedDuration`を選択し、「実行」ボタンをクリックします。

詳細解説:なぜこのコードは「伝説的」なのか

このコードは単に動く以上の価値を持つ。そこには、私が長年の経験で培った「極限の知見」が凝縮されている。

1. `Application.Undo = False` の真意

多くのVBA記事で`ScreenUpdating`や`Calculation`の停止は言及されるが、`Application.Undo = False`の重要性を説くものは少ない。これはProject VBAにおける最も強力かつ危険な最適化の一つだ。

  • パフォーマンス: 大量の変更をUndoスタックに積むことは、膨大なメモリを消費し、処理速度を著しいレベルで低下させる。特に`Assignment`オブジェクトの変更は、Projectの複雑な内部構造に影響を与えるため、Undo情報も肥大化しやすい。
  • 安定性: Undoスタックの過負荷は、Projectアプリケーションそのものの安定性を損なう可能性がある。クラッシュのリスクも高まる。
  • 危険性: しかし、この設定は文字通り「元に戻す」機能を無効化する。したがって、実行前のファイル保存は絶対的な要件となる。私はこの機能を用いる前に、必ずユーザーに「この操作は元に戻せません。今すぐ保存してください。」と強調する。

2. `pjManual` と `pjAutomatic` の賢い利用

`Application.Calculation = pjManual` は、VBA処理中にProjectが自動的にスケジュールを再計算するのを防ぐ。これにより、オブジェクトのプロパティ変更のたびに発生する重い計算処理が抑制され、全体の実行時間が大幅に短縮される。処理が完了した後、`pjAutomatic`に戻すことで、ユーザーが通常通りProjectを利用できるようにする。

3. オブジェクトのライフサイクル管理

`Set objAssignment = Nothing` や `Set objProject = Nothing` は、オブジェクト参照を明示的に解除し、メモリを解放する。VBAはガベージコレクションを持つが、明示的な解放は大規模処理後のリソースクリーンアップを確実にするための良い習慣だ。特にCOMオブジェクトであるProjectオブジェクトは、明示的に解放することで、潜在的なメモリリークやアプリケーションのハングを防ぐことができる。

4. 堅牢なエラーハンドリング (`On Error GoTo CleanUp`)

`On Error GoTo CleanUp` は、予期せぬエラーが発生した場合でも、処理を強制的に終了させず、`CleanUp`ラベルにジャンプして環境設定を元に戻すことを保証する。これにより、処理中にエラーが発生しても、`ScreenUpdating`や`Calculation`、`Undo`の設定が復元され、Projectが不安定な状態のままになることを防ぐ。これはプロダクションコードにおいて、絶対不可欠な設計だ。

5. 進捗表示の工夫

`Application.StatusBar` を利用して進捗を表示する際も、毎ループ更新するのではなく、`Mod 100` のように一定間隔で更新することで、ステータスバーの描画処理によるオーバーヘッドを最小限に抑えつつ、ユーザーへのフィードバックを維持する。

6. ファイルやデータベース連携の注意点

本コードはProjectファイル内部のデータのみを操作するが、もし君がこのツールを拡張し、外部データベースやExcelファイルから情報を取得して割り当てタイプを変更するような要件に直面した場合、以下の点に留意せよ。

  • 排他制御: 外部ファイルへのアクセスは、同時に複数のユーザーやプロセスから行われないよう、排他制御を考慮する必要がある。Projectファイル自体も、VBAから操作する際は他のユーザーが編集していないか確認することが重要だ。
  • データ整合性: 外部データとProjectデータの間の整合性を保つためのロジックを慎重に設計する。特に、外部データが不正確な場合でもProjectがクラッシュしないような堅牢性が求められる。
  • トランザクション: 外部データベースとの連携では、本格的なトランザクション管理(BEGIN TRANSACTION, COMMIT, ROLLBACK)を導入することで、データの一貫性を保証できる。これはVBAの範疇を超え、ADO.NETやODBCといったより低レベルなデータアクセス技術の知識が必要となる。

まとめ:プロジェクト管理を次の次元へ

この割り当てタイプ一括変更ツールは、単なるVBAスクリプトではない。それは、君たちのプロジェクトが直面するスケジューリングの課題に対し、パフォーマンス、堅牢性、そして保守性という三位一体の原則を持って立ち向かうための、私の知見の結晶だ。

初期計画から実行フェーズへの移行、あるいはクリティカルパス上のタスクに対する厳密な期間管理。これらのシナリオにおいて、手作業による変更はもはや許されない。このツールを導入することで、プロジェクトマネージャーはより戦略的な意思決定に時間を割くことができ、チームはスケジュール変更の煩雑さから解放されるだろう。

常に問うてほしい。「なぜこの書き方は非効率なのか?」「どう設計すべきか?」と。表面的な機能だけでなく、その裏側で何が起きているのかを理解し、最適解を追求する姿勢こそが、君を真のチーフアーキテクトへと導くのだ。このツールが、君たちのプロジェクトを次の次元へと引き上げる一助となることを願う。

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