0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Windows Installer手引書 Part.32 MSIのインストール進捗をInno Setupで表示する

Last updated at Posted at 2021-05-26

前の記事へ  目次へ  次の記事へ

Windows Installerでは外部UIを使える

ほとんどの場合Windows InstallerでUIを表示する際、すでに用意されているダイアログセットを使用すると思います。しかし、一度に複数のMSIファイルでインストールすると、画面遷移がわずらわしくなってくるので、UI表示を一括して他のプログラムに任せたくなります。そんなときは、MsiSetExternalUIというMSIの関数を利用して、MSIファイルによるインストール状況を外部のプログラムに知らせることができます。

Inno SetupでMsiSetExternalUIを使用する

Windows Installerは外部のプログラムにインストールの進捗状況を伝えるために、MsiSetExternalUI関数の第1引数に与えた関数を呼び出します。Inno Setupで書いた関数はPascalで書かれているので、Windows APIと関数の呼び出し方式が異なり、直接Windows APIから呼び出すことができません。そのため、Inno Setupには、CreateCallback関数が用意されています。この関数が返す関数のアドレスを使うと、直接Windows APIからPascalの関数を呼び出すことができます。

Windows Installerから渡される情報の中身を確認する

まず、適当なMSIファイルを用意します。この記事では、「Part32_01.msi」と「cab1.cab」を用意しました。このMSIファイルはUIを持っていなくても問題ありません(もちろんUIを持っていても問題ありません)。このMSIファイルをインストールし、Windows installerから渡されてくる情報をInno SetupのDebugログに出力するインストーラーを実装してみました。

Part32_01.iss
#define MsiName "Part32_01.msi"

[Setup]
DisableReadyPage=True
DisableReadyMemo=True
AppName=Part32_01
AppVersion=1.0.0
UsePreviousPrivileges=False
CreateAppDir=False
ShowLanguageDialog=no
Uninstallable=no
OutputBaseFilename=Part32_01_Installer

[Files]
Source: "bin\Debug\{#MsiName}"; DestDir: "{tmp}"; Flags: deleteafterinstall
Source: "bin\Debug\cab1.cab"; DestDir: "{tmp}"; Flags: deleteafterinstall

