Delphi Programming / Object Pascal
サイトのトップページへ リンクのページへ ロゴマーク
[掲載 2017年11月19日] [更新 2017年11月21日] Delphi サンプルプログラム集
354_MSI ( Microsoft Installer ) でインストールしたアプリのリスト
動作確認等 Windows 7 U64(SP1) + Delphi XE(UP1) Pro
354_InstalledSoftByMSI.zip [1,106 KB] 2017年11月21日版 (EXE 同梱)







01_MSI ( Microsoft Installer ) でインストールしたアプリのリスト


MSI (Microsoft Installer) でインストールしたアプリを列挙するサンプルです.
Windows Installer の API (MSI API または Microsoft Installer API) を使用していますが,Delphi XE にはこれらの関数類が実装されていないので,JEDI API ライブラリを使用しています.
JEDI API ライブラリには,JwaMsi.pas 以外にも JwaMsiDefs.pas, JwaMsiQuery.pas に MSI API 関係の関数類があります.

WMI の Win32_Product クラスでも MSI でインストールしたアプリの情報を取得することができますが,非常に時間がかかります.



[備考]
Windows Installer CleanUp というツールがマイクロソフトから提供されていました.
これは MSI でインストールしたアプリのアンインストールが正常に実行されない場合に,インストール情報を削除するツールでした.2017年11月19日現在,このツールは提供されていません.同様なツールがマイクロソフトから提供されています.



図1
実行結果

リスト1
MSI ( Microsoft Installer ) でインストールしたアプリのリスト
unit Unit1;

interface

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

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

var
  Form1: TForm1;

implementation

uses
  JwaMsi;

{$R *.dfm}

//-----------------------------------------------------------------------------
//  MSIでインストールしたアプリのプロパティの取得
//  AProduct  : インストールされているアプリのプロダクト名
//              MsiEnumProducts関数で取得した値
//  AProperty : 取得したいプロパティの定数名
//              使用可能な定数名はMsiGetProductInfo関数の説明を参照
//-----------------------------------------------------------------------------
function GetMsiPropertyValueStr(AProduct: PChar; AProperty: PChar): string;
var
  LValue : PChar;
  LLen   : DWORD;
begin
  //取得に必要なバッファサイズを取得(第3引数をnilにする)
  LLen := 0;
  MsiGetProductInfo(AProduct, AProperty, nil, @LLen);

  //取得したバッファサイズにnul終端文字分が含まれないとのことなので追加
  LLen   := LLen + 1;
  LValue := StrAlloc(LLen);
  try
    MsiGetProductInfo(AProduct, AProperty, LValue, @LLen);
    Result := LValue;
  finally
    StrDispose(LValue);
  end;
end;

//=============================================================================
//  MSIでインストールしたアプリをリスト
//
//  JEDI API ライブラリのMSI API(Microsoft Installer API)関係の関数類を利用
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LIndex    : Integer;
  LpProduct : PChar;
  LText     : string;
begin
  Memo1.Lines.Clear;
  Memo1.Lines.BeginUpdate;
    
  LIndex := 0;
  LpProduct := StrAlloc(MAX_PATH);
  try
    while MsiEnumProducts(LIndex, LpProduct) = ERROR_SUCCESS do begin
      Memo1.Lines.Add(string(LpProduct));

      LText := GetMsiPropertyValueStr(LpProduct, INSTALLPROPERTY_PACKAGENAME);
      Memo1.Lines.Add(LText);

      LText := GetMsiPropertyValueStr(LpProduct, INSTALLPROPERTY_INSTALLEDPRODUCTNAME);
      Memo1.Lines.Add(LText);

      LText := GetMsiPropertyValueStr(LpProduct, INSTALLPROPERTY_VERSIONSTRING);
      Memo1.Lines.Add(LText);

      Memo1.Lines.Add('');
      inc(LIndex);
    end;
  finally
    StrDispose(LpProduct);
  end;

  Memo1.Lines.EndUpdate;
  Label1.Caption := '合計 : ' + IntToStr(LIndex);
end;

end.




02_ジェネリクス TList<T> に格納後にソートして表示


次のサンプルは,アプリのプロパティをジェネリクス TList<T> に全て格納した後にソートして表示する例です.こうすることにより,必要なアプリだけを抽出したり,結果の表示を加工したり,ソートする等の操作が容易にできるようになります.また,プロパティを取得する時に操作するよりもコードの見通しもよくなります.

各アプリのプロパティは,レコード型を定義してそのメンバにセットしています.ソードには無名関数を使用しています.



図2
実行結果

リスト2
MSI ( Microsoft Installer ) でインストールしたアプリのリスト
ジェネリクス TList<T> に格納後にソートして表示
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ExtCtrls, Generics.Collections, Generics.Defaults;

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

