<p><!--META
{
"title": "Access VBAでWMI Win32_Process監視",
"primary_category": "Office自動化",
"secondary_categories": ["VBA","Access","WMI","Windows"],
"tags": ["Win32_Process", "WMI", "GetObject", "VBA", "Access"],
"summary": "Access VBAを使ってWMIのWin32_Processクラスを監視し、特定のプロセス情報やCPU/メモリ使用率を取得・記録する方法を解説。外部ライブラリ不使用、性能チューニングを含む実践的なコード例を提供。",
"mermaid": true,
"verify_level": "L0",
"tweet_hint": {"text":"Access VBAでWMIのWin32_Processクラスを監視する方法を徹底解説!外部ライブラリなしでプロセス情報取得からAccess DBへの記録、性能チューニングまで。
#VBA #Access #WMI ","hashtags":["#VBA","#Access","#WMI"]},
"link_hints": ["https://learn.microsoft.com/en-us/windows/win32/cimwin32prov/win32-process"]
}
-->
本記事は<strong>Geminiの出力をプロンプト工学で整理した業務ドラフト(未検証)</strong>です。</p>
<h1 class="wp-block-heading">Access VBAでWMI Win32_Process監視</h1>
<h2 class="wp-block-heading">はじめに</h2>
<p>企業のIT環境では、基幹システムや特定のアプリケーションが常に稼働しているか、そのリソース使用状況が健全であるかを監視するニーズが頻繁に発生します。Windows Management Instrumentation (WMI) は、Windows OSの管理情報にアクセスするための強力なインターフェースを提供し、VBAからこれを利用することで、外部ライブラリに依存せずシステム状態を監視することが可能です。</p>
<p>本稿では、Access VBAを用いてWMIの <code>Win32_Process</code> クラスを監視し、特定のプロセスの稼働状況やリソース使用状況を取得・記録する具体的な方法を解説します。特に、VBAの標準機能とCOMオブジェクト <code>WbemScripting.SWbemLocator</code> を利用し、参照設定不要でWMIサービスに接続する手法に焦点を当てます。これにより、外部ライブラリの追加インストールを禁止する厳格な環境でも、システム監視ソリューションを構築できます。</p>
<p>監視の対象は以下の要件を満たします。</p>
<ul class="wp-block-list">
<li><p>特定のプロセス(例: <code>EXCEL.EXE</code>、<code>outlook.exe</code>)の存在確認。</p></li>
<li><p>実行中のプロセスの詳細情報(プロセスID、実行パス、CPU/メモリ使用率、起動時刻など)の取得。</p></li>
<li><p>取得した情報をAccessデータベースに記録し、履歴管理を可能にする。</p></li>
<li><p>大量データ処理を想定した性能チューニングを組み込む。</p></li>
</ul>
<h2 class="wp-block-heading">設計</h2>
<h3 class="wp-block-heading">監視の目的と対象</h3>
<p>特定の重要プロセスが予期せず終了していないか、または過剰なリソースを消費していないかを監視し、異常を検知することを目的とします。監視対象は <code>Win32_Process</code> クラスから得られる以下の情報とします。</p>
<ul class="wp-block-list">
<li><p><code>ProcessId</code>: プロセスを一意に識別するID。</p></li>
<li><p><code>Name</code>: プロセス名(例: <code>EXCEL.EXE</code>)。</p></li>
<li><p><code>CommandLine</code>: プロセスを起動したコマンドライン。</p></li>
<li><p><code>ExecutablePath</code>: 実行ファイルのフルパス。</p></li>
<li><p><code>CreationDate</code>: プロセスの起動時刻 (UTC)。</p></li>
<li><p><code>KernelModeTime</code>: カーネルモードで費やされた時間 (マイクロ秒単位)。</p></li>
<li><p><code>UserModeTime</code>: ユーザーモードで費やされた時間 (マイクロ秒単位)。</p></li>
<li><p><code>WorkingSetSize</code>: プロセスが現在使用している物理メモリ量 (バイト単位)。</p></li>
<li><p><code>VirtualSize</code>: プロセスが使用している仮想メモリ量 (バイト単位)。</p></li>
</ul>
<h3 class="wp-block-heading">処理フロー</h3>
<p>監視の基本的な処理は、WMIサービスへの接続、WQL (WMI Query Language) を用いた情報取得、そして取得したデータの処理・記録となります。</p>
<div class="wp-block-merpress-mermaidjs diagram-source-mermaid"><pre class="mermaid">
graph TD
A["開始"] --> B{"Access VBAアプリ起動"};
B --> C{"WMIサービス接続"};
C --> D{"監視対象プロセス指定"};
D --> E{"WQLクエリ作成"};
E --> F{"ExecQuery実行"};
F --> G{"結果セット取得"};
G --|プロセス情報あり| H["情報抽出と整形"];
G --|プロセス情報なし| I["ログ記録: プロセス不在"];
H --> J["Accessテーブルへ書き込み"];
J --> K{"次の監視タイミングまで待機"};
K --> E;
I --> K;
K --> L{"終了条件?"};
L --|はい| M["終了"];
L --|いいえ| E;
</pre></div>
<p><em>図1: WMIプロセス監視フローチャート</em></p>
<h3 class="wp-block-heading">データモデル</h3>
<p>監視結果を保存するためのAccessテーブルを設計します。</p>
<figure class="wp-block-table"><table>
<thead>
<tr>
<th style="text-align:left;">フィールド名</th>
<th style="text-align:left;">データ型</th>
<th style="text-align:left;">説明</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:left;"><code>ID</code></td>
<td style="text-align:left;">オートナンバー</td>
<td style="text-align:left;">主キー</td>
</tr>
<tr>
<td style="text-align:left;"><code>監視日時</code></td>
<td style="text-align:left;">日付/時刻</td>
<td style="text-align:left;">レコードが記録されたJST日時</td>
</tr>
<tr>
<td style="text-align:left;"><code>プロセス名</code></td>
<td style="text-align:left;">短いテキスト</td>
<td style="text-align:left;">プロセス名(例: <code>EXCEL.EXE</code>)</td>
</tr>
<tr>
<td style="text-align:left;"><code>プロセスID</code></td>
<td style="text-align:left;">長整数</td>
<td style="text-align:left;"><code>ProcessId</code></td>
</tr>
<tr>
<td style="text-align:left;"><code>実行パス</code></td>
<td style="text-align:left;">長いテキスト</td>
<td style="text-align:left;"><code>ExecutablePath</code></td>
</tr>
<tr>
<td style="text-align:left;"><code>コマンドライン</code></td>
<td style="text-align:left;">長いテキスト</td>
<td style="text-align:left;"><code>CommandLine</code></td>
</tr>
<tr>
<td style="text-align:left;"><code>起動日時</code></td>
<td style="text-align:left;">日付/時刻</td>
<td style="text-align:left;"><code>CreationDate</code> (UTCをJSTに変換)</td>
</tr>
<tr>
<td style="text-align:left;"><code>カーネル時間</code></td>
<td style="text-align:left;">長整数</td>
<td style="text-align:left;"><code>KernelModeTime</code> (マイクロ秒)</td>
</tr>
<tr>
<td style="text-align:left;"><code>ユーザー時間</code></td>
<td style="text-align:left;">長整数</td>
<td style="text-align:left;"><code>UserModeTime</code> (マイクロ秒)</td>
</tr>
<tr>
<td style="text-align:left;"><code>物理メモリ</code></td>
<td style="text-align:left;">長整数</td>
<td style="text-align:left;"><code>WorkingSetSize</code> (バイト)</td>
</tr>
<tr>
<td style="text-align:left;"><code>仮想メモリ</code></td>
<td style="text-align:left;">長整数</td>
<td style="text-align:left;"><code>VirtualSize</code> (バイト)</td>
</tr>
</tbody>
</table></figure>
<h3 class="wp-block-heading">パフォーマンス考慮点</h3>
<ul class="wp-block-list">
<li><p><strong>WMIクエリの最適化</strong>: 必要なプロパティのみを <code>SELECT</code> 句で指定し、<code>WHERE</code> 句で絞り込むことで、WMIサービスからのデータ転送量を最小限に抑えます。</p></li>
<li><p><strong>配列バッファ</strong>: WMIから取得したデータを一度配列に格納し、その配列のデータをまとめてAccessテーブルに書き込むことで、DAO/ADOのI/Oオーバーヘッドを削減します。</p></li>
<li><p><strong>トランザクション処理</strong>: 複数のレコードをAccessテーブルに書き込む場合、トランザクションを使用することで書き込み速度を大幅に向上させ、データの整合性を保証します。</p></li>
</ul>
<h2 class="wp-block-heading">実装</h2>
<p>Access VBAでのWMI利用は、<code>GetObject</code> 関数を通じて <code>WbemScripting</code> ライブラリのCOMオブジェクトにアクセスします。これはVBAがデフォルトで提供する機能であり、別途参照設定や外部ライブラリのインストールは不要です。よって、「外部ライブラリ禁止」の要件を満たします。<code>Declare PtrSafe</code> については、WMI自体はCOM経由でアクセスするため直接の必要はありませんが、Win32 APIを呼び出す際に必須となる点として補足します。</p>
<h3 class="wp-block-heading">コード1: 特定プロセスの存在確認と基本情報取得</h3>
<p>この例では、<code>EXCEL.EXE</code> プロセスが存在するか確認し、その基本情報をデバッグウィンドウに表示します。</p>
<pre data-enlighter-language="generic">' 標準モジュール (例: Module1) に記述
Option Compare Database
Option Explicit
' Access 64bit環境での互換性のため、Win32 APIを宣言する場合はPtrSafeが必要だが、
' WMIはCOMオブジェクト経由のため、この関数自体はDeclare PtrSafeを必要としない。
' 以下の宣言は、Win32 APIを使用する際の例示であり、本WMI監視には直接使用しない。
' Private Declare PtrSafe Function GetTickCount Lib "kernel32" () As Long
Sub MonitorSpecificProcess()
Dim objWMIService As Object
Dim colProcesses As Object
Dim objProcess As Object
Dim strComputer As String
Dim strProcessName As String
Dim strWQL As String
Dim ws As Object ' WScript.Shell (時間計測用として利用するが必須ではない)
Dim dblStartTime As Double
Dim dblEndTime As Double
strComputer = "." ' ローカルPC
strProcessName = "EXCEL.EXE" ' 監視したいプロセス名
Set ws = CreateObject("WScript.Shell")
On Error GoTo ErrorHandler
' --- 性能計測開始 ---
dblStartTime = Timer
' WMIサービスに接続
' GetObjectはVBAの標準機能で、別途参照設定不要なCOMオブジェクトへのアクセスを可能にする
Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")
' WQLクエリで特定のプロセス情報を取得
' 必要なプロパティのみSELECTすることで、データ転送量を削減
strWQL = "SELECT ProcessId, Name, CommandLine, ExecutablePath, CreationDate, " & _
"KernelModeTime, UserModeTime, WorkingSetSize, VirtualSize FROM Win32_Process WHERE Name = '" & strProcessName & "'"
Set colProcesses = objWMIService.ExecQuery(strWQL)
Debug.Print "--- " & strProcessName & " 監視結果 (" & Format(Now, "yyyy/mm/dd hh:nn:ss") & ") ---"
If colProcesses.Count > 0 Then
For Each objProcess In colProcesses
Debug.Print " プロセスID: " & objProcess.ProcessId
Debug.Print " プロセス名: " & objProcess.Name
Debug.Print " コマンドライン: " & objProcess.CommandLine
Debug.Print " 実行パス: " & objProcess.ExecutablePath
Debug.Print " 起動日時 (UTC): " & WMIStringToDate(objProcess.CreationDate)
Debug.Print " カーネル時間 (ms): " & objProcess.KernelModeTime / 10000 ' マイクロ秒からミリ秒
Debug.Print " ユーザー時間 (ms): " & objProcess.UserModeTime / 10000 ' マイクロ秒からミリ秒
Debug.Print " 物理メモリ (MB): " & Format(objProcess.WorkingSetSize / (1024 * 1024), "0.00")
Debug.Print " 仮想メモリ (MB): " & Format(objProcess.VirtualSize / (1024 * 1024), "0.00")
Debug.Print "--------------------------------------------------"
Next objProcess
Else
Debug.Print " プロセス '" & strProcessName & "' は実行されていません。"
End If
' --- 性能計測終了 ---
dblEndTime = Timer
Debug.Print "処理時間: " & Format(dblEndTime - dblStartTime, "0.000") & " 秒"
Exit_Sub:
Set objProcess = Nothing
Set colProcesses = Nothing
Set objWMIService = Nothing
Set ws = Nothing
Exit Sub
ErrorHandler:
MsgBox "エラーが発生しました: " & Err.Description, vbCritical
Resume Exit_Sub
End Sub
' WMIのUTC日付文字列をVBAのDate型に変換するヘルパー関数
' WMI日付フォーマット: yyyymmddHHMMSS.ffffff(+/-)zzz
Function WMIStringToDate(ByVal WMI_Date_String As String) As Date
Dim Year As Integer, Month As Integer, Day As Integer
Dim Hour As Integer, Minute As Integer, Second As Integer
Dim MicroSecond As Long
Dim TimeZoneOffset As Integer ' 分単位
Year = CInt(Mid(WMI_Date_String, 1, 4))
Month = CInt(Mid(WMI_Date_String, 5, 2))
Day = CInt(Mid(WMI_Date_String, 7, 2))
Hour = CInt(Mid(WMI_Date_String, 9, 2))
Minute = CInt(Mid(WMI_Date_String, 11, 2))
Second = CInt(Mid(WMI_Date_String, 13, 2))
' マイクロ秒は精度のため省略、必要なら利用
' MicroSecond = CLng(Mid(WMI_Date_String, 16, 6))
TimeZoneOffset = CInt(Mid(WMI_Date_String, 20, 3)) ' タイムゾーンオフセット(分)
WMIStringToDate = DateSerial(Year, Month, Day) + TimeSerial(Hour, Minute, Second)
' UTCからローカルタイムゾーン (JST) への変換
' Accessは通常JSTで動作するため、WMIのUTC時刻をそのまま利用するとJST-9hとなる。
' WMIStringToDate = DateAdd("n", -TimeZoneOffset, WMIStringToDate) ' WMIのタイムゾーンを考慮
' シンプルに現在のシステムタイムゾーンのオフセットを加減する
WMIStringToDate = DateAdd("h", 9, WMIStringToDate) ' UTCをJSTに変換 (日本の標準時+9時間)
End Function
</pre>
<p><em>コード1: 特定プロセスの情報取得</em></p>
<p><strong>コード1の実行手順:</strong></p>
<ol class="wp-block-list">
<li><p>Accessデータベースを開き、Alt + F11キーでVBAエディタを開きます。</p></li>
<li><p>左側のプロジェクトエクスプローラで、データベース名の下の「Microsoft Access オブジェクト」を右クリックし、「挿入」→「標準モジュール」を選択します。</p></li>
<li><p>新しいモジュールに上記のVBAコードをコピー&ペーストします。</p></li>
<li><p><code>MonitorSpecificProcess</code> プロシージャ内にカーソルを置き、F5キーを押して実行します。</p></li>
<li><p>結果はVBAエディタの下部にある「イミディエイトウィンドウ」(表示されていない場合はCtrl + Gで表示)に表示されます。</p></li>
</ol>
<h3 class="wp-block-heading">コード2: 複数のプロセスを定期的に監視し、Accessテーブルに履歴を記録</h3>
<p>この例では、複数の特定のプロセスを定期的に監視し、取得したリソース使用状況をAccessテーブルに記録します。<code>Form_Timer</code> イベントと連携させることで、自動的な定期監視を実現します。</p>
<p><strong>準備:</strong></p>
<ol class="wp-block-list">
<li><p>Accessデータベースに、以下の構造を持つテーブル <code>ProcessMonitorLog</code> を作成します。</p>
<ul>
<li><p><code>ID</code>: オートナンバー、主キー</p></li>
<li><p><code>監視日時</code>: 日付/時刻 (短い日付形式)</p></li>
<li><p><code>プロセス名</code>: 短いテキスト (255)</p></li>
<li><p><code>プロセスID</code>: 長整数</p></li>
<li><p><code>実行パス</code>: 長いテキスト</p></li>
<li><p><code>コマンドライン</code>: 長いテキスト</p></li>
<li><p><code>起動日時</code>: 日付/時刻 (短い日付形式)</p></li>
<li><p><code>カーネル時間</code>: 長整数</p></li>
<li><p><code>ユーザー時間</code>: 長整数</p></li>
<li><p><code>物理メモリ</code>: 長整数</p></li>
<li><p><code>仮想メモリ</code>: 長整数</p></li>
</ul></li>
<li><p>新しいフォームを作成し、名前を <code>frmProcessMonitor</code> とします。</p></li>
<li><p>フォームのプロパティで <code>タイマー間隔</code> を <code>60000</code> (60秒 = 1分) に設定します。</p></li>
<li><p>フォームにボタンなどを配置せず、非表示で実行することも可能です。</p></li>
</ol>
<pre data-enlighter-language="generic">' 標準モジュール (例: Module1) に記述
Option Compare Database
Option Explicit
' WMIのUTC日付文字列をVBAのDate型に変換するヘルパー関数
' (コード1と同じなので省略、Module1に既に存在することを前提)
' Function WMIStringToDate(...) As Date
' ...
' End Function
Sub StartProcessMonitoringForm()
' 監視フォームを非表示で開く (Timerイベントが実行される)
DoCmd.OpenForm "frmProcessMonitor", acNormal, , , , acHidden
End Sub
Sub StopProcessMonitoringForm()
' 監視フォームを閉じる
DoCmd.Close acForm, "frmProcessMonitor"
End Sub
' フォームモジュール (frmProcessMonitor) に記述
Option Compare Database
Option Explicit
Private Const PROCESS_LIST As String = "'EXCEL.EXE','OUTLOOK.EXE','WINWORD.EXE','chrome.exe'"
Private Const MAX_BATCH_SIZE As Long = 50 ' バッチ処理の最大レコード数
Private Sub Form_Load()
Me.TimerInterval = 60000 ' 60秒 (1分) ごとにTimerイベント発生
Debug.Print "プロセス監視を開始しました。(" & Format(Now, "yyyy/mm/dd hh:nn:ss") & ")"
End Sub
Private Sub Form_Unload(Cancel As Integer)
Debug.Print "プロセス監視を停止しました。(" & Format(Now, "yyyy/mm/dd hh:nn:ss") & ")"
End Sub
Private Sub Form_Timer()
Dim objWMIService As Object
Dim colProcesses As Object
Dim objProcess As Object
Dim strWQL As String
Dim db As DAO.Database
Dim rs As DAO.Recordset
Dim varProcessData() As Variant ' 配列バッファ
Dim lngCounter As Long
Dim lngRow As Long
Dim dblStartTime As Double
Dim dblEndTime As Double
' --- 性能計測開始 ---
dblStartTime = Timer
On Error GoTo ErrorHandler
' 画面更新を停止し、フォームの処理を高速化
' Excel.Application.ScreenUpdating に相当する機能はAccessにはないが、
' ここではレコードセット操作を高速化する。
Application.SetOption "Confirm Record Changes", False
Application.SetOption "Confirm Document Deletions", False
Application.SetOption "Confirm Action Queries", False
Set db = CurrentDb
' WMIサービスに接続
Set objWMIService = GetObject("winmgmts:\\.\root\cimv2")
' 監視対象プロセスをWHERE句で指定
strWQL = "SELECT ProcessId, Name, CommandLine, ExecutablePath, CreationDate, " & _
"KernelModeTime, UserModeTime, WorkingSetSize, VirtualSize FROM Win32_Process WHERE Name IN (" & PROCESS_LIST & ")"
Set colProcesses = objWMIService.ExecQuery(strWQL)
' 配列バッファの初期化
' 取得するプロパティ数: 10
ReDim varProcessData(0 To colProcesses.Count - 1, 0 To 9)
lngCounter = 0
' --- 取得データを配列に格納 ---
For Each objProcess In colProcesses
varProcessData(lngCounter, 0) = Now() ' 監視日時 (JST)
varProcessData(lngCounter, 1) = objProcess.Name
varProcessData(lngCounter, 2) = objProcess.ProcessId
varProcessData(lngCounter, 3) = objProcess.ExecutablePath
varProcessData(lngCounter, 4) = objProcess.CommandLine
varProcessData(lngCounter, 5) = WMIStringToDate(objProcess.CreationDate) ' UTCをJSTに変換
varProcessData(lngCounter, 6) = objProcess.KernelModeTime
varProcessData(lngCounter, 7) = objProcess.UserModeTime
varProcessData(lngCounter, 8) = objProcess.WorkingSetSize
varProcessData(lngCounter, 9) = objProcess.VirtualSize
lngCounter = lngCounter + 1
Next objProcess
If lngCounter > 0 Then
' --- DAOトランザクションとバッチ処理で高速書き込み ---
db.BeginTrans
Set rs = db.OpenRecordset("ProcessMonitorLog", dbOpenDynaset)
For lngRow = 0 To lngCounter - 1
rs.AddNew
rs!監視日時 = varProcessData(lngRow, 0)
rs!プロセス名 = varProcessData(lngRow, 1)
rs!プロセスID = varProcessData(lngRow, 2)
rs!実行パス = varProcessData(lngRow, 3)
rs!コマンドライン = varProcessData(lngRow, 4)
rs!起動日時 = varProcessData(lngRow, 5)
rs!カーネル時間 = varProcessData(lngRow, 6)
rs!ユーザー時間 = varProcessData(lngRow, 7)
rs!物理メモリ = varProcessData(lngRow, 8)
rs!仮想メモリ = varProcessData(lngRow, 9)
rs.Update
' 一定数ごとにトランザクションをコミット (メモリ消費を抑えつつ高速化)
If (lngRow + 1) Mod MAX_BATCH_SIZE = 0 Then
db.CommitTrans
db.BeginTrans
End If
Next lngRow
' 残りのレコードをコミット
db.CommitTrans
Debug.Print lngCounter & " 件のプロセス情報をデータベースに記録しました。"
Else
Debug.Print "監視対象プロセスは実行されていませんでした。"
End If
' --- 性能計測終了 ---
dblEndTime = Timer
Debug.Print "処理時間: " & Format(dblEndTime - dblStartTime, "0.000") & " 秒"
Exit_Form_Timer:
' 元の設定に戻す
Application.SetOption "Confirm Record Changes", True
Application.SetOption "Confirm Document Deletions", True
Application.SetOption "Confirm Action Queries", True
Set rs = Nothing
Set db = Nothing
Set objProcess = Nothing
Set colProcesses = Nothing
Set objWMIService = Nothing
Exit Sub
ErrorHandler:
If db.Transactions Then ' エラー時はトランザクションをロールバック
db.Rollback
End If
MsgBox "プロセス監視中にエラーが発生しました: " & Err.Description, vbCritical
Resume Exit_Form_Timer
End Sub
</pre>
<p><em>コード2: 定期監視とデータベースへの記録</em></p>
<p><strong>性能チューニングの数値効果 (例):</strong></p>
<ul class="wp-block-list">
<li><p><strong>配列バッファとトランザクションなしの場合</strong>: 1000レコードの挿入に約 500ms ~ 1000ms かかることがあります。</p></li>
<li><p><strong>配列バッファとトランザクションありの場合</strong>: 1000レコードの挿入に約 50ms ~ 100ms で完了することが可能です。</p></li>
<li><p><strong>バッチ処理</strong>: <code>MAX_BATCH_SIZE</code> を設定することで、トランザクションの粒度を調整し、メモリ使用量とI/O効率のバランスを取ることができます。今回の例では、<code>MAX_BATCH_SIZE = 50</code> の場合、1回のコミットで50件ずつ書き込むため、大規模なデータセットでも安定したパフォーマンスを期待できます。</p></li>
</ul>
<p><strong>コード2の実行手順:</strong></p>
<ol class="wp-block-list">
<li><p>上記「準備」で述べたテーブル <code>ProcessMonitorLog</code> とフォーム <code>frmProcessMonitor</code> を作成します。</p></li>
<li><p><code>Module1</code> に <code>WMIStringToDate</code> 関数(コード1に記載)と <code>StartProcessMonitoringForm</code>、<code>StopProcessMonitoringForm</code> プロシージャをコピー&ペーストします。</p></li>
<li><p><code>frmProcessMonitor</code> のフォームモジュールに <code>Form_Load</code>、<code>Form_Unload</code>、<code>Form_Timer</code> イベントプロシージャをコピー&ペーストします。</p></li>
<li><p><code>Module1</code> の <code>StartProcessMonitoringForm</code> を実行することで、フォームが非表示で開き、1分ごとにプロセス監視が開始され、結果が <code>ProcessMonitorLog</code> テーブルに記録されます。</p></li>
<li><p>監視を停止するには、<code>Module1</code> の <code>StopProcessMonitoringForm</code> を実行します。</p></li>
</ol>
<p><strong>ロールバック方法:</strong></p>
<ul class="wp-block-list">
<li><p><strong>テーブルデータ</strong>: <code>DELETE FROM ProcessMonitorLog;</code> SQLクエリを実行することで、記録されたすべてのデータを削除できます。</p></li>
<li><p><strong>VBAコード</strong>: 作成した標準モジュールやフォームモジュールのVBAコードを削除します。</p></li>
<li><p><strong>データベースオブジェクト</strong>: 作成したテーブル <code>ProcessMonitorLog</code> とフォーム <code>frmProcessMonitor</code> をAccessのナビゲーションウィンドウから削除します。</p></li>
</ul>
<h2 class="wp-block-heading">検証</h2>
<ol class="wp-block-list">
<li><p><strong>プロセスの起動・停止確認</strong>:</p>
<ul>
<li><p>コード1を実行し、<code>EXCEL.EXE</code> が起動している場合と停止している場合の両方で動作を確認します。</p></li>
<li><p>コード2を開始し、監視対象のプロセスを起動・終了させて、<code>ProcessMonitorLog</code> テーブルに正確な情報が記録されるか、またはプロセス不在のログが適切に出力されるかを確認します。</p></li>
</ul></li>
<li><p><strong>リソース情報の取得</strong>:</p>
<ul>
<li><p>取得された <code>WorkingSetSize</code> や <code>VirtualSize</code> が、タスクマネージャーで表示される値とおおよそ一致するか確認します。</p></li>
<li><p><code>CreationDate</code> がプロセスの起動時刻と一致するか確認します(WMIStringToDate関数によるJST変換が正しいか)。</p></li>
</ul></li>
<li><p><strong>性能確認</strong>:</p>
<ul>
<li><p>大量のプロセスを監視対象に含めるか、監視間隔を短くして、<code>ProcessMonitorLog</code> テーブルへの書き込み速度が十分に速いかを確認します。イミディエイトウィンドウに出力される「処理時間」を参考にします。</p></li>
<li><p>数百件以上のレコードが追加されたテーブルに対して、トランザクション処理が有効に機能していることを体感します。</p></li>
</ul></li>
<li><p><strong>エラーハンドリング</strong>:</p>
<ul>
<li><p>WMIサービスが停止している状態で実行し、適切にエラーメッセージが表示されるか確認します。</p></li>
<li><p>存在しないコンピュータ名を <code>strComputer</code> に設定した場合の動作を確認します。</p></li>
</ul></li>
</ol>
<h2 class="wp-block-heading">運用</h2>
<h3 class="wp-block-heading">定期実行のスケジュール設定</h3>
<p>Accessアプリケーションを定期的に起動し、監視を開始するためには、Windowsのタスクスケジューラを利用するのが効果的です。</p>
<p><strong>手順:</strong></p>
<ol class="wp-block-list">
<li><p><strong>AccessデータベースをMDB/ACCDB形式で保存</strong>し、監視用フォーム <code>frmProcessMonitor</code> が自動的に起動するように設定します。例えば、データベース起動時に <code>StartProcessMonitoringForm</code> を呼び出すマクロをAutoExecに設定するか、データベースの起動オプションで特定のフォームを開くようにします。</p></li>
<li><p><strong>タスクスケジューラの作成</strong>:</p>
<ul>
<li><p><code>タスクスケジューラ</code> を開き、「基本タスクの作成」を選択。</p></li>
<li><p>タスク名と説明を入力。</p></li>
<li><p>トリガーを「毎日」や「週ごと」など、監視間隔に合わせて設定します。</p></li>
<li><p>操作で「プログラムの開始」を選択し、<code>プログラム/スクリプト</code> に <code>msaccess.exe</code> のフルパスを、<code>引数の追加</code> に監視対象のAccessデータベースファイル (<code>.accdb</code> または <code>.mdb</code>) のフルパスを指定します。</p></li>
</ul></li>
</ol>
<h3 class="wp-block-heading">ログ管理</h3>
<p><code>ProcessMonitorLog</code> テーブルは時間の経過とともに肥大化するため、定期的なログのアーカイブや削除ポリシーを確立することが重要です。</p>
<ul class="wp-block-list">
<li><p>古いレコード(例: 3ヶ月以上前のデータ)を別のアーカイブテーブルに移動または削除するVBAプロシージャを作成し、タスクスケジューラで月に一度実行します。</p></li>
<li><p>テーブルサイズが一定値を超えた場合に警告するロジックを追加することも検討します。</p></li>
</ul>
<h3 class="wp-block-heading">監視アラート</h3>
<p>プロセスが予期せず停止した場合や、リソース使用率が異常に高くなった場合に、メール通知やポップアップアラートを発信する機能を実装することで、より実用的な監視システムとなります。</p>
<ul class="wp-block-list">
<li><p>Outlook COMオブジェクトを利用してメールを送信するVBAコードを追加します。</p></li>
<li><p>特定の閾値を超えた場合に、そのイベントをトリガーとしてアラートを発動させます。</p></li>
</ul>
<h2 class="wp-block-heading">落とし穴と対策</h2>
<ol class="wp-block-list">
<li><p><strong>WMIクエリのパフォーマンス問題</strong>:</p>
<ul>
<li><p><strong>落とし穴</strong>: <code>SELECT * FROM Win32_Process</code> のようにすべてのプロパティを取得するクエリは、WMIサービスへの負荷が高く、結果セットが大きくなり、VBAでの処理時間も長くなります。特に大量のプロセスが実行されている環境では顕著です。</p></li>
<li><p><strong>対策</strong>: 常に必要なプロパティのみを <code>SELECT</code> 句で指定し、<code>WHERE</code> 句で監視対象を明確に絞り込みます。例えば <code>WHERE Name = 'EXCEL.EXE'</code> や <code>WHERE ProcessId = 1234</code> のようにします。</p></li>
</ul></li>
<li><p><strong>セキュリティ設定と権限</strong>:</p>
<ul>
<li><p><strong>落とし穴</strong>: WMIサービスへのアクセスには適切な権限が必要です。特にリモートコンピュータのWMIを監視する場合、権限不足でアクセスが拒否されることがあります。</p></li>
<li><p><strong>対策</strong>: ローカルPCでの監視は通常問題ありませんが、リモート監視の場合は適切なユーザーアカウントとパスワード (<code>objWMIService = GetObject("winmgmts:{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2")</code> のように接続文字列を調整) を指定するか、DCOM設定を確認します。</p></li>
</ul></li>
<li><p><strong>WMIサービスの停止</strong>:</p>
<ul>
<li><p><strong>落とし穴</strong>: WMIサービス自体が停止している場合、監視は機能しません。エラーハンドリングで適切に処理しないと、アプリケーションがクラッシュする可能性があります。</p></li>
<li><p><strong>対策</strong>: WMIサービス接続時にエラーが発生した場合 (<code>On Error GoTo ErrorHandler</code>)、その旨をユーザーに通知し、プログラムを安全に終了させるか、WMIサービスの再起動を試みるなどの対応を検討します。</p></li>
</ul></li>
<li><p><strong>64bit VBA環境とCOMオブジェクト</strong>:</p>
<ul>
<li><p><strong>落とし穴</strong>: 32bit環境で動作していたコードが64bit Accessで動作しない場合があります。特にWin32 APIの <code>Declare</code> ステートメントには <code>PtrSafe</code> が必須です。</p></li>
<li><p><strong>対策</strong>: 本記事で用いた <code>GetObject</code> によるCOMオブジェクトの利用は、VBAのネイティブ機能として提供されるため、通常32bit/64bit環境の違いを意識する必要はありません。ただし、もしWin32 APIを直接呼び出す場合は、必ず <code>Declare PtrSafe</code> を使用します。</p></li>
</ul></li>
</ol>
<h2 class="wp-block-heading">まとめ</h2>
<p>本稿では、Access VBAを用いてWMI <code>Win32_Process</code> クラスを監視し、特定のプロセスの稼働状況やリソース使用状況をデータベースに記録する実践的な方法を解説しました。外部ライブラリを使用せず、VBAの標準機能とCOMオブジェクトを駆使することで、厳格な環境下でもシステム監視ソリューションを構築できることを示しました。</p>
<p>特に、以下の点を強調しました。</p>
<ul class="wp-block-list">
<li><p><code>GetObject</code> 関数によるWMIサービスへの容易なアクセス。</p></li>
<li><p>WQLクエリによる効率的なプロセス情報取得。</p></li>
<li><p>配列バッファとDAOトランザクションを組み合わせた、Accessデータベースへの高速なデータ書き込み。</p></li>
<li><p><code>Form_Timer</code> イベントを活用した定期的な監視の実現。</p></li>
</ul>
<p>これらの手法を応用することで、Access VBAの汎用性を活かし、業務プロセス監視、リソース使用状況の傾向分析、異常検知アラートなど、さまざまなニーズに対応する強力な管理ツールを構築することが可能です。システムの安定稼働に貢献するため、ぜひ本記事のコードと知見をご活用ください。</p>
本記事はGeminiの出力をプロンプト工学で整理した業務ドラフト(未検証) です。
Access VBAでWMI Win32_Process監視
はじめに
企業のIT環境では、基幹システムや特定のアプリケーションが常に稼働しているか、そのリソース使用状況が健全であるかを監視するニーズが頻繁に発生します。Windows Management Instrumentation (WMI) は、Windows OSの管理情報にアクセスするための強力なインターフェースを提供し、VBAからこれを利用することで、外部ライブラリに依存せずシステム状態を監視することが可能です。
本稿では、Access VBAを用いてWMIの Win32_Process
クラスを監視し、特定のプロセスの稼働状況やリソース使用状況を取得・記録する具体的な方法を解説します。特に、VBAの標準機能とCOMオブジェクト WbemScripting.SWbemLocator
を利用し、参照設定不要でWMIサービスに接続する手法に焦点を当てます。これにより、外部ライブラリの追加インストールを禁止する厳格な環境でも、システム監視ソリューションを構築できます。
監視の対象は以下の要件を満たします。
特定のプロセス(例: EXCEL.EXE
、outlook.exe
)の存在確認。
実行中のプロセスの詳細情報(プロセスID、実行パス、CPU/メモリ使用率、起動時刻など)の取得。
取得した情報をAccessデータベースに記録し、履歴管理を可能にする。
大量データ処理を想定した性能チューニングを組み込む。
設計
監視の目的と対象
特定の重要プロセスが予期せず終了していないか、または過剰なリソースを消費していないかを監視し、異常を検知することを目的とします。監視対象は Win32_Process
クラスから得られる以下の情報とします。
ProcessId
: プロセスを一意に識別するID。
Name
: プロセス名(例: EXCEL.EXE
)。
CommandLine
: プロセスを起動したコマンドライン。
ExecutablePath
: 実行ファイルのフルパス。
CreationDate
: プロセスの起動時刻 (UTC)。
KernelModeTime
: カーネルモードで費やされた時間 (マイクロ秒単位)。
UserModeTime
: ユーザーモードで費やされた時間 (マイクロ秒単位)。
WorkingSetSize
: プロセスが現在使用している物理メモリ量 (バイト単位)。
VirtualSize
: プロセスが使用している仮想メモリ量 (バイト単位)。
処理フロー
監視の基本的な処理は、WMIサービスへの接続、WQL (WMI Query Language) を用いた情報取得、そして取得したデータの処理・記録となります。
graph TD
A["開始"] --> B{"Access VBAアプリ起動"};
B --> C{"WMIサービス接続"};
C --> D{"監視対象プロセス指定"};
D --> E{"WQLクエリ作成"};
E --> F{"ExecQuery実行"};
F --> G{"結果セット取得"};
G --|プロセス情報あり| H["情報抽出と整形"];
G --|プロセス情報なし| I["ログ記録: プロセス不在"];
H --> J["Accessテーブルへ書き込み"];
J --> K{"次の監視タイミングまで待機"};
K --> E;
I --> K;
K --> L{"終了条件?"};
L --|はい| M["終了"];
L --|いいえ| E;
図1: WMIプロセス監視フローチャート
データモデル
監視結果を保存するためのAccessテーブルを設計します。
フィールド名
データ型
説明
ID
オートナンバー
主キー
監視日時
日付/時刻
レコードが記録されたJST日時
プロセス名
短いテキスト
プロセス名(例: EXCEL.EXE
)
プロセスID
長整数
ProcessId
実行パス
長いテキスト
ExecutablePath
コマンドライン
長いテキスト
CommandLine
起動日時
日付/時刻
CreationDate
(UTCをJSTに変換)
カーネル時間
長整数
KernelModeTime
(マイクロ秒)
ユーザー時間
長整数
UserModeTime
(マイクロ秒)
物理メモリ
長整数
WorkingSetSize
(バイト)
仮想メモリ
長整数
VirtualSize
(バイト)
パフォーマンス考慮点
WMIクエリの最適化 : 必要なプロパティのみを SELECT
句で指定し、WHERE
句で絞り込むことで、WMIサービスからのデータ転送量を最小限に抑えます。
配列バッファ : WMIから取得したデータを一度配列に格納し、その配列のデータをまとめてAccessテーブルに書き込むことで、DAO/ADOのI/Oオーバーヘッドを削減します。
トランザクション処理 : 複数のレコードをAccessテーブルに書き込む場合、トランザクションを使用することで書き込み速度を大幅に向上させ、データの整合性を保証します。
実装
Access VBAでのWMI利用は、GetObject
関数を通じて WbemScripting
ライブラリのCOMオブジェクトにアクセスします。これはVBAがデフォルトで提供する機能であり、別途参照設定や外部ライブラリのインストールは不要です。よって、「外部ライブラリ禁止」の要件を満たします。Declare PtrSafe
については、WMI自体はCOM経由でアクセスするため直接の必要はありませんが、Win32 APIを呼び出す際に必須となる点として補足します。
コード1: 特定プロセスの存在確認と基本情報取得
この例では、EXCEL.EXE
プロセスが存在するか確認し、その基本情報をデバッグウィンドウに表示します。
' 標準モジュール (例: Module1) に記述
Option Compare Database
Option Explicit
' Access 64bit環境での互換性のため、Win32 APIを宣言する場合はPtrSafeが必要だが、
' WMIはCOMオブジェクト経由のため、この関数自体はDeclare PtrSafeを必要としない。
' 以下の宣言は、Win32 APIを使用する際の例示であり、本WMI監視には直接使用しない。
' Private Declare PtrSafe Function GetTickCount Lib "kernel32" () As Long
Sub MonitorSpecificProcess()
Dim objWMIService As Object
Dim colProcesses As Object
Dim objProcess As Object
Dim strComputer As String
Dim strProcessName As String
Dim strWQL As String
Dim ws As Object ' WScript.Shell (時間計測用として利用するが必須ではない)
Dim dblStartTime As Double
Dim dblEndTime As Double
strComputer = "." ' ローカルPC
strProcessName = "EXCEL.EXE" ' 監視したいプロセス名
Set ws = CreateObject("WScript.Shell")
On Error GoTo ErrorHandler
' --- 性能計測開始 ---
dblStartTime = Timer
' WMIサービスに接続
' GetObjectはVBAの標準機能で、別途参照設定不要なCOMオブジェクトへのアクセスを可能にする
Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")
' WQLクエリで特定のプロセス情報を取得
' 必要なプロパティのみSELECTすることで、データ転送量を削減
strWQL = "SELECT ProcessId, Name, CommandLine, ExecutablePath, CreationDate, " & _
"KernelModeTime, UserModeTime, WorkingSetSize, VirtualSize FROM Win32_Process WHERE Name = '" & strProcessName & "'"
Set colProcesses = objWMIService.ExecQuery(strWQL)
Debug.Print "--- " & strProcessName & " 監視結果 (" & Format(Now, "yyyy/mm/dd hh:nn:ss") & ") ---"
If colProcesses.Count > 0 Then
For Each objProcess In colProcesses
Debug.Print " プロセスID: " & objProcess.ProcessId
Debug.Print " プロセス名: " & objProcess.Name
Debug.Print " コマンドライン: " & objProcess.CommandLine
Debug.Print " 実行パス: " & objProcess.ExecutablePath
Debug.Print " 起動日時 (UTC): " & WMIStringToDate(objProcess.CreationDate)
Debug.Print " カーネル時間 (ms): " & objProcess.KernelModeTime / 10000 ' マイクロ秒からミリ秒
Debug.Print " ユーザー時間 (ms): " & objProcess.UserModeTime / 10000 ' マイクロ秒からミリ秒
Debug.Print " 物理メモリ (MB): " & Format(objProcess.WorkingSetSize / (1024 * 1024), "0.00")
Debug.Print " 仮想メモリ (MB): " & Format(objProcess.VirtualSize / (1024 * 1024), "0.00")
Debug.Print "--------------------------------------------------"
Next objProcess
Else
Debug.Print " プロセス '" & strProcessName & "' は実行されていません。"
End If
' --- 性能計測終了 ---
dblEndTime = Timer
Debug.Print "処理時間: " & Format(dblEndTime - dblStartTime, "0.000") & " 秒"
Exit_Sub:
Set objProcess = Nothing
Set colProcesses = Nothing
Set objWMIService = Nothing
Set ws = Nothing
Exit Sub
ErrorHandler:
MsgBox "エラーが発生しました: " & Err.Description, vbCritical
Resume Exit_Sub
End Sub
' WMIのUTC日付文字列をVBAのDate型に変換するヘルパー関数
' WMI日付フォーマット: yyyymmddHHMMSS.ffffff(+/-)zzz
Function WMIStringToDate(ByVal WMI_Date_String As String) As Date
Dim Year As Integer, Month As Integer, Day As Integer
Dim Hour As Integer, Minute As Integer, Second As Integer
Dim MicroSecond As Long
Dim TimeZoneOffset As Integer ' 分単位
Year = CInt(Mid(WMI_Date_String, 1, 4))
Month = CInt(Mid(WMI_Date_String, 5, 2))
Day = CInt(Mid(WMI_Date_String, 7, 2))
Hour = CInt(Mid(WMI_Date_String, 9, 2))
Minute = CInt(Mid(WMI_Date_String, 11, 2))
Second = CInt(Mid(WMI_Date_String, 13, 2))
' マイクロ秒は精度のため省略、必要なら利用
' MicroSecond = CLng(Mid(WMI_Date_String, 16, 6))
TimeZoneOffset = CInt(Mid(WMI_Date_String, 20, 3)) ' タイムゾーンオフセット(分)
WMIStringToDate = DateSerial(Year, Month, Day) + TimeSerial(Hour, Minute, Second)
' UTCからローカルタイムゾーン (JST) への変換
' Accessは通常JSTで動作するため、WMIのUTC時刻をそのまま利用するとJST-9hとなる。
' WMIStringToDate = DateAdd("n", -TimeZoneOffset, WMIStringToDate) ' WMIのタイムゾーンを考慮
' シンプルに現在のシステムタイムゾーンのオフセットを加減する
WMIStringToDate = DateAdd("h", 9, WMIStringToDate) ' UTCをJSTに変換 (日本の標準時+9時間)
End Function
コード1: 特定プロセスの情報取得
コード1の実行手順:
Accessデータベースを開き、Alt + F11キーでVBAエディタを開きます。
左側のプロジェクトエクスプローラで、データベース名の下の「Microsoft Access オブジェクト」を右クリックし、「挿入」→「標準モジュール」を選択します。
新しいモジュールに上記のVBAコードをコピー&ペーストします。
MonitorSpecificProcess
プロシージャ内にカーソルを置き、F5キーを押して実行します。
結果はVBAエディタの下部にある「イミディエイトウィンドウ」(表示されていない場合はCtrl + Gで表示)に表示されます。
コード2: 複数のプロセスを定期的に監視し、Accessテーブルに履歴を記録
この例では、複数の特定のプロセスを定期的に監視し、取得したリソース使用状況をAccessテーブルに記録します。Form_Timer
イベントと連携させることで、自動的な定期監視を実現します。
準備:
Accessデータベースに、以下の構造を持つテーブル ProcessMonitorLog
を作成します。
ID
: オートナンバー、主キー
監視日時
: 日付/時刻 (短い日付形式)
プロセス名
: 短いテキスト (255)
プロセスID
: 長整数
実行パス
: 長いテキスト
コマンドライン
: 長いテキスト
起動日時
: 日付/時刻 (短い日付形式)
カーネル時間
: 長整数
ユーザー時間
: 長整数
物理メモリ
: 長整数
仮想メモリ
: 長整数
新しいフォームを作成し、名前を frmProcessMonitor
とします。
フォームのプロパティで タイマー間隔
を 60000
(60秒 = 1分) に設定します。
フォームにボタンなどを配置せず、非表示で実行することも可能です。
' 標準モジュール (例: Module1) に記述
Option Compare Database
Option Explicit
' WMIのUTC日付文字列をVBAのDate型に変換するヘルパー関数
' (コード1と同じなので省略、Module1に既に存在することを前提)
' Function WMIStringToDate(...) As Date
' ...
' End Function
Sub StartProcessMonitoringForm()
' 監視フォームを非表示で開く (Timerイベントが実行される)
DoCmd.OpenForm "frmProcessMonitor", acNormal, , , , acHidden
End Sub
Sub StopProcessMonitoringForm()
' 監視フォームを閉じる
DoCmd.Close acForm, "frmProcessMonitor"
End Sub
' フォームモジュール (frmProcessMonitor) に記述
Option Compare Database
Option Explicit
Private Const PROCESS_LIST As String = "'EXCEL.EXE','OUTLOOK.EXE','WINWORD.EXE','chrome.exe'"
Private Const MAX_BATCH_SIZE As Long = 50 ' バッチ処理の最大レコード数
Private Sub Form_Load()
Me.TimerInterval = 60000 ' 60秒 (1分) ごとにTimerイベント発生
Debug.Print "プロセス監視を開始しました。(" & Format(Now, "yyyy/mm/dd hh:nn:ss") & ")"
End Sub
Private Sub Form_Unload(Cancel As Integer)
Debug.Print "プロセス監視を停止しました。(" & Format(Now, "yyyy/mm/dd hh:nn:ss") & ")"
End Sub
Private Sub Form_Timer()
Dim objWMIService As Object
Dim colProcesses As Object
Dim objProcess As Object
Dim strWQL As String
Dim db As DAO.Database
Dim rs As DAO.Recordset
Dim varProcessData() As Variant ' 配列バッファ
Dim lngCounter As Long
Dim lngRow As Long
Dim dblStartTime As Double
Dim dblEndTime As Double
' --- 性能計測開始 ---
dblStartTime = Timer
On Error GoTo ErrorHandler
' 画面更新を停止し、フォームの処理を高速化
' Excel.Application.ScreenUpdating に相当する機能はAccessにはないが、
' ここではレコードセット操作を高速化する。
Application.SetOption "Confirm Record Changes", False
Application.SetOption "Confirm Document Deletions", False
Application.SetOption "Confirm Action Queries", False
Set db = CurrentDb
' WMIサービスに接続
Set objWMIService = GetObject("winmgmts:\\.\root\cimv2")
' 監視対象プロセスをWHERE句で指定
strWQL = "SELECT ProcessId, Name, CommandLine, ExecutablePath, CreationDate, " & _
"KernelModeTime, UserModeTime, WorkingSetSize, VirtualSize FROM Win32_Process WHERE Name IN (" & PROCESS_LIST & ")"
Set colProcesses = objWMIService.ExecQuery(strWQL)
' 配列バッファの初期化
' 取得するプロパティ数: 10
ReDim varProcessData(0 To colProcesses.Count - 1, 0 To 9)
lngCounter = 0
' --- 取得データを配列に格納 ---
For Each objProcess In colProcesses
varProcessData(lngCounter, 0) = Now() ' 監視日時 (JST)
varProcessData(lngCounter, 1) = objProcess.Name
varProcessData(lngCounter, 2) = objProcess.ProcessId
varProcessData(lngCounter, 3) = objProcess.ExecutablePath
varProcessData(lngCounter, 4) = objProcess.CommandLine
varProcessData(lngCounter, 5) = WMIStringToDate(objProcess.CreationDate) ' UTCをJSTに変換
varProcessData(lngCounter, 6) = objProcess.KernelModeTime
varProcessData(lngCounter, 7) = objProcess.UserModeTime
varProcessData(lngCounter, 8) = objProcess.WorkingSetSize
varProcessData(lngCounter, 9) = objProcess.VirtualSize
lngCounter = lngCounter + 1
Next objProcess
If lngCounter > 0 Then
' --- DAOトランザクションとバッチ処理で高速書き込み ---
db.BeginTrans
Set rs = db.OpenRecordset("ProcessMonitorLog", dbOpenDynaset)
For lngRow = 0 To lngCounter - 1
rs.AddNew
rs!監視日時 = varProcessData(lngRow, 0)
rs!プロセス名 = varProcessData(lngRow, 1)
rs!プロセスID = varProcessData(lngRow, 2)
rs!実行パス = varProcessData(lngRow, 3)
rs!コマンドライン = varProcessData(lngRow, 4)
rs!起動日時 = varProcessData(lngRow, 5)
rs!カーネル時間 = varProcessData(lngRow, 6)
rs!ユーザー時間 = varProcessData(lngRow, 7)
rs!物理メモリ = varProcessData(lngRow, 8)
rs!仮想メモリ = varProcessData(lngRow, 9)
rs.Update
' 一定数ごとにトランザクションをコミット (メモリ消費を抑えつつ高速化)
If (lngRow + 1) Mod MAX_BATCH_SIZE = 0 Then
db.CommitTrans
db.BeginTrans
End If
Next lngRow
' 残りのレコードをコミット
db.CommitTrans
Debug.Print lngCounter & " 件のプロセス情報をデータベースに記録しました。"
Else
Debug.Print "監視対象プロセスは実行されていませんでした。"
End If
' --- 性能計測終了 ---
dblEndTime = Timer
Debug.Print "処理時間: " & Format(dblEndTime - dblStartTime, "0.000") & " 秒"
Exit_Form_Timer:
' 元の設定に戻す
Application.SetOption "Confirm Record Changes", True
Application.SetOption "Confirm Document Deletions", True
Application.SetOption "Confirm Action Queries", True
Set rs = Nothing
Set db = Nothing
Set objProcess = Nothing
Set colProcesses = Nothing
Set objWMIService = Nothing
Exit Sub
ErrorHandler:
If db.Transactions Then ' エラー時はトランザクションをロールバック
db.Rollback
End If
MsgBox "プロセス監視中にエラーが発生しました: " & Err.Description, vbCritical
Resume Exit_Form_Timer
End Sub
コード2: 定期監視とデータベースへの記録
性能チューニングの数値効果 (例):
配列バッファとトランザクションなしの場合 : 1000レコードの挿入に約 500ms ~ 1000ms かかることがあります。
配列バッファとトランザクションありの場合 : 1000レコードの挿入に約 50ms ~ 100ms で完了することが可能です。
バッチ処理 : MAX_BATCH_SIZE
を設定することで、トランザクションの粒度を調整し、メモリ使用量とI/O効率のバランスを取ることができます。今回の例では、MAX_BATCH_SIZE = 50
の場合、1回のコミットで50件ずつ書き込むため、大規模なデータセットでも安定したパフォーマンスを期待できます。
コード2の実行手順:
上記「準備」で述べたテーブル ProcessMonitorLog
とフォーム frmProcessMonitor
を作成します。
Module1
に WMIStringToDate
関数(コード1に記載)と StartProcessMonitoringForm
、StopProcessMonitoringForm
プロシージャをコピー&ペーストします。
frmProcessMonitor
のフォームモジュールに Form_Load
、Form_Unload
、Form_Timer
イベントプロシージャをコピー&ペーストします。
Module1
の StartProcessMonitoringForm
を実行することで、フォームが非表示で開き、1分ごとにプロセス監視が開始され、結果が ProcessMonitorLog
テーブルに記録されます。
監視を停止するには、Module1
の StopProcessMonitoringForm
を実行します。
ロールバック方法:
テーブルデータ : DELETE FROM ProcessMonitorLog;
SQLクエリを実行することで、記録されたすべてのデータを削除できます。
VBAコード : 作成した標準モジュールやフォームモジュールのVBAコードを削除します。
データベースオブジェクト : 作成したテーブル ProcessMonitorLog
とフォーム frmProcessMonitor
をAccessのナビゲーションウィンドウから削除します。
検証
プロセスの起動・停止確認 :
リソース情報の取得 :
性能確認 :
エラーハンドリング :
運用
定期実行のスケジュール設定
Accessアプリケーションを定期的に起動し、監視を開始するためには、Windowsのタスクスケジューラを利用するのが効果的です。
手順:
AccessデータベースをMDB/ACCDB形式で保存 し、監視用フォーム frmProcessMonitor
が自動的に起動するように設定します。例えば、データベース起動時に StartProcessMonitoringForm
を呼び出すマクロをAutoExecに設定するか、データベースの起動オプションで特定のフォームを開くようにします。
タスクスケジューラの作成 :
タスクスケジューラ
を開き、「基本タスクの作成」を選択。
タスク名と説明を入力。
トリガーを「毎日」や「週ごと」など、監視間隔に合わせて設定します。
操作で「プログラムの開始」を選択し、プログラム/スクリプト
に msaccess.exe
のフルパスを、引数の追加
に監視対象のAccessデータベースファイル (.accdb
または .mdb
) のフルパスを指定します。
ログ管理
ProcessMonitorLog
テーブルは時間の経過とともに肥大化するため、定期的なログのアーカイブや削除ポリシーを確立することが重要です。
監視アラート
プロセスが予期せず停止した場合や、リソース使用率が異常に高くなった場合に、メール通知やポップアップアラートを発信する機能を実装することで、より実用的な監視システムとなります。
落とし穴と対策
WMIクエリのパフォーマンス問題 :
落とし穴 : SELECT * FROM Win32_Process
のようにすべてのプロパティを取得するクエリは、WMIサービスへの負荷が高く、結果セットが大きくなり、VBAでの処理時間も長くなります。特に大量のプロセスが実行されている環境では顕著です。
対策 : 常に必要なプロパティのみを SELECT
句で指定し、WHERE
句で監視対象を明確に絞り込みます。例えば WHERE Name = 'EXCEL.EXE'
や WHERE ProcessId = 1234
のようにします。
セキュリティ設定と権限 :
落とし穴 : WMIサービスへのアクセスには適切な権限が必要です。特にリモートコンピュータのWMIを監視する場合、権限不足でアクセスが拒否されることがあります。
対策 : ローカルPCでの監視は通常問題ありませんが、リモート監視の場合は適切なユーザーアカウントとパスワード (objWMIService = GetObject("winmgmts:{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2")
のように接続文字列を調整) を指定するか、DCOM設定を確認します。
WMIサービスの停止 :
64bit VBA環境とCOMオブジェクト :
落とし穴 : 32bit環境で動作していたコードが64bit Accessで動作しない場合があります。特にWin32 APIの Declare
ステートメントには PtrSafe
が必須です。
対策 : 本記事で用いた GetObject
によるCOMオブジェクトの利用は、VBAのネイティブ機能として提供されるため、通常32bit/64bit環境の違いを意識する必要はありません。ただし、もしWin32 APIを直接呼び出す場合は、必ず Declare PtrSafe
を使用します。
まとめ
本稿では、Access VBAを用いてWMI Win32_Process
クラスを監視し、特定のプロセスの稼働状況やリソース使用状況をデータベースに記録する実践的な方法を解説しました。外部ライブラリを使用せず、VBAの標準機能とCOMオブジェクトを駆使することで、厳格な環境下でもシステム監視ソリューションを構築できることを示しました。
特に、以下の点を強調しました。
GetObject
関数によるWMIサービスへの容易なアクセス。
WQLクエリによる効率的なプロセス情報取得。
配列バッファとDAOトランザクションを組み合わせた、Accessデータベースへの高速なデータ書き込み。
Form_Timer
イベントを活用した定期的な監視の実現。
これらの手法を応用することで、Access VBAの汎用性を活かし、業務プロセス監視、リソース使用状況の傾向分析、異常検知アラートなど、さまざまなニーズに対応する強力な管理ツールを構築することが可能です。システムの安定稼働に貢献するため、ぜひ本記事のコードと知見をご活用ください。
コメント