EternalWindows
MSI カスタムアクション編 / ログファイル

カスタムアクションの作成に入る前に、ログファイルの利用ついて説明します。 ログファイルを有効にしておけば、インストール中に実行されたアクションなどが書き込まれるため、 どこまでインストールが進んだのかを確認できますし、 シーケンスやアクションについての理解も深まります。 次に示すMsiEnableLogは、インストールの状況を書き込むためのファイルを作成します。

UINT MsiEnableLog(
  DWORD dwLogMode,
  LPCTSTR szLogFile,
  DWORD dwLogAttributes
);

dwLogModeは、インストール中のどのような情報についてログをとるかを表す定数を指定します。 szLogFileは、作成するログファイルの名前を指定します。 dwLogAttributesは、既存のログファイルが存在するときにどうするかを表す定数を指定します。 INSTALLLOGATTRIBUTES_APPENDは既存のデータに追加しますが、 INSTALLLOGATTRIBUTES_FLUSHEACHLINEはデータを上書きします。

dwLogModeに指定できる定数の一部を次に示します。 これらは、複数組み合わせて指定することもできます。

ログモード 説明
INSTALLLOGMODE_ACTIONSTART 実行したアクション名と動作概要を示す文字列を記述する。 アクション名が記述されなければ、詳細を記述してもどのアクションのものか分からないため、 この定数はなるべく含めるようにする。
INSTALLLOGMODE_ACTIONDATA アクションの具体的な内容を記述する。たとえば、実際に作成したファイル名など。
INSTALLLOGMODE_INFO エラーの発生したアクションについて記述する。
INSTALLLOGMODE_PROPERTYDUMP 全てのプロパティの値を記述する。
INSTALLLOGMODE_VERBOSE 非常に多くの情報を記述する。この値に関しては、単一の指定で十分だと思われる。

今回のプログラムは、MsiEnableLogを呼び出してから、 MsiInstallProductで製品をインストールします。 これにより、ログファイルにインスール状況が書き込まれていきます。

#include <windows.h>
#include <msi.h>

#pragma comment (lib, "msi.lib")

int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hinstPrev, LPSTR lpszCmdLine, int nCmdShow)
{
	UINT uResult;

	uResult = MsiEnableLog(INSTALLLOGMODE_ACTIONSTART, TEXT("sample.txt"), INSTALLLOGATTRIBUTES_FLUSHEACHLINE);
	if (uResult != ERROR_SUCCESS)
		MessageBox(NULL, TEXT("ログファイルの作成に失敗しました。"), NULL, MB_ICONWARNING);

	MsiSetInternalUI(INSTALLUILEVEL_FULL, NULL);
	
	uResult = MsiInstallProduct(TEXT("sample.msi"), NULL);
	if (uResult == ERROR_SUCCESS)
		MessageBox(NULL, TEXT("インストールを完了しました。"), TEXT("OK"), MB_OK);
	else
		MessageBox(NULL, TEXT("インストールに失敗しました。"), NULL, MB_ICONWARNING);

	return 0;
}

プログラムを実行すると、sample.txtがカレントディレクトリに作成されます。 MsiSetInternalUIでUIレベルをINSTALLUILEVEL_FULLにしているのは、 インストールでMSIファイルに含まれるダイアログを表示したいという意図もありますが、 そもそも、ログファイルにはINSTALLUILEVEL_FULLによるインストールでしか意味のあるデータは書き込まれません。 よって、UIレベルをINSTALLUILEVEL_FULLにするのは必須であるといえます。

MsiEnableLogでは、ログモードをINSTALLLOGMODE_ACTIONSTARTとしていますから、 そこにはシーケンステーブルに書かれている個々のアクションが記録されているはずです。 アクション名のみを抜粋して、次に一例を示します。

INSTALL
AppSearch
FindRelatedProducts
LaunchConditions
・
・
・
ExecuteAction
INSTALL
AppSearch
・
・
・

まず、INSTALLという文字列ですが、これはシーケンステーブルに記述されない特殊なアクションです。 意味的には、シーケンステーブルの走査の開始を示す指標ということになるでしょう。 シーケンステーブルは、UIの表示を行うためのInstallUISequenceから参照されるため、 最初のINSTALLという文字列は、InstallUISequenceの開始を表します。 各種アクションを実行していき、ExecuteActionまで達すると、 次はInstallExecuteSequenceテーブルのアクションを実行しなければならないため、 テーブルの走査を示すINSTALLという文字列がここにも記述されています。 InstallUISequenceとInstallExecuteSequenceで同じアクションが実行されることについては、 詳しい理由は分かりません。

アクション名を記録したログファイルとシーケンステーブルの内容を見比べた場合、 ログファイルにはいくつかのアクションが記述されていないことがあると思われます。 これは、シーケンステーブルのConditionカラムに記述した条件式が、 インストールの際にFALSEと判定されたからであり、 これによりそのアクションは実行されませんから、ログファイルには記述されなくなります。 Conditionカラムの条件式のフォーマットは複雑ですが、 カスタムアクションを作成するにあたっては覚えておいたほうがよい場合もあります。 たとえば、DLLを利用するカスタムアクションを設計した場合、 DLLのコードを実行するかどうかの判定は関数の先頭に記述することもできますが、 Conditionカラムにその判定内容を記述すればアクション自体が実行されませんから、 DLLのロードを省略したという点では、効率的であるといえるでしょう。


戻る