Delphi Programming / Object Pascal
サイトのトップページへ リンクのページへ ロゴマーク
[掲載 2008年05月02日] [更新 2013年09月04日] Delphi サンプルプログラム集
901_WMI ソフトウェア関係情報の取得
動作確認等 Windows 7 U64(SP1) + Delphi XE Pro
901_WMI_SoftWare.zip [1,800 KB] 2013年09月04日版 (EXE 同梱)



  • 2010年07月29日
  • プロパティ取得のループ処理を while 文を使用するコードに統一
  • 2013年09月03日
  • [898_WMI イントロダクション] の更新に伴い,コードを整備し,記事も修正




WMI を使用したソフトウェア関係情報の取得


WMI でハードウェア,ソフトウェアの情報,プロパティということになりますが,これらを取得する手順は次のようになります.
  • (1) WMI オブジェクトのインスタンスを生成
  • (2) そのインスタンスを WMI サービスに接続
  • (3) そのサービスに対して,プロパティのセットを取得するクエリーを実行
  • (4) クエリの結果からプロパティを取得していく
1 番目と 2 番目のコードはいつもほとんど同じです.
3 番目のクエリ文で,使用する Win32_LogicalDisk と言ったクラス名と呼ばれる部分を,目的に応じて変えることになります.どのようなクラスに,どのような機能があるかは,[WMI イントロダクション] のページの参考リンクの記事を参考にしてください.

本ページでは,Win32 Classes クラスの Installed Applications Classes の一部のクラスを使用して,ソフトウェアの情報を取得します.





01_インストールされているソフトウェアのリスト


クエリに Win32_SoftwareFeature クラスを指定して,インストールされている全ソフトウェアをリストします.
[900_WMI を使用したハードウェア関係情報の取得] では,プロパティの数だけ行を作成しましたが,本ページでは,各カラムをプロパティとしています.したがって,TListView の行数がインストールされてるいるソフトウェアの数ということになります.

このサンプルのクエリの実行は,非常に時間がかかります.


  • 図1
  • 設計時画面.本ページのサンプルでは全て同じ
  • カラムは実行時に作成する
  • 図2
  • 実行例.8484 はインストールされているソフトの数
  • このようなことほとんど必要性はないと思うが,クエリの実行に非常に時間がかかる

リスト1
インストールされている全ソフトウェアのリストを取得
unit Unit1;

interface

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

type
  TForm1 = class(TForm)
    Panel1: TPanel;
    Panel3: TPanel;
    Panel4: TPanel;
    ListView1: TListView;
    Button1: TButton;
    ProgressBar1: TProgressBar;
    Label1: TLabel;
    procedure Button1Click(Sender: TObject);
  private
    { Private 宣言 }
    function VarToString(const OleVar: OleVariant): String;
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

//=============================================================================
//  インストールされているソフトウェアのリストを取得して表示
//  非常に時間がかかるので要注意
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  objWMILocator  : ISWbemLocator;
  objWMIService  : ISWbemServices;
  objPropSet     : ISWbemObjectSet;
  objPropSetEnum : IEnumVariant;
  objTemp        : OleVariant;
  iValue         : LongWord;
  wmiPropSet     : ISWbemPropertySet;
  wmiPropSetEnum : IEnumVariant;
  wmiProperty    : ISWbemProperty;
  AColumn        : TListColumn;
  AItem          : TListItem;
  CreateRowFlag  : Boolean;
