カスタムアクションとUI
カスタムアクションでは、
- ダイアログボックスの表示
- インストールログへの任意の文字出力
- インストールの進捗バーやバーの周囲に表示される文字のコントロール
といった3つの機能を、「Windows Installerにメッセージを送る」という1つの仕組みで呼び出すことができます。今回の記事では、1.のダイアログボックスの表示を扱います。Windows Installerにメッセージを送る方法を簡単に言うと、「レコード」という形式で送るべきメッセージを作成し、MsiProcessMessage()関数でレコードをWindows Installerに送る、となります。
レコード
レコードの構造は、C言語で言うところの構造体1の配列に似ています2。そして1つのレコードには下記3種類のデータを1つずつ格納できます。
- 整数
- 文字列
- ストリーム
そして、レコードを扱うための関数をWindows Installerが用意しています。レコードの生成、書き込み、廃棄の関数は下表の通りです。
関数名 | 機能 |
---|---|
MsiCreateRecord() | レコードの生成 |
MsiRecordSetInteger() | 整数の設定 |
MsiRecordSetString() | 文字列の設定3 |
MsiRecordSetStream() | ストリームの設定3 |
MsiCloseHandle() | レコードの破棄 |
ダイアログボックスを表示するには、レコードを1つ生成し、レコードに表示する文字列を設定してやります。
基本
前回説明を先送りしたコードを少し改造して以下に示します。ここではダイアログボックスを表示するコードを書いています。これを例に説明していきましょう。
PMSIHANDLE hRecord = MsiCreateRecord(1);
MsiRecordSetString(hRecord, 0, "This is test message.");
MsiProcessMessage(hInstall, INSTALLMESSAGE(INSTALLMESSAGE_ERROR | MB_OK), hRecord);
1行目でMsiCreateRecord()
関数を使用し、レコードを1つ生成します。レコードの型をPMSIHANDLE
とすると、関数を抜けたとき自動的にレコードが破棄されます4。2行目のMsiRecordSetString()
関数で1つ目(0から数え始まるので第2引数に0を指定)のレコードに文字列を設定します。そして、第3引数に本文の文字列を指定。3行目のMsiProcessMessage()
関数を使い、レコードをWindow Installerに送ります。第2引数の指定で、色々な種類のダイアログボックスを表示できるようになっています。ここでは、この指定に従い、[OK]ボタンが一つの黄色アイコンのエラーダイアログボックスが表示されます。
Error Tableにメッセージを格納する
MSIファイルのErrorテーブルに、ダイアログボックスで表示する文字列を格納することができます。Errorテーブルの説明によると、カスタムアクション用として使えるerror numberは25000から30000ということになっているので、この範囲の番号を使用します。WiX Toolsetでは、下記のようにUIエレメントの中にErrorエレメントを定義してErrorテーブルを定義します。
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Product Id="C2667EC3-95EA-4A5A-81C6-8627E1B9E2CA" Name="Part18_02" Language="1033" Version="1.0.0" Manufacturer="tohshima" UpgradeCode="82b1414a-ba67-43f6-bf7e-090828edd195">
<Package InstallerVersion="200" Compressed="yes" InstallScope="perMachine" />
<MajorUpgrade DowngradeErrorMessage="A newer version of [ProductName] is already installed." />
<MediaTemplate />
<UI>
<Error Id="25000">Error table Message #1.</Error>
</UI>
<Binary Id="CA" SourceFile="CA\CustomAction.dll"/>
<CustomAction Id="cShowMsg" BinaryKey="CA" DllEntry="showMessages" Execute="immediate"/>
<InstallExecuteSequence>
<Custom Action="cShowMsg" After="InstallInitialize">NOT Installed</Custom>
</InstallExecuteSequence>
<Feature Id="ProductFeature" Title="Part18_02" Level="1">
<ComponentGroupRef Id="ProductComponents" />
</Feature>
</Product>
<Fragment>
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="ProgramFilesFolder">
<Directory Id="INSTALLFOLDER" Name="Part18_02" />
</Directory>
</Directory>
</Fragment>
<Fragment>
<ComponentGroup Id="ProductComponents" Directory="INSTALLFOLDER">
<Component Id="cExe" Guid="{EB49E325-3BB1-4998-BD93-0C559B770D10}">
<File Id="fExe" Source="C:\Windows\System32\calc.exe" KeyPath="yes"/>
</Component>
</ComponentGroup>
</Fragment>
</Wix>
C言語側では、下記のようにMsiRecordSetInteger()関数を使ってerror numberをレコードに格納します。
#pragma comment(lib, "msi.lib")
#include <windows.h>
#include <msiquery.h>
extern "C" __declspec(dllexport) UINT showMessages(MSIHANDLE hInstall) {
PMSIHANDLE hRecord = MsiCreateRecord(1);
MsiRecordSetInteger(hRecord, 1, 25000);
MsiProcessMessage(hInstall, INSTALLMESSAGE(INSTALLMESSAGE_ERROR | MB_OK), hRecord);
return ERROR_SUCCESS;
}
これを実行すると、エラーテーブルに格納した文字列がダイアログボックスに表示されるのがわかります。
ダイアログボックスに表示するアイコンの種類を変える
今まで、黄色い三角の「!」マークアイコンを表示していましたが、青い丸の「!」マークアイコンを表示できます。
これを表示するには、MsiProcessMessage()関数の第2引数にINSTALLMESSAGE_WARNING
を与えます。
MsiProcessMessage(hInstall, INSTALLMESSAGE(INSTALLMESSAGE_WARNING | MB_OK), hRecord);
また、アイコンなしにするにはINSTALLMESSAGE_USER
を与えます。
MsiProcessMessage(hInstall, INSTALLMESSAGE(INSTALLMESSAGE_USER | MB_OK), hRecord);
ここまでで紹介したアイコンは、INSTALLMESSAGE_ERROR
、INSTALLMESSAGE_WARNING
、INSTALLMESSAGE_USER
の既定のアイコン表示ですが、MB_ICONERROR
、MB_ICONQUESTION
、MB_ICONWARNING
、MB_ICONINFORMATION
等を指定することで、既定とは異なるアイコンを使うこともできます。例えば、下記のようにしてやれば、
MsiProcessMessage(hInstall, INSTALLMESSAGE(INSTALLMESSAGE_USER | MB_ICONQUESTION | MB_OK), hRecord);
ダイアログボックスのボタンの種類を変える
これまでOKボタン一つのダイアログボックスを表示し、ダイアログボックスを閉じるためにボタンを利用しました。しかし、ここを読めばわかるように、2つ以上のボタンを表示して、ユーザーの意思を確認することができます。種類として使用できるのは、MB_OK
、MB_OKCANCEL
、MB_ABORTRETRYIGNORE
、MB_YESNOCANCEL
、MB_YESNO
、MB_RETRYCANCEL
です。ユーザーが押したボタンの判別は、Win32 APIのMessageBox()関数と同じくReturn valueを使います。以下にMB_OKCANCEL
の使用例を示します。一つ目のMsiProcessMessage()
関数の第2引数に注目してください。
これを実行すると、[OK]ボタンと[Cancel]ボタンを備えたダイアログボックスを表示し、どちらかのボタンを押すと次に押したボタンの内容を示すダイアログボックスを表示します。
#pragma comment(lib, "msi.lib")
#include <windows.h>
#include <msiquery.h>
extern "C" __declspec(dllexport) UINT showMessages(MSIHANDLE hInstall) {
char* msg = NULL;
PMSIHANDLE hRecord = MsiCreateRecord(1);
MsiRecordSetString(hRecord, 0, "Dialog Box Button test.");
int ret = MsiProcessMessage(hInstall, INSTALLMESSAGE(INSTALLMESSAGE_USER | MB_OKCANCEL), hRecord);
switch (ret) {
case IDOK:
msg = "IDOK";
break;
case IDCANCEL:
msg = "IDCANCEL";
break;
case IDABORT:
msg = "IDABORT";
break;
case IDRETRY:
msg = "IDRETRY";
break;
case IDIGNORE:
msg = "IDIGNORE";
break;
case IDYES:
msg = "IDYES";
break;
case IDNO:
msg = "IDNO";
break;
case IDTRYAGAIN:
msg = "IDTRYAGAIN";
break;
case IDCONTINUE:
msg = "IDCONTINUE";
break;
default:
msg = "UNKNOWN";
break;
}
MsiRecordSetString(hRecord, 0, msg);
MsiProcessMessage(hInstall, INSTALLMESSAGE(INSTALLMESSAGE_USER | MB_OK), hRecord);
return ERROR_SUCCESS;
}
以下に指定できるオプションと実際のダイアログボックスの表示、MsiProcessMessage()
関数の戻り値の対応を示します。
デフォルトボタンの変更
ダイアログボックスが表示されたとき、最初にフォーカスを持っているボタンをデフォルトボタンと呼びます。フォーカスを持ったボタンは、SpaceキーやEnterキーを押せばすぐに反応しますので、インストーラーにとっては安全な方向に進むようデフォルトボタンを設定しておくべきです。このために、MB_DEFBUTTON1
、MB_DEFBUTTON2
、MB_DEFBUTTON3
のオプションが用意されています。
Option | 用途 |
---|---|
MB_DEFBUTTON1 | 1番左のボタンをデフォルトボタンに設定する |
MB_DEFBUTTON2 | 左から2番目のボタンをデフォルトボタンに設定する |
MB_DEFBUTTON3 | 左から3番目のボタンをデフォルトボタンに設定する |
例えば下記のように指定したとします。
MsiProcessMessage(hInstall, INSTALLMESSAGE(INSTALLMESSAGE_USER | MB_YESNO | MB_DEFBUTTON2), hRecord);
そうすると、左から2番目のボタンであるNOボタンにフォーカスが当たった状態でダイアログボックスが開きます。