[Code]
const
  INSTALLUILEVEL_NOCHANGE = 0;    // UI level is unchanged
  INSTALLUILEVEL_DEFAULT  = 1;    // default UI is used
  INSTALLUILEVEL_NONE     = 2;    // completely silent installation
  INSTALLUILEVEL_BASIC    = 3;    // simple progress and error handling
  INSTALLUILEVEL_REDUCED  = 4;    // authored UI, wizard dialogs suppressed
  INSTALLUILEVEL_FULL     = 5;    // authored UI with wizards, progress, errors
  INSTALLUILEVEL_ENDDIALOG    = $80; // display success/failure dialog at end of install
  INSTALLUILEVEL_PROGRESSONLY = $40; // display only progress dialog
  INSTALLUILEVEL_HIDECANCEL   = $20; // do not display the cancel button in basic UI
  INSTALLUILEVEL_SOURCERESONLY = $100; // force display of source resolution even if quiet

  INSTALLMESSAGE_FATALEXIT      = $00000000; // premature termination, possibly fatal OOM
  INSTALLMESSAGE_ERROR          = $01000000; // formatted error message
  INSTALLMESSAGE_WARNING        = $02000000; // formatted warning message
  INSTALLMESSAGE_USER           = $03000000; // user request message
  INSTALLMESSAGE_INFO           = $04000000; // informative message for log
  INSTALLMESSAGE_FILESINUSE     = $05000000; // list of files in use that need to be replaced
  INSTALLMESSAGE_RESOLVESOURCE  = $06000000; // request to determine a valid source location
  INSTALLMESSAGE_OUTOFDISKSPACE = $07000000; // insufficient disk space message
  INSTALLMESSAGE_ACTIONSTART    = $08000000; // start of action: action name & description
  INSTALLMESSAGE_ACTIONDATA     = $09000000; // formatted data associated with individual action item
  INSTALLMESSAGE_PROGRESS       = $0A000000; // progress gauge info: units so far, total
  INSTALLMESSAGE_COMMONDATA     = $0B000000; // product info for dialog: language Id, dialog caption
  INSTALLMESSAGE_INITIALIZE     = $0C000000; // sent prior to UI initialization, no string data
  INSTALLMESSAGE_TERMINATE      = $0D000000; // sent after UI termination, no string data
  INSTALLMESSAGE_SHOWDIALOG     = $0E000000; // sent prior to display or authored dialog or wizard

  INSTALLLOGMODE_FATALEXIT      = 1 shl (INSTALLMESSAGE_FATALEXIT      shr 24);
  INSTALLLOGMODE_ERROR          = 1 shl (INSTALLMESSAGE_ERROR          shr 24);
  INSTALLLOGMODE_WARNING        = 1 shl (INSTALLMESSAGE_WARNING        shr 24);
  INSTALLLOGMODE_USER           = 1 shl (INSTALLMESSAGE_USER           shr 24);
  INSTALLLOGMODE_INFO           = 1 shl (INSTALLMESSAGE_INFO           shr 24);
  INSTALLLOGMODE_RESOLVESOURCE  = 1 shl (INSTALLMESSAGE_RESOLVESOURCE  shr 24);
  INSTALLLOGMODE_OUTOFDISKSPACE = 1 shl (INSTALLMESSAGE_OUTOFDISKSPACE shr 24);
  INSTALLLOGMODE_ACTIONSTART    = 1 shl (INSTALLMESSAGE_ACTIONSTART    shr 24);
  INSTALLLOGMODE_ACTIONDATA     = 1 shl (INSTALLMESSAGE_ACTIONDATA     shr 24);
  INSTALLLOGMODE_COMMONDATA     = 1 shl (INSTALLMESSAGE_COMMONDATA     shr 24);
  INSTALLLOGMODE_PROPERTYDUMP   = 1 shl (INSTALLMESSAGE_PROGRESS       shr 24); // log only
  INSTALLLOGMODE_VERBOSE        = 1 shl (INSTALLMESSAGE_INITIALIZE     shr 24); // log only
  INSTALLLOGMODE_EXTRADEBUG     = 1 shl (INSTALLMESSAGE_TERMINATE      shr 24); // log only
  INSTALLLOGMODE_PROGRESS       = 1 shl (INSTALLMESSAGE_PROGRESS       shr 24); // external handler only
  INSTALLLOGMODE_INITIALIZE     = 1 shl (INSTALLMESSAGE_INITIALIZE     shr 24); // external handler only
  INSTALLLOGMODE_TERMINATE      = 1 shl (INSTALLMESSAGE_TERMINATE      shr 24); // external handler only
  INSTALLLOGMODE_SHOWDIALOG     = 1 shl (INSTALLMESSAGE_SHOWDIALOG     shr 24); // external handler only

var
  StrCounter: Integer;
  
type
  INSTALLUILEVEL = Longint;
  LPHWND = Cardinal;
  LPVOID = Cardinal;
  LPCWSTR = Cardinal;

function MsiSetInternalUI(dwUILevel: INSTALLUILEVEL; phWnd: LPHWND): INSTALLUILEVEL;
external 'MsiSetInternalUI@msi.dll stdcall';
function MsiSetExternalUIW(puiHandler: Longword; dwMessageFilter: DWORD; pvContext: String): Integer;
external 'MsiSetExternalUIW@msi.dll stdcall';
function MsiInstallProductW(szPackagePath: String; szCommandLine: String): UINT;
external 'MsiInstallProductW@msi.dll stdcall';

