Delphi Programming / Object Pascal
サイトのトップページへ リンクのページへ ロゴマーク
[掲載 2008年05月03日] [更新 2013年10月16日] Delphi サンプルプログラム集
904_WMI  同期・半同期イベントシンク
動作確認等 Windows 7 U64(SP1) + Delphi XE Pro
904_WMI_EventSink.zip [2,753 KB] 2013年10月16日版 (EXE 同梱)



  • 2013年10月16日
  • プロセスの起動と終了のページに掲載していた半同期イベント関係サンプルをこちらに移動.サンプルを細かく分割した




WMI の同期・半同期イベントシンク


[903_ WMI 非同期イベントシンク] に続いて,同期,半同期のイベントのサンプルです.
実際には全て,半同期のサンプルとなっています.非同期のイベントでも,フラグ等を使用すれば,本ページのような半同期の処理が可能です.
クエリの構文や,組み込みイベントクラス,外部イベントクラスの使用方法も [903_ WMI 非同期イベントシンク] と全く同じです.同期,半同期のイベント処理では.イベント用のメソッドは作成しません.また,実行メソッド名が,非同期イベントのとは違います.
  • (1) WMI (Wbem) オブジェクトのインスタンスを生成する
  • (2) WMI (Wbem) サービスに接続する (ローカルかネットワークかによって引数の値が異なる)
  • (3) クエリ文を作成して,ExecNotificationQuery 関数で実行する
  • (4) ISWbemEventSource の NextEvent(iTimeoutMs: Integer) メソッドをループ内で実行
という手順でコーディングします.クエリ文の条件が不適切だと,ループから脱出できなくなる可能性があります.そこで,本ページのサンプルでは [監視中止] ボタンでイベント処理を中断できるようにしています.

NextEvent メソッドの引数の単位はミリセコンドです.この時間内にクエリの結果が取得できないとタイムアウトの例外が発生します.この引数の値を -1 にすると,クエリの結果が取得できるまでこのメソッドの実行が終了しなくなり,完全な同期実行となります.

筆者のテスト環境では,外部イベントクラスを使用したサンプルでは,この NextEvent メソッドの引数の値は無視されているようです.また,検出まで少しタイムラグがあるようです.非同期イベントでもタイムラグがありましたから,おそらく,外部イベントクラスの仕様なのかも知れません.





01_指定アプリが起動するまで待つ - 組み込みイベントクラス使用


組み込みイベントクラスの __InstanceCreationEvent を使用して,指定したアプリケーションが起動するまで待つサンプルです.ターゲットのクラスとして,Win32_Process を使用しています.

クエリの検出条件に,メモ帳の起動パスをフルパスで設定しています.イベント内で,このフルパスと同じパスを検出したらイベント処理を終了します.
クエリでこのフルパスを検出条件にしています.したがって,実際にはこの比較操作は必要ありません.イベント内で任意の条件で検出する際の参考として追加しているだけです.


図1
設計時画面
  • 他のサンプルもほとんど同じ
図2
実行例
  • メモ帳を起動したところ

リスト1
組み込みイベントクラスを使用して,指定アプリが起動するまで待つ
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ExtCtrls, WbemScripting_TLB;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    Memo1: TMemo;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
    procedure FormCreate(Sender: TObject);
  private
    { Private 宣言 }
    FobjWMILocator  : ISWbemLocator;
    FobjWMIService  : ISWbemServices;
    FobjEventSource : OleVariant;
    FobjEventResult : OleVariant;
    FAppFullPath    : String;
    FStopFlag       : Boolean;
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

//=============================================================================
//  フォーム生成時の処理
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
  Button2Click(nil);
end;

//=============================================================================
//  [監視開始]ボタン
//
//  指定アプリの起動を待つ
//  組み込みイベントクラス__InstanceCreationEventを使用
//  ターゲットクラスをWin32_Processとした場合
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  strSQL      : String;
  sqlAppPath  : String;
  objAppPath  : String;
  objTemp     : OleVariant;
