Windows Installer で Session.Message メソッドを使う

MSI を作成している最中、デバッグ用途にカスタムアクション内に VBScript で MsgBox を入れ込むということを
よくやると思います。デバッグ用にはこれで十分なのですが、リリース版で何等かのメッセージボックスを表示したいと
いう場合、同様にカスタムアクションで VBscript の MsgBox で表示すると次の現象が発生します。

  • VBScript で作成したメッセージボックスがモードレスになり、インストールウィザードの後ろに隠れてしまう
    MsgBox に 4096(vbSystemModal) を加えても変わらない
  • メッセージボックスを表示中でもインストールウィザードを操作することができてしまう

リリース版で、この動きになってしまうと、ユーザーに悪い印象を与えかねません。
この原因ですが、以前、このブログでも紹介したように、カスタムアクション用の msiexec.exe が
別に起動されてしまうことによる影響です。

    WindowsInstallerアーキテクチャ図

ところが、Custom Action Types をよく見ると、Type 19 というものがあります。説明には、

Displays a specified error message and returns failure, terminating the installation.

と記述があり、何やら、メッセージを表示することができるようです。

確かに、Windows Installer による MSI ファイルのインストールを行っている最中に Windows Installer が
表示するメッセージボックス(例えば、キャンセルとか)を思い出してみると、インストールウィザードを親として
モーダルダイアログになっています。
SPY++ で表示されたメッセージボックスを見ると、確かに親ウィンドウがインストールウィザード画面になっていますし、
タスクバーを見ても、アイコンが 1 つだけになっています。
つまり、カスタムアクション用 msiexec.exe が動かずにエラーメッセージボックスが表示されます。
ということは、UI シーケンスを処理する msiexec.exe や実行シーケンスを処理する msiexec.exe 内に
メッセージボックスを表示する仕組みがあるということです。
このメッセージボックスを表示する仕組みが、今回紹介する Session.Message メソッドです。

使い方が何気に複雑なので、きちんと調べてみました…。

なお、カスタムアクションを DLL で実装して、DLL 内から

MessageBox(GetForegroundWindow(),
    TEXT("カスタムアクションが呼ばれました"),
    TEXT("Call CA_MyDebug"), MB_OK | MB_ICONINFORMATION);

というように、GetForegroundWindow() で親ウィンドウを指定した場合には、インストールウィザードに対する
モーダル表示ができます。

まずは、リファレンスから。

kind パラメーターには、どのような種別のメッセージボックスなのかを指定できるようです。

では、サンプルとして、Session.Message メソッドを使って、次のエラーメッセージを表示してみます。

123456 サンプルエラーが発生しました。

まず、Windows Installer データベースの Error テーブルに
Error テーブルに次のようにテータを用意します。

Error(Integer) Message(Template)
30000 [2] サンプルエラーが発生しました。処理を継続しますか?

次に、VB スクリプトを作成します。
Session.Message メソッドの recode パラメーターには、Windows Installer の Record オブジェクトを渡すので、
Installer.CreateRecord メソッドを使ってオブジェクトを作成します。

Record オブジェクトを生成したら、先ほど用意した Error テーブルの Error カラム値と表示したい番号を
設定します。次のような ShowSessionMsg 関数にしてみました。

‘Custom Action Return Values 定数値の宣言
Const msiDoActionStatusSuccess  = 1
Const msiDoActionStatusFailure  = 3
‘Session.Message の kind
Const msiMessageTypeError = &H01000000
‘メッセージボックスで使用
Const vbYesNo = 4
Const vbDefaultButton2 = 256
Const vbYes = 6

Function ShowSessionMsg()
    Set statusRecordObj = session.Installer.CreateRecord(2)
    statusRecordObj.IntegerData(1) = 30000 ← Error カラム値
    statusRecordObj.IntegerData(2) = 123456 ← 表示したい番号
    Dim rt
    rt = Session.Message (msiMessageTypeError + vbYesNo + vbDefaultButton2, statusRecordObj)
    if CInt(rt) = vbYes then
        ShowMsgBox2 = msiDoActionStatusSuccess
    Else
        ShowMsgBox2 = msiDoActionStatusFailure
    End If
    Exit Function
End Function

あとは、これを Custom Action Type 6 に組み込むだけです。

Action Type Source Target ExtendedType
CA_ShowMsg 518 Binary テーブルで VBS を指定 ShowSessionMsg  

試しに、UI シーケンスの MigrateFeatureStates シーケンスの後に組み込んでみます。
今回は、InstallShield を使い、基本 MSI プロジェクトで MSI を作成してみました。ビルドした MSI を実行してみると、

MSI_SessionMessageサンプル

というように表示されます。メッセージボックスは、モーダルで表示されました。

Session.Message メソッドは、kind パラメーターに様々な値を指定することができるので、いろいろな種類の
メッセージボックスを表示することができます。使い方も慣れると、簡単に使えると思いますので
いろいろと実験してみるといいと思います。
やはり、一番よいところは、UI シーケンス内の msiexec.exe や実行シーケンス内の msiexec.exe に
対してモーダル表示してくれる点です。
インストーラーのエクスペリエンスに、ぜひ、加えてください。

コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト / 変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト / 変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト / 変更 )

Google+ フォト

Google+ アカウントを使ってコメントしています。 ログアウト / 変更 )

%s と連携中

%d人のブロガーが「いいね」をつけました。