function TestMyBasicUIHandler(pvContext: LPVOID; iMessageType: UINT; szMessage: String): Integer;
begin
  StrCounter := StrCounter + 1;
  Log(Format('%d : %X', [StrCounter, iMessageType]));
  case ($FF000000 and iMessageType) of
    INSTALLMESSAGE_FATALEXIT:     Log(Format('%d : INSTALLMESSAGE_FATALEXIT', [StrCounter]));
    INSTALLMESSAGE_ERROR:         Log(Format('%d : INSTALLMESSAGE_ERROR', [StrCounter]));
    INSTALLMESSAGE_WARNING:       Log(Format('%d : INSTALLMESSAGE_WARNING', [StrCounter]));
    INSTALLMESSAGE_USER:          Log(Format('%d : INSTALLMESSAGE_USER', [StrCounter]));
    INSTALLMESSAGE_INFO:          Log(Format('%d : INSTALLMESSAGE_INFO', [StrCounter]));
    INSTALLMESSAGE_FILESINUSE:    Log(Format('%d : INSTALLMESSAGE_FILESINUSE', [StrCounter]));
    INSTALLMESSAGE_RESOLVESOURCE: Log(Format('%d : INSTALLMESSAGE_RESOLVESOURCE', [StrCounter]));
    INSTALLMESSAGE_OUTOFDISKSPACE:Log(Format('%d : INSTALLMESSAGE_OUTOFDISKSPACE', [StrCounter]));
    INSTALLMESSAGE_ACTIONSTART:   Log(Format('%d : INSTALLMESSAGE_ACTIONSTART', [StrCounter]));
    INSTALLMESSAGE_ACTIONDATA:    Log(Format('%d : INSTALLMESSAGE_ACTIONDATA', [StrCounter]));
    INSTALLMESSAGE_PROGRESS:      Log(Format('%d : INSTALLMESSAGE_PROGRESS', [StrCounter]));
    INSTALLMESSAGE_COMMONDATA:    Log(Format('%d : INSTALLMESSAGE_COMMONDATA', [StrCounter]));
    INSTALLMESSAGE_INITIALIZE:    Log(Format('%d : INSTALLMESSAGE_INITIALIZE', [StrCounter]));
    INSTALLMESSAGE_TERMINATE:     Log(Format('%d : INSTALLMESSAGE_TERMINATE', [StrCounter]));
    INSTALLMESSAGE_SHOWDIALOG:    Log(Format('%d : INSTALLMESSAGE_SHOWDIALOG', [StrCounter]));
  end;
  Log(Format('%d : %s', [StrCounter, szMessage]));
  result := 0;
end;

procedure CurStepChanged(CurStep: TSetupStep);
var 
  tmpPath: String;
  ResultCode: Integer;
begin
  // msi/cabファイルの展開が終わったら、msiを起動する
  if CurStep=ssPostInstall then begin
    MsiSetInternalUI(INSTALLUILEVEL_NONE or INSTALLUILEVEL_SOURCERESONLY, 0);
    MsiSetExternalUIW(CreateCallback(@TestMyBasicUIHandler), INSTALLLOGMODE_PROGRESS 
         or INSTALLLOGMODE_FATALEXIT or INSTALLLOGMODE_ERROR
         or INSTALLLOGMODE_WARNING or INSTALLLOGMODE_USER or INSTALLLOGMODE_INFO
         or INSTALLLOGMODE_RESOLVESOURCE or INSTALLLOGMODE_OUTOFDISKSPACE
         or INSTALLLOGMODE_ACTIONSTART or INSTALLLOGMODE_ACTIONDATA
         or INSTALLLOGMODE_COMMONDATA or INSTALLLOGMODE_PROGRESS
         or INSTALLLOGMODE_INITIALIZE or INSTALLLOGMODE_TERMINATE
         or INSTALLLOGMODE_SHOWDIALOG,
        'TEST'
    );
    MsiInstallProductW(ExpandConstant('{tmp}\{#MsiName}'), '');
  end;
end;

function InitializeSetup(): Boolean;
begin
  StrCounter := 0;
  result := True;
end;

インストールを実行して、Debugログをファイルに出力して中身を確認してください。非常に多くの情報が渡されてきており、UI表示に関係なさそうな情報も渡されてくることが分かります。MSIファイルのインストールログを見たことがあるなら、内容がほとんど一緒であることに気付くでしょう。Inno Setupでは、この中からインストールの進捗表示で使う、現在のステータス文字列や、ファイル名やレジストリ項目、プログレスバーの進捗度合い等を取り出して、UIに反映させます。