begin
  FStopFlag       := False;
  Button1.Enabled := False;
  Button2.Enabled := True;
  Memo1.Lines.Clear;

  FobjWMILocator := CoSWbemLocator.Create;
  FobjWMIService := FobjWMILocator.ConnectServer(
                            'localhost',
                            'root\cimv2',
                            '',
                            '',
                            '',
                            '',
                            wbemConnectFlagUseMaxWait,
                            nil);

  //起動を監視するアプリケーションのフルパス(ここではメモ帳)
  FAppFullPath := 'C:\Windows\System32\notepad.exe';

  //クエリに渡すパス名のパス区切り文字は \ ではなく \\ とする
  //文字列として渡すのでダブルクォーテーションで囲む
  sqlAppPath := StringReplace(FAppFullPath, '\', '\\', [rfReplaceAll]);
  sqlAppPath := AnsiQuotedStr(sqlAppPath, '"');


  //クエリ文
  strSQL := 'SELECT * '
          + 'FROM __InstanceCreationEvent '
          + 'WITHIN 1 '
          + 'WHERE TargetInstance '
          + 'ISA "Win32_Process" and TargetInstance.ExecutablePath = ' + sqlAppPath;

  //クエリを実行してイベント処理を開始
  FobjEventSource := FobjWMIService.ExecNotificationQuery(
                            strSQL,
                            'WQL',
                            wbemFlagForwardOnly or wbemFlagReturnImmediately,
                            nil);

  //---------------------------------------------------------------------------
  //  以下,イベントの処理コード
  //  このループは,指定アプリが起動するか,FStopFlagがTrueになれば終了
  //---------------------------------------------------------------------------

  while True do begin
    if FStopFlag then break;

    try
      //同期イベントを実行
      //NextEventの引数で指定する時間(ミリセコンド)が経過してもクエリの結果が
      //取得できない場合はタイムアウト例外となるのでtry~exceptで処理
      //NextEventの引数の値を-1にすると,起動するまで永久に待ち続ける
      FobjEventResult := FobjEventSource.NextEvent(100);

      //検出できたらExecutablePathプロパティの値を取得
      objAppPath := VarToStr(FobjEventResult.TargetInstance.ExecutablePath);

      //クエリで指定したアプリのフルパスと同じだったら起動したことになる
      if UpperCase(FAppFullPath) = UpperCase(objAppPath) then begin
        objTemp := FobjEventResult.TargetInstance;
        Memo1.Lines.Add('ExecutablePath  : ' + VarToStr(objTemp.ExecutablePath));
        Memo1.Lines.Add('CommandLine     : ' + VarToStr(objTemp.CommandLine));
        Memo1.Lines.Add('Caption         : ' + VarToStr(objTemp.Caption));
        Memo1.Lines.Add('ProcessId       : ' + VarToStr(objTemp.ProcessId));
        Memo1.Lines.Add('');
        Break;
      end;
    except
    end;
    Application.ProcessMessages;
    //どんな内容が入ってくるか不明なので一応クリアておく
    //おそらく,NextEventで初期化しているとは思うが
    FobjEventResult := Unassigned;
  end;
  //---------------------------------------------------------------------------
  //  イベントの処理ループ終了
  //---------------------------------------------------------------------------

  Button2Click(nil);
end;

//=============================================================================
//  [監視中止]ボタン
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
begin
  FStopFlag := True;

  FobjWMILocator  := nil;
  FobjWMIService  := nil;
  FobjEventSource := Unassigned;
  FobjEventResult := Unassigned;

  Button2.Enabled := False;
  Button1.Enabled := True;
end;

//=============================================================================
//  イベント実行中に[X]ボタンを操作してしまった場合の簡易対策策
//  監視を終了しないと閉じることはできない
//=============================================================================
procedure TForm1.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
begin
  CanClose := FStopFlag;
end;

end.




02_指定アプリが起動するまで待つ - 外部イベントクラス使用


外部イベントクラスの Win32_ProcessStartTrace 使用して,指定したアプリケーションが起動するまで待つサンプルです.

クエリの検出条件に,メモ帳の EXE 名である notepad.exe を設定しています.
Win32_ProcessStartTrace クラスのプロパティには,起動プロセスのフルパスに相当するものはありません.そこで起動ファイル名のみを指定しています.
当サイトのサンプルプログラムの多くは,Project1.exe です.この名前をクエリ内で指定すると,どのサンプルプログラムの起動も検出することになります.

イベント内で,これと同じ EXE 名を検出したらイベント処理を終了します.
クエリでこの EXE 名を検出条件にしています.したがって,実際にはこの比較操作は必要ありません.イベント内で任意の条件で検出する際の参考として追加しているだけです.

このサンプルの実行には管理者権限が必要です.


図3
実行例
  • メモ帳を起動したところ
  • Win32_ProcessStartTrace イベントクラスでは,アプリのフルパスは取得できない

リスト2
外部イベントクラスを使用して,指定アプリが起動するまで待つ
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ExtCtrls, WbemScripting_TLB;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    Memo1: TMemo;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
    procedure FormCreate(Sender: TObject);
  private
    { Private 宣言 }
    FobjWMILocator  : ISWbemLocator;
    FobjWMIService  : ISWbemServices;
    FobjEventSource : OleVariant;
    FobjEventResult : OleVariant;
    FAppName        : String;
    FStopFlag       : Boolean;
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

//=============================================================================
//  フォーム生成時の処理
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
  Button2Click(nil);
end;

//=============================================================================
//  [監視開始]ボタン
//
//  指定アプリの起動を待つ
//  外部イベントクラスWin32_ProcessStartTraceを使用
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  strSQL     : String;
  sqlAppName : String;
  objAppName : String;
begin
  FStopFlag       := False;
  Button1.Enabled := False;
  Button2.Enabled := True;
  Memo1.Lines.Clear;

  FobjWMILocator := CoSWbemLocator.Create;
  FobjWMIService := FobjWMILocator.ConnectServer(
                            'localhost',
                            'root\cimv2',
                            '',
                            '',
                            '',
                            '',
                            wbemConnectFlagUseMaxWait,
                            nil);

  //起動を監視するアプリケーションのEXE名(ここではメモ帳)
  FAppName := 'notepad.exe';
  //クエリに文字列として渡すのでダブルクォーテーションで囲む
  sqlAppName := AnsiQuotedStr(FAppName, '"');


  //クエリ文
  strSQL := 'SELECT * '
          + 'FROM Win32_ProcessStartTrace '
          + 'WHERE ProcessName = ' + sqlAppName;

  //クエリを実行してイベント処理を開始
  FobjEventSource := FobjWMIService.ExecNotificationQuery(
                            strSQL,
                            'WQL',
                            wbemFlagForwardOnly or wbemFlagReturnImmediately,
                            nil);

  //---------------------------------------------------------------------------
  //  以下,イベントの処理コード
  //  このループは,指定アプリが起動するか,FStopFlagがTrueになれば終了
  //---------------------------------------------------------------------------

  while True do begin
    if FStopFlag then break;

    try
      FobjEventResult := FobjEventSource.NextEvent(100);

      objAppName := VarToStr(FobjEventResult.ProcessName);
      if UpperCase(FAppName) = UpperCase(objAppName) then begin
        Memo1.Lines.Add('ProcessName  : ' + VarToStr(FobjEventResult.ProcessName));
        Memo1.Lines.Add('ProcessId    : ' + VarToStr(FobjEventResult.ProcessId));
        Break;
      end;
    except
    end;
    Application.ProcessMessages;
    FobjEventResult := Unassigned;
  end;
  //---------------------------------------------------------------------------
  //  イベントの処理ループ終了
  //---------------------------------------------------------------------------

  Button2Click(nil);
end;

//=============================================================================
//  [監視中止]ボタン
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
begin
  FStopFlag := True;

  FobjWMILocator  := nil;
  FobjWMIService  := nil;
  FobjEventSource := Unassigned;
  FobjEventResult := Unassigned;

  Button2.Enabled := False;
  Button1.Enabled := True;
end;

//=============================================================================
//  イベント実行中に[X]ボタンを操作してしまった場合の簡易対策策
//  監視を終了しないと閉じることはできない
//=============================================================================
procedure TForm1.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
begin
  CanClose := FStopFlag;
end;

end.




03_指定アプリが終了するまで待つ - 組み込みイベントクラス使用


組み込みイベントクラスの __InstanceDeletionEvent を使用して,指定したアプリケーションが終了するまで待つサンプルです.ターゲットのクラスとして,Win32_Process を使用しています.

クエリの検出条件に,メモ帳の起動パスをフルパスで設定しています.イベント内で,このフルパスと同じパスを検出したらイベント処理を終了します.
クエリでこのフルパスを検出条件にしています.したがって,実際にはこの比較操作は必要ありません.イベント内で任意の条件で検出する際の参考として追加しているだけです.


図4
実行例
  • メモ帳を終了したところ

リスト3
組み込みイベントクラスを使用して,指定アプリが終了するまで待つ
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ExtCtrls, WbemScripting_TLB;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    Memo1: TMemo;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
    procedure FormCreate(Sender: TObject);
  private
    { Private 宣言 }
    FobjWMILocator  : ISWbemLocator;
    FobjWMIService  : ISWbemServices;
    FobjEventSource : OleVariant;
    FobjEventResult : OleVariant;
    FAppFullPath    : String;
    FStopFlag       : Boolean;
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

//=============================================================================
//  フォーム生成時の処理
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
  Button2Click(nil);
end;

//=============================================================================
//  [監視開始]ボタン
//
//  指定アプリの起動を待つ
//  組み込みイベントクラス__InstanceDeletionEventを使用
//  ターゲットクラスをWin32_Processとした場合
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  strSQL      : String;
  sqlAppPath  : String;
  objAppPath  : String;
  objTemp     : OleVariant;
begin
  FStopFlag       := False;
  Button1.Enabled := False;
  Button2.Enabled := True;
  Memo1.Lines.Clear;

  FobjWMILocator := CoSWbemLocator.Create;
  FobjWMIService := FobjWMILocator.ConnectServer(
                            'localhost',
                            'root\cimv2',
                            '',
                            '',
                            '',
                            '',
                            wbemConnectFlagUseMaxWait,
                            nil);

  //起動を監視するアプリケーションのフルパス(ここではメモ帳)
  FAppFullPath := 'C:\Windows\System32\notepad.exe';

  //クエリに渡すパス名のパス区切り文字は \ ではなく \\ とする
  //文字列として渡すのでダブルクォーテーションで囲む
  sqlAppPath := StringReplace(FAppFullPath, '\', '\\', [rfReplaceAll]);
  sqlAppPath := AnsiQuotedStr(sqlAppPath, '"');


  //クエリ文
  strSQL := 'SELECT * '
          + 'FROM __InstanceDeletionEvent '
          + 'WITHIN 1 '
          + 'WHERE TargetInstance '
          + 'ISA "Win32_Process" and TargetInstance.ExecutablePath = ' + sqlAppPath;

  //クエリを実行してイベント処理を開始
  FobjEventSource := FobjWMIService.ExecNotificationQuery(
                            strSQL,
                            'WQL',
                            wbemFlagForwardOnly or wbemFlagReturnImmediately,
                            nil);

  //---------------------------------------------------------------------------
  //  以下,イベントの処理コード
  //  このループは,指定アプリが終了するか,FStopFlagがTrueになれば終了
  //---------------------------------------------------------------------------

  while True do begin
    if FStopFlag then break;

    try
      FobjEventResult := FobjEventSource.NextEvent(100);

      objAppPath := VarToStr(FobjEventResult.TargetInstance.ExecutablePath);

      if UpperCase(FAppFullPath) = UpperCase(objAppPath) then begin
        objTemp := FobjEventResult.TargetInstance;
        Memo1.Lines.Add('ExecutablePath  : ' + VarToStr(objTemp.ExecutablePath));
        Memo1.Lines.Add('CommandLine     : ' + VarToStr(objTemp.CommandLine));
        Memo1.Lines.Add('Caption         : ' + VarToStr(objTemp.Caption));
        Memo1.Lines.Add('ProcessId       : ' + VarToStr(objTemp.ProcessId));
        Memo1.Lines.Add('');
        Break;
      end;
    except
    end;
    Application.ProcessMessages;
    FobjEventResult := Unassigned;
  end;
  //---------------------------------------------------------------------------
  //  イベントの処理ループ終了
  //---------------------------------------------------------------------------

  Button2Click(nil);
end;

//=============================================================================
//  [監視中止]ボタン
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
begin
  FStopFlag := True;

  FobjWMILocator  := nil;
  FobjWMIService  := nil;
  FobjEventSource := Unassigned;
  FobjEventResult := Unassigned;

  Button2.Enabled := False;
  Button1.Enabled := True;
end;

//=============================================================================
//  イベント実行中に[X]ボタンを操作してしまった場合の簡易対策策
//  監視を終了しないと閉じることはできない
//=============================================================================
procedure TForm1.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
begin
  CanClose := FStopFlag;
end;

end.




04_指定アプリが終了するまで待つ - 外部イベントクラス使用


外部イベントクラスの Win32_ProcessStopTrace 使用して,指定したアプリケーションが終了するまで待つサンプルです.

クエリの検出条件に,メモ帳の EXE 名である notepad.exe を設定しています. Win32_ProcessStopTrace クラスのプロパティには,起動プロセスのフルパスに相当するものはありません.そこで起動ファイル名のみを指定しています.
当サイトのサンプルプログラムの多くは,Project1.exe です.この名前をクエリ内で指定すると,どのサンプルプログラムの終了も検出することになります.

イベント内で,これと同じ EXE 名を検出したらイベント処理を終了します.
クエリでこの EXE 名を検出条件にしています.したがって,実際にはこの比較操作は必要ありません.イベント内で任意の条件で検出する際の参考として追加しているだけです.

このサンプルの実行には管理者権限が必要です.


図5
実行例
  • メモ帳を終了したところ
  • Win32_ProcessStopTrace イベントクラスでは,アプリのフルパスは取得できない

リスト4
外部イベントクラスを使用して,指定アプリが終了するまで待つ
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ExtCtrls, WbemScripting_TLB;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    Memo1: TMemo;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
    procedure FormCreate(Sender: TObject);
  private
    { Private 宣言 }
    FobjWMILocator  : ISWbemLocator;
    FobjWMIService  : ISWbemServices;
    FobjEventSource : OleVariant;
    FobjEventResult : OleVariant;
    FAppName        : String;
    FStopFlag       : Boolean;
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

//=============================================================================
//  フォーム生成時の処理
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
  Button2Click(nil);
end;

//=============================================================================
//  [監視開始]ボタン
//
//  指定アプリの終了を待つ
//  外部イベントクラスWin32_ProcessStopTraceを使用
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  strSQL     : String;
  sqlAppName : String;
  objAppName : String;
begin
  FStopFlag       := False;
  Button1.Enabled := False;
  Button2.Enabled := True;
  Memo1.Lines.Clear;

  FobjWMILocator := CoSWbemLocator.Create;
  FobjWMIService := FobjWMILocator.ConnectServer(
                            'localhost',
                            'root\cimv2',
                            '',
                            '',
                            '',
                            '',
                            wbemConnectFlagUseMaxWait,
                            nil);

  //起動を監視するアプリケーションのEXE名(ここではメモ帳)
  FAppName := 'notepad.exe';
  //クエリに文字列として渡すのでダブルクォーテーションで囲む
  sqlAppName := AnsiQuotedStr(FAppName, '"');


  //クエリ文
  strSQL := 'SELECT * '
          + 'FROM Win32_ProcessStopTrace '
          + 'WHERE ProcessName = ' + sqlAppName;

  //クエリを実行してイベント処理を開始
  FobjEventSource := FobjWMIService.ExecNotificationQuery(
                            strSQL,
                            'WQL',
                            wbemFlagForwardOnly or wbemFlagReturnImmediately,
                            nil);

  //---------------------------------------------------------------------------
  //  以下,イベントの処理コード
  //  このループは,指定アプリが終了するか,FStopFlagがTrueになれば終了
  //---------------------------------------------------------------------------

  while True do begin
    if FStopFlag then break;

    try
      FobjEventResult := FobjEventSource.NextEvent(100);

      objAppName := VarToStr(FobjEventResult.ProcessName);
      if UpperCase(FAppName) = UpperCase(objAppName) then begin
        Memo1.Lines.Add('ProcessName  : ' + VarToStr(FobjEventResult.ProcessName));
        Memo1.Lines.Add('ProcessId    : ' + VarToStr(FobjEventResult.ProcessId));
        Break;
      end;
    except
    end;
    Application.ProcessMessages;
    FobjEventResult := Unassigned;
  end;
  //---------------------------------------------------------------------------
  //  イベントの処理ループ終了
  //---------------------------------------------------------------------------

  Button2Click(nil);
end;

//=============================================================================
//  [監視中止]ボタン
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
begin
  FStopFlag := True;

  FobjWMILocator  := nil;
  FobjWMIService  := nil;
  FobjEventSource := Unassigned;
  FobjEventResult := Unassigned;

  Button2.Enabled := False;
  Button1.Enabled := True;
end;

//=============================================================================
//  イベント実行中に[X]ボタンを操作してしまった場合の簡易対策策
//  監視を終了しないと閉じることはできない
//=============================================================================
procedure TForm1.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
begin
  CanClose := FStopFlag;
end;

end.




05_自アプリが起動したアプリの終了を待つ


自分自身のアプリで,他のアプリを起動し,その起動したアプリが終了するまで待つサンプルです.
組み込みイベントクラスの __InstanceDeletionEvent を使用して,Win32_Process をターゲットクラスとしています.起動アプリは,これまでのサンプルと同じメモ帳です.

メモ帳の起動には,Win32_Process の Create メソッドを使用しています.このメソッドでプロセスを起動すると,起動したプロセスの ID 値を引数から取得できます.このプロセスの ID 値をクエリの検索条件に追加しています.したがって,このアプリから起動したメモ帳以外のメモ帳を終了しても,クエリの結果としては検出できません.プロセス ID の値は,プロセスに固有の値です.

クエリでこのプロセス ID 値を検出条件にしていますから,実際にはイベント処理内の比較操作は必要ありません.イベント内で任意の条件で検出する際の参考として追加しているだけです.

本サンプルでは,WMI 関係オブジェクトを全て OleVariant 型にしています.WbemScripting_TLB.pas は使用していません.


図6
実行例
  • [アプリ起動] ボタンでメモ帳を起動して,それを終了したところ

リスト5
組み込みイベントクラスを使用して,自アプリが起動したアプリの終了を待つ
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ExtCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

uses ComObj;

{$R *.dfm}

//=============================================================================
//  [アプリ起動]ボタン
//
//  自アプリが起動したアプリが終了するまで待つ
//  組み込みイベントクラス__InstanceDeletionEventを使用
//  ターゲットクラスをWin32_Processとした場合
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  objWMILocator  : OleVariant;
  objWMIService  : OleVariant;
  objEventSource : OleVariant;
  objEventResult : OleVariant;
  AppFullPath    : String;
  objProcess     : OleVariant;
  objConfig      : OleVariant;
  ProcessID      : Cardinal;
  strSQL         : String;
  objProceddID   : Cardinal;
begin
  Button1.Enabled := False;

  objWMILocator := CreateOleObject('WbemScripting.SWbemLocator');
  objWMIService := objWMILocator.ConnectServer('localhost', 'root\cimv2');


  //Win32_ProcessクラスのCreateメソッドで指定アプリを起動(ここではメモ帳)
  AppFullPath          := 'C:\Windows\System32\notepad.exe';
  objConfig            := objWMIService.Get('Win32_ProcessStartup').SpawnInstance_;
  objConfig.ShowWindow := SW_NORMAL;

  objProcess := objWMIService.Get('Win32_Process');
  if objProcess.Create(AppFullPath, null, objConfig, ProcessID) > 0 then begin
    Button1.Enabled := True;
    exit;
  end;


  //クエリ文
  //Win32_ProcessのCreateメソッドで取得したプロセスのIDを条件設定に使用
  strSQL := 'SELECT * '
          + 'FROM __InstanceDeletionEvent '
          + 'WITHIN 1 '
          + 'WHERE TargetInstance '
          + 'ISA "Win32_Process" and '
          + 'TargetInstance.ProcessId = ' + IntToStr(ProcessID);

  objEventSource := objWMIService.ExecNotificationQuery(strSQL, 'WQL');

  //---------------------------------------------------------------------------
  //  以下,イベントの処理コード
  //  このループは,指定アプリが終了すると終了
  //---------------------------------------------------------------------------

  while True do begin
    try
      objEventResult := objEventSource.NextEvent(100);
      objProceddID   := Cardinal(objEventResult.TargetInstance.ProcessId);

      if ProcessID = objProceddID then Break;
    except
    end;
    Application.ProcessMessages;
    objEventResult := Unassigned;
  end;
  //---------------------------------------------------------------------------
  //  イベントの処理ループ終了
  //---------------------------------------------------------------------------

  MessageBox(Handle, '起動したアプリが終了しました', '情報', MB_ICONINFORMATION);
  Button1.Enabled := True;
end;

end.