begin
  Screen.Cursor := crHourGlass;

  //WMIのオブジェクトを生成
  objWMILocator := CoSWbemLocator.Create;

  //WMIサービスに接続.引数は必要に応じて設定
  objWMIService := objWMILocator.ConnectServer(
                     '',
                     'root\cimv2',
                     '',
                     '',
                     '',
                     '',
                     0,
                     nil);

  //クエリーを実行して,プロパティのセットのオブジェクトを取得
  objPropSet := objWMIService.ExecQuery(
                     'Select * From Win32_SoftwareFeature',
                     'WQL',
                     wbemFlagReturnImmediately,
                     nil);


  //プロパティのセットのコレクションを取得
  objPropSetEnum := objPropSet._NewEnum as IEnumVariant;

  //-------------------------------------------------------------------------
  //  以下がプロパティの取得コード
  //-------------------------------------------------------------------------

  ListView1.Items.Clear;
  ListView1.Items.BeginUpdate;
  ListView1.Columns.Clear;
  Label1.Caption := '';

  ProgressBar1.Position := 0;
  ProgressBar1.Min      := 0;
  ProgressBar1.Max      := objPropSet.Count;

  //取得したソフトの数を表示
  Label1.Caption := IntToStr(objPropSet.Count);

  //プロパティセットの数だけ繰返す
  //このサンプルでは論理ディクスの数だけ
  while objPropSetEnum.Next(1, objTemp, iValue) = 0 do begin

    //プロパティのセットを取得
    wmiPropSet     := (IUnknown(objTemp) as ISWBemObject).Properties_;
    wmiPropSetEnum := (wmiPropSet._NewEnum) as IEnumVariant;

    CreateRowFlag := True;
    //プロパティの数だけ繰返す
    //プロパティ名はName,プロパティの値はGet_Valueで取得
    while wmiPropSetEnum.Next(1, objTemp, iValue) = 0 do begin
      if VarIsNull(objTemp) then Continue;

      //プロパティを取得
      wmiProperty := IUnknown(objTemp) as ISWBemProperty;

      //プロパティセットの最初のプロパティ取得の際に行を追加
      //後のプロパティはSubItems(列)方向に追加していく
      if CreateRowFlag then begin
        AItem         := ListView1.Items.Add;
        AItem.Caption := VarToString(wmiProperty.Get_Value);
        CreateRowFlag := False;
      end else begin
        AItem.SubItems.Add(VarToString(wmiProperty.Get_Value));
      end;

      //次のプロパティ表示用にカラム(列)を追加
      if ListView1.Items.Count = 1 then begin
        AColumn         := ListView1.Columns.Add;
        AColumn.Caption := VarToString(wmiProperty.Name);
        AColumn.Width   := 130;
      end;
      Application.ProcessMessages;
      Sleep(1);
    end;
    ProgressBar1.Position := ProgressBar1.Position + 1;
  end;

  ListView1.Items.EndUpdate;
  Screen.Cursor := crDefault;
end;

//=============================================================================
//  OleVariantの値をString型に変換(再帰処理)
//  複数の値を持つプロパティがある.そこでこれを[]で括った結果を返すことにする
//  そのためVarArrayも扱えるようにしたVarToStr関数
//=============================================================================
function TForm1.VarToString(const OleVar: OleVariant): String;
var
  LowBound  : Integer;
  HighBound : Integer;
  i         : Integer;
begin
  Result := '';

  //配列の場合
  if VarIsArray(OleVar) then begin
    LowBound  := VarArrayLowBound(OleVar, 1);
    HighBound := VarArrayHighBound(OleVar, 1);

    for i := LowBound to HighBound do begin
      if i = HighBound then begin
        Result := Result + VarToString(OleVar[i]);
      end else begin
        Result := Result + VarToString(OleVar[i]) + ',';
      end;
    end;

    Result:='[' + Result + ']';

  //単一の値を持つプロパティの場合
  end else begin
    Result := VarToStr(OleVar);
  end;
  if Result = '' then  Result := 'NULL';
end;

end.


下図は,Caption プロパティに Microsoft という文字列が含まれるソフトだけを取得した結果です.その下のコードのように,クエリ文の where 句で指定しています.

  • 図3
  • Caption プロパティに Microsoft の文字が含まれるいるソフトだけのリスト

  objPropSet := objWMIService.ExecQuery(
                     'Select * From Win32_SoftwareFeature '
                     + 'where caption like "%Microsoft%"',
                     'WQL',
                     wbemFlagReturnImmediately,
                     nil);




02_MSI でインストールした製品のリスト


クエリに Win32_Product クラスを使用して,Windows のインストーラである MSI でインストールしたアプリケーションの一覧を取得して表示します.
上のサンプルと違うのはクエリで指定するクラス名の文字列だけです.


  • 図4
  • 実行例.140 はインストールされている MSI アプリケーションの数

リスト2
MSI でインストールしたアプリのリストを取得
上のサンプルと違うのは,クエリ文のクラス名の部分だけ
unit Unit1;

interface

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

type
  TForm1 = class(TForm)
    Panel1: TPanel;
    Panel3: TPanel;
    Panel4: TPanel;
    ListView1: TListView;
    Button1: TButton;
    ProgressBar1: TProgressBar;
    Label1: TLabel;
    procedure Button1Click(Sender: TObject);
  private
    { Private 宣言 }
    function VarToString(const OleVar: OleVariant): String;
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

//=============================================================================
//  MSIでインストールした製品のリストを取得して表示
//  クエリーの実行に少し時間がかかるので注意
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  objWMILocator  : ISWbemLocator;
  objWMIService  : ISWbemServices;
  objPropSet     : ISWbemObjectSet;
  objPropSetEnum : IEnumVariant;
  objTemp        : OleVariant;
  iValue         : LongWord;
  wmiPropSet     : ISWbemPropertySet;
  wmiPropSetEnum : IEnumVariant;
  wmiProperty    : ISWbemProperty;
  AColumn        : TListColumn;
  AItem          : TListItem;
  CreateRowFlag  : Boolean;