サンプルを移植する

Windows Installerから渡されてくる情報は、そのままUIに反映することが考慮されていないので、少し加工して使用する必要があります。また、プログレスバー向けの情報を反映するのもかなり面倒な作業です。ここでは、マイクロソフトのサンプルコードを移植してみました。これをこのまま製品に適用できるわけではありませんが、仕組みを読み解くには役に立つでしょう。製品に適用する場合は、FileInUseメッセージやエラー、ワーニングを受けたときの動作を決めておく必要があります。ここでは表示する文字列の加工も、最低限やっているだけです。また、できるだけマイクロソフトのサンプルに近づけるため、グローバル変数を多用していますので、組織の実装ルールに当てはめて実装を変える必要があるかもしれません。

Part32_02.iss
#define MsiName "Part32_02.msi"

[Setup]
DisableReadyPage=True
DisableReadyMemo=True
AppName=Part32_02
AppVersion=1.0.0
UsePreviousPrivileges=False
CreateAppDir=False
ShowLanguageDialog=no
Uninstallable=no
OutputBaseFilename=Part32_02_Installer

[Files]
Source: "bin\Debug\{#MsiName}"; DestDir: "{tmp}"; Flags: deleteafterinstall
Source: "bin\Debug\cab1.cab"; DestDir: "{tmp}"; Flags: deleteafterinstall

