LoginSignup
0
0

More than 3 years have passed since last update.

Windows Installer手引書 Part.18 カスタムアクションでダイアログボックスを表示する

Last updated at Posted at 2020-03-01

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

カスタムアクションとUI

カスタムアクションでは、
1. ダイアログボックスの表示
2. インストールログへの任意の文字出力
3. インストールの進捗バーやバーの周囲に表示される文字のコントロール
といった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]ボタンが一つの黄色アイコンのエラーダイアログボックスが表示されます。
part18TestMessage.png

Error Tableにメッセージを格納する

MSIファイルのErrorテーブルに、ダイアログボックスで表示する文字列を格納することができます。Errorテーブルの説明によると、カスタムアクション用として使えるerror numberは25000から30000ということになっているので、この範囲の番号を使用します。WiX Toolsetでは、下記のようにUIエレメントの中にErrorエレメントを定義してErrorテーブルを定義します。

Product.wxs
<?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をレコードに格納します。

CustomAction.cpp
#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;
}

これを実行すると、エラーテーブルに格納した文字列がダイアログボックスに表示されるのがわかります。
part18useErrorTable.png

ダイアログボックスに表示するアイコンの種類を変える

今まで、黄色い三角の「!」マークアイコンを表示していましたが、青い丸の「!」マークアイコンを表示できます。
part18warningMessage.png
これを表示するには、MsiProcessMessage()関数の第2引数にINSTALLMESSAGE_WARNINGを与えます。

MsiProcessMessage(hInstall, INSTALLMESSAGE(INSTALLMESSAGE_WARNING | MB_OK), hRecord);

また、アイコンなしにするにはINSTALLMESSAGE_USERを与えます。
part18noIcon.png

MsiProcessMessage(hInstall, INSTALLMESSAGE(INSTALLMESSAGE_USER | MB_OK), hRecord);

ここまでで紹介したアイコンは、INSTALLMESSAGE_ERRORINSTALLMESSAGE_WARNINGINSTALLMESSAGE_USERの既定のアイコン表示ですが、MB_ICONERRORMB_ICONQUESTIONMB_ICONWARNINGMB_ICONINFORMATION等を指定することで、既定とは異なるアイコンを使うこともできます。例えば、下記のようにしてやれば、

MsiProcessMessage(hInstall, INSTALLMESSAGE(INSTALLMESSAGE_USER | MB_ICONQUESTION | MB_OK), hRecord);

「?」マークのアイコンを表示できます。
part18questionIcon.png

ダイアログボックスのボタンの種類を変える

これまでOKボタン一つのダイアログボックスを表示し、ダイアログボックスを閉じるためにボタンを利用しました。しかし、ここを読めばわかるように、2つ以上のボタンを表示して、ユーザーの意思を確認することができます。種類として使用できるのは、MB_OKMB_OKCANCELMB_ABORTRETRYIGNOREMB_YESNOCANCELMB_YESNOMB_RETRYCANCELです。ユーザーが押したボタンの判別は、Win32 APIのMessageBox()関数と同じくReturn valueを使います。以下にMB_OKCANCELの使用例を示します。一つ目のMsiProcessMessage()関数の第2引数に注目してください。

これを実行すると、[OK]ボタンと[Cancel]ボタンを備えたダイアログボックスを表示し、どちらかのボタンを押すと次に押したボタンの内容を示すダイアログボックスを表示します。

CustomAction.cpp
#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()関数の戻り値の対応を示します。

Option ダイアログボックスの表示 MsiProcessMessage()
の戻り値
MB_OK part18MB_OK.png IDOK
MB_OKCANCEL part18okCancel.png IDOK
IDCANCEL
MB_ABORTRETRYIGNORE part18MB_ABORTRETRYIGNORE.png IDCANCEL
IDRETRY
IDIGNORE
MB_YESNOCANCEL part18MB_YESNOCANCEL.png IDYES
IDNO
IDCANCEL
MB_YESNO part18MB_YESNO.png IDYES
IDNO
MB_RETRYCANCEL part18MB_RETRYCANCEL.png IDRETRY
IDCANCEL

デフォルトボタンの変更

ダイアログボックスが表示されたとき、最初にフォーカスを持っているボタンをデフォルトボタンと呼びます。フォーカスを持ったボタンは、SpaceキーやEnterキーを押せばすぐに反応しますので、インストーラーにとっては安全な方向に進むようデフォルトボタンを設定しておくべきです。このために、MB_DEFBUTTON1MB_DEFBUTTON2MB_DEFBUTTON3のオプションが用意されています。

Option 用途
MB_DEFBUTTON1 1番左のボタンをデフォルトボタンに設定する
MB_DEFBUTTON2 左から2番目のボタンをデフォルトボタンに設定する
MB_DEFBUTTON3 左から3番目のボタンをデフォルトボタンに設定する

例えば下記のように指定したとします。

MsiProcessMessage(hInstall, INSTALLMESSAGE(INSTALLMESSAGE_USER | MB_YESNO | MB_DEFBUTTON2), hRecord);

そうすると、左から2番目のボタンであるNOボタンにフォーカスが当たった状態でダイアログボックスが開きます。
Part18defaultButton.png

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


  1. そういえばPascalの構造体はRecordですね! 

  2. Windows InstallerではMSIテーブルの各行もレコードとして扱います。 

  3. リンク先をよく見ると、関数名の末尾にAが付いたものとWが付いたものがあることが分かります。前者はAnsi、後者はWideのことですが、実際に関数を使うときには、AもWも付ける必要はありません。コンパイラが自動的にどちらか適切な方を呼んでくれます。 

  4. そのため、ここで説明したような用途でMsiCloseHandle()関数を使うことはありません。 

0
0
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
0