こんにちは! 現場でバリバリ動く自動化システムを作っていますか?
今回は、MS Project VBAの真骨頂である「オブジェクトモデルの縦断と外部連携(Outlook)」を、実務でそのまま使えるレベルで解説します。
「マクロの記録」が存在しないMicrosoft Projectの世界では、オブジェクトの構造を頭に叩き込むことが第一歩です。しかし、怖がる必要はありません。ここをクリアすれば、あなたはもうProject VBAの初級者を卒業し、立派な自動化エンジニアの仲間入りです。
今回は、現場で最も要望が多い「タスクのDeadline(期限)をVBAで一括チェックし、期限超過(または間近)のタスクをOutlook経由で担当者にメール通知する仕組み」を作っていきましょう。
優しい先輩が、舞台裏の仕組みからコードの隅々まで丁寧に手取り足取り解説しますね。ここをクリアすれば、Project VBAの基本はバッチリですよ!
—
1. MS Projectオブジェクトモデルの「土地勘」を持とう
Excel VBAなら`Range(“A1”)`のように直感的にセルを触れますが、Projectの世界は少し構造が違います。まずは今回使うオブジェクトの「階層関係(ツリー構造)」を頭に思い浮かべましょう。
Application (全体のアプリ)
┗ ActiveProject (現在開いているプロジェクト)
┗ Tasks (タスクのコレクション)
┗ Task (個別のタスク)
┣ 属性: Name (タスク名)
┣ 属性: Deadline (期限日)
┣ 属性: Finish (完了予定日)
┗ 属性: ResourceNames (割当リソース名)
Project VBAで何かを操作するときは、この「上から下へのルート」を正しくコードでトレースしてあげる必要があります。この感覚さえ掴めば、どんな複雑な処理も怖くありません。
—
2. 実装の全体像と事前準備
実装する機能
1. 現在開いているプロジェクトの全タスクを上から順にスキャンする。
2. タスクに「Deadline(期限)」が設定されているか確認する。
3. 「期限が設定されており、かつ、本日の日付から指定日数を切っている(または超過している)」タスクを抽出する。
4. 担当者(ResourceNames)宛てに、Outlookを使って自動でアラートメールを作成・送信(または下書き保存)する。
事前準備(参照設定の魔法)
OutlookをVBAから操作するため、VBAの画面([Alt] + [F11])を開き、上部メニューの [ツール] > [参照設定] から、以下のチェックを入れておいてください。
- Microsoft Outlook XX.X Object Library (XX.Xの数字はExcelのバージョンにより異なります)
これをしておくことで、Outlookのオブジェクトをコード内でスマートに扱えるようになります(これを「事前バインディング」と呼び、入力補完が効くようになるので開発効率が爆上がりします)。
—
3. 実務仕様のVBAコード
それでは、そのままコピペして使える実務レベルのコードを公開します。標準モジュールに貼り付けて実行してください。
Option Explicit
‘ ==============================================================================
‘ 処理名: 期限超過タスク一括チェック&Outlook通知ツール
‘ 概要 : アクティブプロジェクトのタスクを走査し、Deadlineが迫っているものを
‘ 担当者へOutlook経由で通知します。
‘ ==============================================================================
Sub CheckTaskDeadlinesAndNotify()
‘ — 1. 変数の宣言とオブジェクトの取得 —
Dim prj As Project
Set prj = ActiveProject
Dim tsk As Task
Dim alertDays As Long
alertDays = 3 ‘ 【設定】何日前からアラート対象にするか(例: 3日前)
Dim targetCount As Long
targetCount = 0
‘ Outlookオブジェクト用の変数
Dim olApp As Object ‘ 互換性を考慮してLate Bindingにする場合もありますが今回は事前バインディング前提
Dim olMail As Outlook.MailItem
‘ Outlookが起動できるかチェック
On Error Resume Next
Set olApp = CreateObject(“Outlook.Application”)
On Error GoTo 0
If olApp Is Nothing Then
MsgBox “Outlookが起動できませんでした。処理を中止します。”, vbCritical, “エラー”
Exit Sub
End If
‘ — 2. タスクループの開始 —
‘ Projectのタスクループは、サマリータスク(親)や空行なども含まれるため注意が必要
For Each tsk In prj.Tasks
‘ 存在しないタスク(削除された行など)やマイルストーン、サマリータスクを除外する場合のガード
If Not tsk Is Nothing Then
If tsk.Summary = False And tsk.Milestone = False Then
‘ Deadline(期限)が設定されているか?
‘ Projectでは未設定の場合「1984/1/1」などの初期値、あるいはNAが返るため、有効な日付か判定
If tsk.Deadline <> #1/1/1984# And tsk.Deadline <> 0 Then
‘ 判定ロジック: 「期限日 – 本日の日付」が設定日数以下、かつ未完了(ActiveProject.StatusDate等を使うことも可能)
‘ ここでは単純に「今日から見てDeadlineがalertDays日以内、または経過している」を判定
If tsk.Deadline <= DateAdd("d", alertDays, Date) And tsk.PercentComplete < 100 Then
targetCount = targetCount + 1
' --- 3. Outlookメールの自動作成 ---
Set olMail = olApp.CreateItem(olMailItem)
With olMail
' 宛先(※Projectのリソース名がそのままOutlookのメールアドレスや名前に対応している前提)
' 実務ではアドレス帳とのマッピングが必要な場合もあります
.To = tsk.ResourceNames
.Subject = "【要対応】プロジェクト期限アラート: " & tsk.Name
.Body = tsk.ResourceNames & " さん" & vbCrLf & vbCrLf & _
"お疲れ様です。進捗管理ボットです。" & vbCrLf & _
"以下のタスクについて、期限(Deadline)が迫っているか、または超過しています。" & vbCrLf & vbCrLf & _
"・タスク名: " & tsk.Name & vbCrLf & _
"・期限 (Deadline): " & Format(tsk.Deadline, "yyyy/mm/dd") & vbCrLf & _
"・完了予定日 (Finish): " & Format(tsk.Finish, "yyyy/mm/dd") & vbCrLf & _
"・現在の進捗率: " & tsk.PercentComplete & "%" & vbCrLf & vbCrLf & _
"状況の確認とスケジュール調整をお願いいたします。"
' .Send とすると即時送信されますが、安全のため .Display(画面表示)または .Save(下書き保存)を推奨
.Display ' 画面に作成されたメールを表示する(送信ボタンは人間が押す)
'.Save ' 下書き保存する場合
End With
Set olMail = Nothing
End If
End If
End If
End If
Next tsk
' --- 4. 処理完了のフィードバック ---
MsgBox "チェックが完了しました。" & vbCrLf & _
"該当するタスク件数: " & targetCount & "件の通知メールを作成しました。", vbInformation, "完了"
End Sub
---
4. ここでハマる!陥りやすい罠とエンジニアの知見
実務でこのコードを動かすとき、多くの人が次のような「Project特有の罠」にハマります。先回りしてクリアしておきましょう。
罠1:Deadlineの未設定値が特殊すぎる問題
Excel感覚で `If tsk.Deadline = “” ` などと書くと、型不一致エラーになります。
MS Projectにおいて、Deadlineが設定されていない場合の内部値は `1984/1/1`(または数値の `0`)として扱われます。そのため、コード内にあるように `#1/1/1984#` 以外のもの、かつ有効な日付であるかを弾くガード節が絶対に必要です。
罠2:サマリータスク(親タスク)を拾ってしまう
プロジェクトには「大項目」のような親タスク(サマリータスク)が存在します。親タスクにもDeadlineが設定できるため、これを拾ってしまうと、子タスクの担当者ではなく親の管理者に無駄なメールが飛ぶか、エラーになります。
`If tsk.Summary = False` という条件を入れて、「実際に人が手を動かす末端のタスク(リソースがアサインされるタスク)」だけをピンポイントで狙うのがプロの技です。
罠3:リソース名とOutlookの宛先が一致しない
コードでは `.To = tsk.ResourceNames` としていますが、Project上のリソース名が「山田 太郎」となっている場合、Outlookの宛先としてそのままでは認識されないことがあります。
組織の運用に合わせて、リソース名からメールアドレスを引くマッピング関数(Dictionaryなどを使う)を挟むと、より実用的な堅牢なシステムになります。まずは社内アドレス帳の補完機能に頼れる環境でテストしてみてください。
—
まとめ
今回は、Project VBAのオブジェクトモデルを縦断し、DeadlineをトリガーにしてOutlookと連携する実用マクロを解説しました。
- プロジェクトのツリー構造(`Project` > `Tasks` > `Task`)を意識する
- Deadlineの初期値(1984年問題)やサマリータスクの除外など、Project特有の癖に対応する
- 外部アプリケーション(Outlook)との連携には「参照設定」と「エラーハンドリング」が不可欠
この仕組みを週に1回、定例会議の前に自動実行する(あるいはWindowsのタスクスケジューラやリボンからのカスタムボタンで起動する)ように組むだけで、プロジェクトの遅延リスクを劇的に減らすことができます。
「ここをこう変えたいんだけど、どうすればいい?」といった応用に関する質問があれば、いつでもエンジニア仲間として相談に乗りますよ。現場の自動化を、一緒に楽しんでいきましょう!
