こんにちは!プロジェクト管理の世界へようこそ。
マクロの記録ボタンを押すだけの自動化から一歩進み、自分だけのオリジナルシステムを構築しようとしているあなたへ、今日はとてもエキサイティングなお話をします。
私たちが日々向き合うMS Project。数千行にも及ぶタスク、そして何十人ものリソースが入り乱れる大規模プロジェクトにおいて、VBAでリソース情報をゴリゴリ操作しようとしたとき…こんな絶望感を味わったことはありませんか?
「……画面が固まった。終わらない……」
そう、Project VBAでリソースを愚直にループさせて探そうとすると、信じられないほど処理が重くなります。オブジェクトの生成と破棄、そしてアプリケーションの画面描画(再描画)が、あなたのPCのCPUをいじめているからです。
ここをクリアすれば、あなたも立派な「Project VBAのアーキテクト」です。
今回は、メモリ上に特急列車のエクスプレスレーンを作る『Dictionary(ディクショナリー)オブジェクト』を使った、極限の高速化術を一緒にマスターしていきましょう!
—
なぜ、大規模プロジェクトのリソース処理は遅くなるのか?
まず、敵を知ることから始めましょう。
MS Projectのデータ構造は、タスク(Task)やリソース(Resource)がそれぞれ階層構造(コレクション)としてメモリに存在しています。
例えば、「リソース名『山田太郎』のコスト単価を変更したい」と思ったとき、あなたならどうコードを書きますか?
❌ やってはいけない「全件ループ」のアンチパターン
Sub UpdateResourceCost_Slow()
Dim r As Resource
‘ すべてのリソースを上から順番に探す(これがいけない!)
For Each r In ActiveProject.Resources
If Not r Is Nothing Then
If r.Name = “山田太郎” Then
r.StandardRate = 5000 ‘ コスト変更
Exit For
End If
End If
Next r
End Sub
このコード、リソースが5人くらいなら一瞬です。しかし、数千人のリソースを抱える大規模プロジェクトでこれをやると、「お名前確認のために、毎回全社員名簿の1ページ目から順番に指名手配犯を探す」ような無駄なことを毎回やることになります。
これが、処理が遅くなる最大の原因です。
—
救世主:「Dictionary」という名のメモリー脳
ここで登場するのが `Scripting.Dictionary` です。
Dictionaryとは、「キー(Key)」と「値(Value)」をセットにして、一瞬でデータを記憶・検索できる連想配列のこと。
- キー(Key): 検索するための目印(例:リソース名、あるいはリソース固有のID)
- 値(Value): 実際の「リソースオブジェクトそのもの」
一度、プロジェクト全体のリソースをDictionaryに読み込ませてしまえば、次回からは名前を指定するだけで、一瞬にして(O(1)の計算量で)そのリソースオブジェクトを呼び出せるようになります。
—
実践!超高速リソース管理マクロ
それでは、実際に現場でそのまま使える実用的なコードを見ていきましょう。
このコードを実行するには、事前にVBEのメニューから [ツール] > [参照設定] で 「Microsoft Scripting Runtime」 にチェックを入れておくか、あるいは遅延バインディング(CreateObject)を使いましょう。今回は汎用的な `CreateObject` で記述します。
Sub HighSpeedResourceUpdate()
Dim t1 As Single: t1 = Timer ‘ 処理時間計測用
Dim dictResources As Object
Set dictResources = CreateObject(“Scripting.Dictionary”)
Dim r As Resource
‘ ==========================================
‘ Step 1: リソース情報を一気にDictionaryにキャッシュする
‘ ==========================================
‘ 画面の描画を停止してパフォーマンスを極限まで引き上げる
Application.ScreenUpdating = False
For Each r In ActiveProject.Resources
If Not r Is Nothing Then
‘ キーを「リソース名」、値を「リソースオブジェクト」として登録
If Not dictResources.Exists(r.Name) Then
dictResources.Add r.Name, r
End If
End If
Next r
‘ ==========================================
‘ Step 2: メモリ上で超高速に検索・更新を行う
‘ ==========================================
‘ 例:「山田太郎」の標準単価を 6,000円にアップロードする
Dim targetName As String
targetName = “山田太郎”
If dictResources.Exists(targetName) then
‘ Dictionaryから一瞬でオブジェクトを取り出す!
Dim targetResource As Resource
Set targetResource = dictResources(targetName)
‘ プロパティの更新
targetResource.StandardRate = 6000
MsgBox targetName & ” のコストを更新しました!”, vbInformation
Else
MsgBox targetName & ” が見つかりませんでした。”, vbExclamation
End If
‘ ==========================================
‘ Step 3: 後始末
‘ ==========================================
Application.ScreenUpdating = True
Set dictResources = Nothing
MsgBox “処理完了! 実行時間: ” & Format(Timer – t1, “0.00秒”) & “秒”, vbInformation
End Sub
このコードの美しさとポイント
1. `Application.ScreenUpdating = False` の魔法
MS Projectは、VBAからオブジェクトが変更されるたびに画面のガントチャートやリソースシートを再描画しようとします。これを止めるだけで、体感速度が何倍にも跳ね上がります。
2. ループは最初の1回だけ
`For Each` で回すのは、初期データの取り込み時(Step 1)の1回きり。あとはDictionaryの索引機能(`Exists` と キー指定)を使うため、検索に時間がかかりません。
—
現場でやりがちな「落とし穴(エラー)」と対策
初心者のうちは、DictionaryやProjectオブジェクトの扱いでいくつかの罠にハマりがちです。ここをクリアしておけばバッチリです。
1. `Nothing` のチェックを怠るな
MS Projectの `Resources` コレクションには、削除されたリソースの残骸や空行が含まれていることがあります。`For Each r In ActiveProject.Resources` を回すときは、必ず `If Not r Is Nothing Then` でガードしてください。これを忘れると「オブジェクト変数が設定されていません」という憎きエラーに襲われます。
2. キーの重複(大文字・小文字の罠)
Dictionaryはデフォルトで大文字・小文字を区別します(`A` と `a` は別物)。
もしリソース名の表記揺れが怖い場合は、Dictionaryを作成する際に以下のように文字列を小文字に統一してキーにするのがプロのテクニックです。
dictResources.Add LCase(r.Name), r
‘ 検索時も
If dictResources.Exists(LCase(targetName)) Then …
—
おわりに
いかがでしたでしょうか?
「リソースが多いからマクロが重いのは仕方ない」というのは、アルゴリズムを知らなかった昔の話です。
Dictionaryオブジェクトを味方につければ、どれほど巨大なプロジェクトファイルであっても、メモリ上で軽快に、そしてエレガントにリソースを操ることができます。
ここをクリアしたあなたなら、もうマクロの記録の卒業生ではありません立派な自動化エンジニアです。ぜひ、日々のプロジェクト管理の現場でこのテクニックを試してみてください。あなたの作業時間が劇的に短縮されることを約束します!