[Code]
const
  WM_USER         = $0400;
  PBM_SETRANGE    = WM_USER+1;
  PBM_SETPOS      = WM_USER+2;
  PBM_DELTAPOS    = WM_USER+3;
  PBM_SETSTEP     = WM_USER+4;
  PBM_STEPIT      = WM_USER+5;
  PBM_SETRANGE32  = WM_USER+6;  // lParam = high, wParam = low
  PBM_GETRANGE    = WM_USER+7;  // wParam = return (TRUE ? low : high). lParam = PPBRANGE or NULL
  PBM_GETPOS      = WM_USER+8;
  PBM_SETBARCOLOR = WM_USER+9;  // lParam = bar color

  INSTALLUILEVEL_NOCHANGE      = 0;    // UI level is unchanged
  INSTALLUILEVEL_DEFAULT       = 1;    // default UI is used
  INSTALLUILEVEL_NONE          = 2;    // completely silent installation
  INSTALLUILEVEL_BASIC         = 3;    // simple progress and error handling
  INSTALLUILEVEL_REDUCED       = 4;    // authored UI, wizard dialogs suppressed
  INSTALLUILEVEL_FULL          = 5;    // authored UI with wizards, progress, errors
  INSTALLUILEVEL_ENDDIALOG     = $80;  // display success/failure dialog at end of install
  INSTALLUILEVEL_PROGRESSONLY  = $40;  // display only progress dialog
  INSTALLUILEVEL_HIDECANCEL    = $20;  // do not display the cancel button in basic UI
  INSTALLUILEVEL_SOURCERESONLY = $100; // force display of source resolution even if quiet

  INSTALLMESSAGE_FATALEXIT      = $00000000; // premature termination, possibly fatal OOM
  INSTALLMESSAGE_ERROR          = $01000000; // formatted error message
  INSTALLMESSAGE_WARNING        = $02000000; // formatted warning message
  INSTALLMESSAGE_USER           = $03000000; // user request message
  INSTALLMESSAGE_INFO           = $04000000; // informative message for log
  INSTALLMESSAGE_FILESINUSE     = $05000000; // list of files in use that need to be replaced
  INSTALLMESSAGE_RESOLVESOURCE  = $06000000; // request to determine a valid source location
  INSTALLMESSAGE_OUTOFDISKSPACE = $07000000; // insufficient disk space message
  INSTALLMESSAGE_ACTIONSTART    = $08000000; // start of action: action name & description
  INSTALLMESSAGE_ACTIONDATA     = $09000000; // formatted data associated with individual action item
  INSTALLMESSAGE_PROGRESS       = $0A000000; // progress gauge info: units so far, total
  INSTALLMESSAGE_COMMONDATA     = $0B000000; // product info for dialog: language Id, dialog caption
  INSTALLMESSAGE_INITIALIZE     = $0C000000; // sent prior to UI initialization, no string data
  INSTALLMESSAGE_TERMINATE      = $0D000000; // sent after UI termination, no string data
  INSTALLMESSAGE_SHOWDIALOG     = $0E000000; // sent prior to display or authored dialog or wizard
  INSTALLMESSAGE_PERFORMANCE    = $0F000000; // log only, to log performance number like action time
  INSTALLMESSAGE_RMFILESINUSE   = $19000000; // the list of apps that the user can request Restart Manager to shut down and restart
  INSTALLMESSAGE_INSTALLSTART   = $1A000000; // sent prior to server-side install of a product
  INSTALLMESSAGE_INSTALLEND     = $1B000000; // sent after server-side install

  INSTALLLOGMODE_FATALEXIT      = 1 shl (INSTALLMESSAGE_FATALEXIT      shr 24);
  INSTALLLOGMODE_ERROR          = 1 shl (INSTALLMESSAGE_ERROR          shr 24);
  INSTALLLOGMODE_WARNING        = 1 shl (INSTALLMESSAGE_WARNING        shr 24);
  INSTALLLOGMODE_USER           = 1 shl (INSTALLMESSAGE_USER           shr 24);
  INSTALLLOGMODE_INFO           = 1 shl (INSTALLMESSAGE_INFO           shr 24);
  INSTALLLOGMODE_RESOLVESOURCE  = 1 shl (INSTALLMESSAGE_RESOLVESOURCE  shr 24);
  INSTALLLOGMODE_OUTOFDISKSPACE = 1 shl (INSTALLMESSAGE_OUTOFDISKSPACE shr 24);
  INSTALLLOGMODE_ACTIONSTART    = 1 shl (INSTALLMESSAGE_ACTIONSTART    shr 24);
  INSTALLLOGMODE_ACTIONDATA     = 1 shl (INSTALLMESSAGE_ACTIONDATA     shr 24);
  INSTALLLOGMODE_COMMONDATA     = 1 shl (INSTALLMESSAGE_COMMONDATA     shr 24);
  INSTALLLOGMODE_PROPERTYDUMP   = 1 shl (INSTALLMESSAGE_PROGRESS       shr 24); // log only
  INSTALLLOGMODE_VERBOSE        = 1 shl (INSTALLMESSAGE_INITIALIZE     shr 24); // log only
  INSTALLLOGMODE_EXTRADEBUG     = 1 shl (INSTALLMESSAGE_TERMINATE      shr 24); // log only
  INSTALLLOGMODE_PROGRESS       = 1 shl (INSTALLMESSAGE_PROGRESS       shr 24); // external handler only
  INSTALLLOGMODE_INITIALIZE     = 1 shl (INSTALLMESSAGE_INITIALIZE     shr 24); // external handler only
  INSTALLLOGMODE_TERMINATE      = 1 shl (INSTALLMESSAGE_TERMINATE      shr 24); // external handler only
  INSTALLLOGMODE_SHOWDIALOG     = 1 shl (INSTALLMESSAGE_SHOWDIALOG     shr 24); // external handler only

  LANG_NEUTRAL = $00;

type
  INSTALLUILEVEL = Longint;
  INSTALLMESSAGE = Longint;
  LPHWND = Cardinal;
  LPVOID = Cardinal;
  LPCWSTR = Cardinal;