var
  Form1: TForm1;

implementation

uses
  JwaMsi;

{$R *.dfm}

//----------------------------------------------
//  取得したプロパティの値を格納するレコード型
//----------------------------------------------
type
  TSoftRecord = record
    Product   : string;
    Name      : string;
    PkgName   : string;
    InstDate  : TDateTime;
    Publisher : string;
    VerStr    : string;
  end;
  PSoftRecord = ^TSoftRecord;

//-----------------------------------------------------------------------------
//  MSIでインストールしたアプリのプロパティの取得
//  AProduct  : インストールされているアプリのプロダクト名
//              MsiEnumProducts関数で取得した値
//  AProperty : 取得したいプロパティの定数名
//              使用可能な定数名はMsiGetProductInfo関数の説明を参照
//-----------------------------------------------------------------------------
function GetMsiPropertyValueStr(AProduct: PChar; AProperty: PChar): string;
var
  LValue : PChar;
  LLen   : DWORD;
begin
  //取得に必要なバッファサイズを取得(第3引数をnilにする)
  LLen := 0;
  MsiGetProductInfo(AProduct, AProperty, nil, @LLen);

  //取得したバッファサイズにnul終端文字分が含まれないとのことなので追加
  LLen   := LLen + 1;
  LValue := StrAlloc(LLen);
  try
    MsiGetProductInfo(AProduct, AProperty, LValue, @LLen);
    Result := LValue;
  finally
    StrDispose(LValue);
  end;
end;

//=============================================================================
//  MSIでインストールしたアプリをリスト
//
//  JEDI API ライブラリのMSI API(Microsoft Installer API)関係の関数類を利用
//  TList<T>の使用にはusesにGenerics.Collectionsが必要
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LIndex    : Integer;
  LpProduct : PChar;
  LText     : string;
  LSoftRec  : TSoftRecord;
  LSoftList : TList<TSoftRecord>;
begin
  LIndex := 0;
  LpProduct := StrAlloc(MAX_PATH);

  LSoftList := TList<TSoftRecord>.Create;
  try
    while MsiEnumProducts(LIndex, LpProduct) = ERROR_SUCCESS do begin
      LSoftRec.Product := string(LpProduct);

      LText := GetMsiPropertyValueStr(LpProduct, INSTALLPROPERTY_INSTALLEDPRODUCTNAME);
      LSoftRec.Name := LText;

      LText := GetMsiPropertyValueStr(LpProduct, INSTALLPROPERTY_PACKAGENAME);
      LSoftRec.PkgName := LText;

      LText := GetMsiPropertyValueStr(LpProduct, INSTALLPROPERTY_PUBLISHER);
      LSoftRec.Publisher := LText;

      LText := GetMsiPropertyValueStr(LpProduct, INSTALLPROPERTY_INSTALLDATE);
      LSoftRec.InstDate := StrToDate(FormatFloat('0000/00/00', StrToFloat(LText)));

      LText := GetMsiPropertyValueStr(LpProduct, INSTALLPROPERTY_VERSIONSTRING);
      LSoftRec.VerStr := LText;

      LSoftList.Add(LSoftRec);

      inc(LIndex);
    end;

    //-------------------------------------------------------------------------
    //  TLst<T>のソート
    //  TComparer関係の使用にはusesにGenerics.Defaultsが必要
    //-------------------------------------------------------------------------
    if CheckBox1.Checked then begin
      LSoftList.Sort(TComparer<TSoftRecord>.Construct(
        function(const Left, Right: TSoftRecord): Integer
        begin
          //Name(INSTALLPROPERTY_INSTALLEDPRODUCTNAME)の値でソート
          Result := SysUtils.CompareText(Left.Name, Right.Name);

          //TDateTime型の日付でソートする場合の例
          //Result := (Trunc(Left.InstDate) - Trunc(Right.InstDate));
        end)
      );
    end;
    //-------------------------------------------------------------------------


    //結果の表示
    Memo1.Lines.Clear;
    Memo1.Lines.BeginUpdate;
    for LSoftRec in LSoftList do begin
      Memo1.Lines.Add(LSoftRec.Name);
      Memo1.Lines.Add(LSoftRec.PkgName);
      Memo1.Lines.Add(LSoftRec.Publisher);
      Memo1.Lines.Add(DateTimeToStr(LSoftRec.InstDate));
      Memo1.Lines.Add(LSoftRec.VerStr);
      Memo1.Lines.Add(LSoftRec.Product);

      Memo1.Lines.Add('');
    end;
    Memo1.Lines.EndUpdate;
    Label1.Caption := '合計 : ' + IntToStr(LIndex);
  finally
    StrDispose(LpProduct);
    FreeAndNil(LSoftList);
  end;
end;

end.