begin
  Screen.Cursor := crHourGlass;

  //WMIのオブジェクトを生成
  objWMILocator := CoSWbemLocator.Create;

  //WMIサービスに接続.引数は必要に応じて設定
  objWMIService := objWMILocator.ConnectServer(
                     '',
                     'root\cimv2',
                     '',
                     '',
                     '',
                     '',
                     0,
                     nil);

  //クエリーを実行して,プロパティのセットのオブジェクトを取得
  objPropSet := objWMIService.ExecQuery(
                     'Select * From Win32_Product',
                     'WQL',
                     wbemFlagReturnImmediately,
                     nil);


  //プロパティのセットのコレクションを取得
  objPropSetEnum := objPropSet._NewEnum as IEnumVariant;

  //-------------------------------------------------------------------------
  //  以下がプロパティの取得コード
  //-------------------------------------------------------------------------

  ListView1.Items.Clear;
  ListView1.Items.BeginUpdate;
  ListView1.Columns.Clear;
  Label1.Caption := '';

  ProgressBar1.Position := 0;
  ProgressBar1.Min      := 0;
  ProgressBar1.Max      := objPropSet.Count;

  //取得したソフトの数を表示
  Label1.Caption := IntToStr(objPropSet.Count);

  //プロパティセットの数だけ繰返す
  //このサンプルでは論理ディクスの数だけ
  while objPropSetEnum.Next(1, objTemp, iValue) = 0 do begin

    //プロパティのセットを取得
    wmiPropSet     := (IUnknown(objTemp) as ISWBemObject).Properties_;
    wmiPropSetEnum := (wmiPropSet._NewEnum) as IEnumVariant;

    CreateRowFlag := True;
    //プロパティの数だけ繰返す
    //プロパティ名はName,プロパティの値はGet_Valueで取得
    while wmiPropSetEnum.Next(1, objTemp, iValue) = 0 do begin
      if VarIsNull(objTemp) then Continue;

      //プロパティを取得
      wmiProperty := IUnknown(objTemp) as ISWBemProperty;

      //プロパティセットの最初のプロパティ取得の際に行を追加
      //後のプロパティはSubItems(列)方向に追加していく
      if CreateRowFlag then begin
        AItem         := ListView1.Items.Add;
        AItem.Caption := VarToString(wmiProperty.Get_Value);
        CreateRowFlag := False;
      end else begin
        AItem.SubItems.Add(VarToString(wmiProperty.Get_Value));
      end;

      //次のプロパティ表示用にカラム(列)を追加
      if ListView1.Items.Count = 1 then begin
        AColumn         := ListView1.Columns.Add;
        AColumn.Caption := VarToString(wmiProperty.Name);
        AColumn.Width   := 130;
      end;
      Application.ProcessMessages;
      Sleep(1);
    end;
    ProgressBar1.Position := ProgressBar1.Position + 1;
  end;

  ListView1.Items.EndUpdate;
  Screen.Cursor := crDefault;
end;

//=============================================================================
//  OleVariantの値をString型に変換(再帰処理)
//  複数の値を持つプロパティがある.そこでこれを[]で括った結果を返すことにする
//  そのためVarArrayも扱えるようにしたVarToStr関数
//=============================================================================
function TForm1.VarToString(const OleVar: OleVariant): String;
var
  LowBound  : Integer;
  HighBound : Integer;
  i         : Integer;
begin
  Result := '';

  //配列の場合
  if VarIsArray(OleVar) then begin
    LowBound  := VarArrayLowBound(OleVar, 1);
    HighBound := VarArrayHighBound(OleVar, 1);

    for i := LowBound to HighBound do begin
      if i = HighBound then begin
        Result := Result + VarToString(OleVar[i]);
      end else begin
        Result := Result + VarToString(OleVar[i]) + ',';
      end;
    end;

    Result:='[' + Result + ']';

  //単一の値を持つプロパティの場合
  end else begin
    Result := VarToStr(OleVar);
  end;
  if Result = '' then  Result := 'NULL';
end;

end.




03_プロセスのリスト


クエリに Win32_Process クラスを使用して,プロセスのリストを取得して表示します.
上のサンプルと違うのはクエリーで指定するクラス名の文字列だけです.


  • 図5
  • 実行例.66 は現在起動中のプロセスの数

リスト3
プロセスのリストを取得
上のサンプルと違うのは,クエリ文のクラス名の部分だけ
unit Unit1;

interface

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

type
  TForm1 = class(TForm)
    Panel1: TPanel;
    Panel3: TPanel;
    Panel4: TPanel;
    ListView1: TListView;
    Button1: TButton;
    ProgressBar1: TProgressBar;
    Label1: TLabel;
    procedure Button1Click(Sender: TObject);
  private
    { Private 宣言 }
    function VarToString(const OleVar: OleVariant): String;
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