var
  StrCounter: Integer;
  bFirstTime: Boolean;
  g_bEnableActionData: Boolean;     // INSTALLOGMODE_ACTIONDATAメッセージが進捗情報を送るならTrue
  iField: array [0..3] of Integer;  // INSTALLOGMODE_PROGRESSデータハンドルへのレコードの配列
  g_bForwardProgress: Boolean;      // プログレスバーを正方向に進めるならTRUE
  g_iProgressTotal: Integer;        // プログレスバーの合計コマ数
  g_iProgress: Integer;             // 進捗の総数
  g_bScriptInProgress: Boolean;
  iCurPos: Integer;
  g_wLANGID: Word;                  // neutral languageに初期化
  g_rgiField: array [0..2] of Integer; // INSTALLOGMODE_COMMONDATAデータハンドルの配列
  progressStep: Integer;

// 外部DLLの関数宣言
function MsiSetInternalUI(dwUILevel: INSTALLUILEVEL; phWnd: LPHWND): INSTALLUILEVEL;
external 'MsiSetInternalUI@msi.dll stdcall';
function MsiSetExternalUIW(puiHandler: Longword; dwMessageFilter: DWORD; pvContext: String): Integer;
external 'MsiSetExternalUIW@msi.dll stdcall';
function MsiInstallProductW(szPackagePath: String; szCommandLine: String): UINT;
external 'MsiInstallProductW@msi.dll stdcall';

// プロトタイプ宣言
function ParseCommonDataString(sz: String): Boolean; forward;
function ParseProgressString(sz: String): Boolean; forward;

// ------------------------------------------------------------------
//  TestMyBasicUIHandler()
//  MSIで外部ユーザー・インターフェース・ハンドラを扱うデモ
function TestMyBasicUIHandler(pvContext: LPVOID; iMessageType: UINT; szMessage: String): Integer;
var
  r1: INSTALLUILEVEL;
  mt: INSTALLMESSAGE;
  uiFlags: UINT;
  wParam: Int64;  // WPARAM type
  nCmdShow: Integer;
begin
  result := 0;
  if bFirstTime then begin
    r1 := MsiSetInternalUI(INSTALLUILEVEL_BASIC, 0);
    bFirstTime := False;
  end;
  mt := $FF000000 and iMessageType;
  uiFlags := $00FFFFFF and iMessageType;
  case (mt) of
    INSTALLMESSAGE_FATALEXIT: begin
      // ここでfatal errorメッセージを受け取り表示する
      result := MsgBox(szMessage, mbError, uiFlags);
    end;
    INSTALLMESSAGE_ERROR: begin
      // ここでエラーメッセージを受け取り表示する。
      // 言語とタイトルはcommon dataメッセージから得られる
      result := MsgBox(szMessage, mbError, uiFlags);
    end;
    INSTALLMESSAGE_WARNING: begin
      // ここでwarningメッセージを受け取り表示する
      result := MsgBox(szMessage, mbError, uiFlags);
    end;
    INSTALLMESSAGE_USER: begin
      // ここでuserメッセージを受け取る
      // メッセージボックススタイルのフラグを得るためにuiFlagsをパースし、IDOK, IDYES等を返す
      result := IDOK;
    end;
    INSTALLMESSAGE_INFO: begin
      // Actionの実行状況などが返される、一般的にインストーラーのUIには表示しない
      result := IDOK;
    end;
    INSTALLMESSAGE_FILESINUSE: begin
      // FilesInUseダイアログを表示する
      // ユーザーにファイルを使っているアプリケーション名を提示するには、メッセージテキストをパースする
      result := 0;
    end;
    INSTALLMESSAGE_RESOLVESOURCE: begin
      // ResolveSourceのために常に0を返す
      result := 0;
    end;
    INSTALLMESSAGE_OUTOFDISKSPACE: begin
      // ここでユーザーメッセージを得る
      result := IDOK;
    end;
    INSTALLMESSAGE_ACTIONSTART: begin
      // 新しくアクションが開始された、action dataもこのアクションによって送られる
      WizardForm.StatusLabel.Caption := Copy(szMessage, 18, Length(szMessage)-17);
      WizardForm.FilenameLabel.Caption := '';
      g_bEnableActionData := False;
      result := IDOK;
    end;
    INSTALLMESSAGE_ACTIONDATA: begin
      // ファイル、レジストリ等の書き込みを順次知らせる
      // 進捗の合計が初期化されたら実行する
      if 0 <> g_iProgressTotal then begin
        // アクションテキストを表示する
        WizardForm.FilenameLabel.Caption := szMessage;
        if g_bEnableActionData then begin
          // progressStepに設定しただけ進める
          WizardForm.ProgressGauge.Position := WizardForm.ProgressGauge.Position + progressStep;
        end;
      end;
      result := IDOK;
    end;
    INSTALLMESSAGE_PROGRESS: begin
      // プログレスバーの進捗を管理する
      if ParseProgressString(szMessage) then begin
        case (iField[0]) of
          0: begin  // プログレスバーをリセットする
            // field 1 = 0, field 2 = コマ数の合計,  field 3 = 方向, field 4 = 進行中か
            // プログレスバーの合計コマ数を取得する
            g_iProgressTotal := iField[1];
            // 方向を決める
            if iField[2] = 0 then begin
              g_bForwardProgress := True;
            end else begin  // iField[2] == 1
              g_bForwardProgress := False;
            end;
            // 方向に応じたプログレスバーの現在位置を取得する
            // 前進なら現在位置は0
            // 後進なら現在位置は合計コマ数
            if g_bForwardProgress then begin
              g_iProgress := 0;
            end else begin
              g_iProgress := g_iProgressTotal;
            end;
            WizardForm.ProgressGauge.Max := g_iProgressTotal;
            // もしg_bScriptInProgressが真ならプログレスバーは終わり、そうでなければリセット(そして進行方向に従ってセットアップする)
            if g_bScriptInProgress then begin
              WizardForm.ProgressGauge.Position := g_iProgressTotal;
            end else begin
              WizardForm.ProgressGauge.Position := g_iProgress;
            end;
            iCurPos := 0;
            // 新しい値を決定する
            // もしnew state = 1 (script in progress)なら、"Please wait..."メッセージを送る
            // new state = 1は総コマ数が推定であり、正確でないことを示す
            if iField[3] = 1 then begin
              g_bScriptInProgress := True;
            end else begin
              g_bScriptInProgress := False;
            end;
          end;
          1: begin  // ActionInfo
            // field 1 = 1, field 2はバーを進めるコマ数を得られる
            // field 3がゼロなら無視する
            if iField[2]<>0 then begin
                // g_bForwardProgressによって進捗方向が決まり、reset progress msgでセットされる
                if g_bForwardProgress then begin
                  wParam := iField[1];
                end else begin
                  wParam := -1 * iField[1];
                end;
                progressStep := wParam;
                g_bEnableActionData := True;
            end else begin
                g_bEnableActionData := False;
            end;
          end;
          2: begin  //ProgressReport
            // プログレスバーの合計コマ数が初期化されたら実行する
            if 0 <> g_iProgressTotal then begin
              iCurPos := iCurPos + iField[1];
              //field 1 = 2,field 2は動いたバーのコマ数を持っていることを示す
              // reset progressメッセージでセットされたg_bForwardProgressによって移動方向が決まる
              if g_bForwardProgress then begin
                wParam := iCurPos;
              end else begin
                wParam := -1 * iCurPos;
              end;
              WizardForm.ProgressGauge.Position := wParam;
            end;
          end;
          3: begin  // ProgressAddition - そのまま抜ける (これを使って何かする必要はない -- 総コマ数の調整のためにある)
            result := IDOK;
          end;
        end;
      end;
    end;
    INSTALLMESSAGE_COMMONDATA: begin
      // キャンセルボタンの表示制御等
      if ParseCommonDataString(szMessage) then begin
        case (g_rgiField[0]) of
          0: begin
            // field 1 = 0, field 2 = LANGID, field 3 = CodePage
            g_wLANGID := g_rgiField[1];
          end;
          1: begin
            // field 1 = 1, field 2 = CAPTION
            // これはMessageBoxesのキャプションとして使うことができる
          end;
          2: begin
            // field 1 = 2, field 2 = 0 (cancelボタンを隠す) OR 1 (cancelボタンを表示する)
            if g_rgiField[1] = 0 then begin
              WizardForm.CancelButton.Hide;
            end else begin
              WizardForm.CancelButton.Show;
            end;
          end;
        end;
      end;
      result := IDOK;
    end;
    INSTALLMESSAGE_INITIALIZE: begin
      // 一番最初に1回だけ来るメッセージ
      // このメッセージは内部のUI初期化の前に受け取る、文字列データは含まない
      result := IDOK;
    end;
    INSTALLMESSAGE_TERMINATE: begin
      // 一番最後に1回だけ来るメッセージ
      // UI停止後に送られる、文字列データは含まない
      result := IDOK;
    end;
    INSTALLMESSAGE_SHOWDIALOG: begin
      // ダイアログやウィザードが表示される前に送られる
      result := IDOK;
    end;
    else begin
      result := 0;
    end;
  end;