//=============================================================================
//  プロセスのリストを取得して表示
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  objWMILocator  : ISWbemLocator;
  objWMIService  : ISWbemServices;
  objPropSet     : ISWbemObjectSet;
  objPropSetEnum : IEnumVariant;
  objTemp        : OleVariant;
  iValue         : LongWord;
  wmiPropSet     : ISWbemPropertySet;
  wmiPropSetEnum : IEnumVariant;
  wmiProperty    : ISWbemProperty;
  AColumn        : TListColumn;
  AItem          : TListItem;
  CreateRowFlag  : Boolean;
begin
  Screen.Cursor := crHourGlass;

  //WMIのオブジェクトを生成
  objWMILocator := CoSWbemLocator.Create;

  //WMIサービスに接続.引数は必要に応じて設定
  objWMIService := objWMILocator.ConnectServer(
                     '',
                     'root\cimv2',
                     '',
                     '',
                     '',
                     '',
                     0,
                     nil);

  //クエリーを実行して,プロパティのセットのオブジェクトを取得
  objPropSet := objWMIService.ExecQuery(
                     'Select * From Win32_Process',
                     'WQL',
                     wbemFlagReturnImmediately,
                     nil);


  //プロパティのセットのコレクションを取得
  objPropSetEnum := objPropSet._NewEnum as IEnumVariant;

  //-------------------------------------------------------------------------
  //  以下がプロパティの取得コード
  //-------------------------------------------------------------------------

  ListView1.Items.Clear;
  ListView1.Items.BeginUpdate;
  ListView1.Columns.Clear;
  Label1.Caption := '';

  ProgressBar1.Position := 0;
  ProgressBar1.Min      := 0;
  ProgressBar1.Max      := objPropSet.Count;

  //取得したソフトの数を表示
  Label1.Caption := IntToStr(objPropSet.Count);

  //プロパティセットの数だけ繰返す
  //このサンプルでは論理ディクスの数だけ
  while objPropSetEnum.Next(1, objTemp, iValue) = 0 do begin

    //プロパティのセットを取得
    wmiPropSet     := (IUnknown(objTemp) as ISWBemObject).Properties_;
    wmiPropSetEnum := (wmiPropSet._NewEnum) as IEnumVariant;

    CreateRowFlag := True;
    //プロパティの数だけ繰返す
    //プロパティ名はName,プロパティの値はGet_Valueで取得
    while wmiPropSetEnum.Next(1, objTemp, iValue) = 0 do begin
      if VarIsNull(objTemp) then Continue;

      //プロパティを取得
      wmiProperty := IUnknown(objTemp) as ISWBemProperty;

      //プロパティセットの最初のプロパティ取得の際に行を追加
      //後のプロパティはSubItems(列)方向に追加していく
      if CreateRowFlag then begin
        AItem         := ListView1.Items.Add;
        AItem.Caption := VarToString(wmiProperty.Get_Value);
        CreateRowFlag := False;
      end else begin
        AItem.SubItems.Add(VarToString(wmiProperty.Get_Value));
      end;

      //次のプロパティ表示用にカラム(列)を追加
      if ListView1.Items.Count = 1 then begin
        AColumn         := ListView1.Columns.Add;
        AColumn.Caption := VarToString(wmiProperty.Name);
        AColumn.Width   := 130;
      end;
      Application.ProcessMessages;
      Sleep(1);
    end;
    ProgressBar1.Position := ProgressBar1.Position + 1;
  end;

  ListView1.Items.EndUpdate;
  Screen.Cursor := crDefault;
end;

//=============================================================================
//  OleVariantの値をString型に変換(再帰処理)
//  複数の値を持つプロパティがある.そこでこれを[]で括った結果を返すことにする
//  そのためVarArrayも扱えるようにしたVarToStr関数
//=============================================================================
function TForm1.VarToString(const OleVar: OleVariant): String;
var
  LowBound  : Integer;
  HighBound : Integer;
  i         : Integer;
begin
  Result := '';

  //配列の場合
  if VarIsArray(OleVar) then begin
    LowBound  := VarArrayLowBound(OleVar, 1);
    HighBound := VarArrayHighBound(OleVar, 1);

    for i := LowBound to HighBound do begin
      if i = HighBound then begin
        Result := Result + VarToString(OleVar[i]);
      end else begin
        Result := Result + VarToString(OleVar[i]) + ',';
      end;
    end;

    Result:='[' + Result + ']';

  //単一の値を持つプロパティの場合
  end else begin
    Result := VarToStr(OleVar);
  end;
  if Result = '' then  Result := 'NULL';
end;

end.




04_OS ( Windows のバージョン等 ) の情報


Windows のバージョン情報等は,Win32_OperatingSystem を使用すると取得できます.
以下のリンクの記事に実際の操作結果の図があります.