end;

function ParseCommonDataString(sz: String): Boolean;
var
  StrLength, i: Integer;
begin
  StrLength := Length(sz);
  g_rgiField[0] := StrToInt(sz[4]);
  g_rgiField[1] := StrToInt(Copy(sz, 9, StrLength-8));
  Result := True;
end;

function ParseProgressString(sz: String): Boolean;
var
  i, segCounter: Integer;
  segIndex: array[1..8] of Integer;
begin
  segCounter := 1;
  segIndex[segCounter] := 1;
  for i:=1 to Length(sz) do begin
    if sz[i]=' ' then begin
      segCounter := segCounter + 1;
      segIndex[segCounter] := i + 1;
    end;
    if segCounter=8 then break;
  end;
  iField[0] := StrToInt(Copy(sz, segIndex[2], segIndex[3]-segIndex[2]-1));
  iField[1] := StrToInt(Copy(sz, segIndex[4], segIndex[5]-segIndex[4]-1));
  iField[2] := StrToInt(Copy(sz, segIndex[6], segIndex[7]-segIndex[6]-1));
  iField[3] := StrToInt(Copy(sz, segIndex[8], Length(sz)-segIndex[8]+1));
  Result := True;
end;

// ------------------------------------------------------------------
// 一番最初に呼ばれるイベント
function InitializeSetup(): Boolean;
begin
  StrCounter := 0;  // for Test
  g_wLANGID := LANG_NEUTRAL;
  g_iProgressTotal := 0;
  g_iProgress := 0;
  iCurPos := 0;
  bFirstTime := True;
  g_bForwardProgress := True;
  g_bScriptInProgress := False;
  progressStep := 1;
  Result := True;
end;

// ------------------------------------------------------------------
//
procedure CurStepChanged(CurStep: TSetupStep);
var 
  tmpPath: String;
  ResultCode: Integer;
begin
  // msi/cabファイルの展開が終わったら、msiを起動する
  if CurStep=ssPostInstall then begin
    MsiSetInternalUI(INSTALLUILEVEL_NONE or INSTALLUILEVEL_SOURCERESONLY, 0);
    MsiSetExternalUIW(CreateCallback(@TestMyBasicUIHandler), INSTALLLOGMODE_PROGRESS 
         or INSTALLLOGMODE_FATALEXIT or INSTALLLOGMODE_ERROR
         or INSTALLLOGMODE_WARNING or INSTALLLOGMODE_USER or INSTALLLOGMODE_INFO
         or INSTALLLOGMODE_RESOLVESOURCE or INSTALLLOGMODE_OUTOFDISKSPACE
         or INSTALLLOGMODE_ACTIONSTART or INSTALLLOGMODE_ACTIONDATA
         or INSTALLLOGMODE_COMMONDATA or INSTALLLOGMODE_PROGRESS
         or INSTALLLOGMODE_INITIALIZE or INSTALLLOGMODE_TERMINATE
         or INSTALLLOGMODE_SHOWDIALOG,
        'TEST'
    );
    MsiInstallProductW(ExpandConstant('{tmp}\{#MsiName}'), '');
  end;
end;

前の記事へ  目次へ  次の記事へ

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?