EternalWindows
MSI インストール編 / アドバタイズインストール

製品をアドバイタイズインストールするには、 前節で触れたようにMsiInstallProductのコマンドライン文字列を利用することになります。

MsiInstallProduct(TEXT("sample.msi"), TEXT("ACTION=ADVERTISE"));

ACTIONプロパティにADVERTISEを設定します。 これで、アドバイタイズインストールが行われることになります。 インストールといっても、正確にはアドバイタイズの性質を持ったショートカットが 作成されるだけですから、実際にファイルなどがインストールされることになるのは、 そのショートカットを起動してからということになります。

ショートカットがデスクトップやスタートメニューに作成されるのは、 あくまでMSIファイルの作成時にそのような設計をしたからであり、 アドバイタイズインストール自体に、ショーカットの作成という行為は含まれません。 つまり、ショートカットを含まないMSIファイルに対してアドバイタイズインストールを 行った場合、ユーザーは残りのファイルをインストールする手立てがなくなることになります。 ただ、実際にはアドバイタイズインストールの時点でMSIのレジストリには製品の情報が書き込まれることになっているため、 その製品コードに着目することにより、製品を完全にインストールすることができるようになります。 既存製品に対して特定の調整を行いたい場合は、MsiConfigureProductを呼び出します。

UINT MsiConfigureProduct(
  LPCTSTR szProduct,
  int iInstallLevel,
  INSTALLSTATE eInstallState
);

szProductは、調整を行いたい製品の製品コードを指定します。 iInstallLevelは、無視される引数のようですが、INSTALLLEVEL_DEFAULTを指定しておきます。 eInstallStateは、製品をどのような状態に調整するのかを示す定数を指定します。 INSTALLSTATE_LOCALを指定すると、製品はローカルに完全にインストールされます。 また、INSTALLSTATE_ABSENTを指定すると、製品をアンインストールすることができます。 アンインストールは、まだローカルにインストールされていない アドバタイズされた製品に対しても有効です。

MsiConfigureProductでアドバタイズされた製品をインストールするような場合、 それが既にローカルに完全にインストールされている場合はこの処理は不要となりますから、 現在の製品の状態というものを取得できると便利だといえます。 次に示すMsiQueryProductStateは、現在の製品の状態を返します。

INSTALLSTATE MsiQueryProductState(
  LPCTSTR szProduct
);

szProductは、現在の状態を取得したい製品の製品コードを指定します。 戻り値は、製品の状態を表す定数となりますが、この型は先のMsiConfigureProductでも 使用されたINSTALLSTATE(enum型)となっています。 MsiQueryProductStateにおける列挙値の意味は、次のようになります。

定数 意味
INSTALLSTATE_ABSENT 製品は現在ログオンしているユーザーと異なるユーザーにインストールされている。
INSTALLSTATE_ADVERTISED 製品はアドバタイズされた状態である。
INSTALLSTATE_DEFAULT 製品は現在ログオンしているユーザーにインストールされれている。
INSTALLSTATE_INVALIDARG 指定された製品コードの形式は不正である。
INSTALLSTATE_UNKNOWN 製品はインストールされていない。

今回のプログラムは、製品をアドバタイズとしてインストールする処理と、 アドバタイズされた製品をローカルに完全にインストール処理が含まれています。

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

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

int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hinstPrev, LPSTR lpszCmdLine, int nCmdShow)
{
	TCHAR szProductCode[] = TEXT("");
	UINT  uResult;
	
	uResult = MsiQueryProductState(szProductCode);
	
	MsiSetInternalUI(INSTALLUILEVEL_NONE, NULL);

	if (uResult == INSTALLSTATE_ADVERTISED) {
		if (MessageBox(NULL, TEXT("製品をローカルにインストールしてもよろしいですか。"), TEXT("OK"), MB_YESNO) == IDYES) {
			uResult = MsiConfigureProduct(szProductCode, INSTALLLEVEL_DEFAULT, INSTALLSTATE_LOCAL);
			if (uResult == ERROR_SUCCESS)
				MessageBox(NULL, TEXT("ローカルへのインストールを完了しました。"), TEXT("OK"), MB_OK);
			else
				MessageBox(NULL, TEXT("ローカルへのインストールに失敗しました。"), NULL, MB_ICONWARNING);
		}
	}
	else if (uResult == INSTALLSTATE_UNKNOWN) {
		uResult = MsiInstallProduct(TEXT("sample.msi"), TEXT("ACTION=ADVERTISE"));
		if (uResult == ERROR_SUCCESS)
			MessageBox(NULL, TEXT("アドバイタイズインストールを完了しました。"), TEXT("OK"), MB_OK);
		else
			MessageBox(NULL, TEXT("アドバイタイズインストールに失敗しました。"), NULL, MB_ICONWARNING);
	}
	else if (uResult == INSTALLSTATE_DEFAULT || uResult == INSTALLSTATE_ABSENT)
		MessageBox(NULL, TEXT("製品は既にローカルにインストールされています。"), NULL, MB_ICONWARNING);
	else
		MessageBox(NULL, TEXT("製品コードの形式が不正です。"), NULL, MB_ICONWARNING);

	return 0;
}

このプログラムは、カレントディレクトリにsample.msiがあるものとし、 それに格納されている製品コードをszProductCodeに指定したものと仮定しています。 MSIファイルに書き込まれている製品コードを確認するには、 OrcaでPropertyテーブルを開き、ProductCodeというプロパティ名の値を参照します。 プログラムから参照する場合は、次節で説明するMsiGetProductPropertyを呼び出すことができます。

製品をアドバタイズインストールするのか、もしくはアドバタイズされた製品を ローカルにインストールするのかの判断は、MsiQueryProductStateの戻り値で決定します。 ただし、既に製品がローカルにインストールされている場合は どちらの処理も必要になりませんから、その時はアプリケーションを終了します。

uResult = MsiQueryProductState(szProductCode);

アドバタイズインストールをする時点では、製品情報がレジストリに書き込まれていないため、 不正な製品コードとしてINSTALLSTATE_INVALIDARGが返ることになりますが、 この点については許容することにします。 ではまず、アドバタイズされた製品をローカルにインストールする処理を見てみます。

if (uResult == INSTALLSTATE_ADVERTISED) {
	if (MessageBox(NULL, TEXT("製品をローカルにインストールしてもよろしいですか。"), TEXT("OK"), MB_YESNO) == IDYES) {
		uResult = MsiConfigureProduct(szProductCode, INSTALLLEVEL_DEFAULT, INSTALLSTATE_LOCAL);
		if (uResult == ERROR_SUCCESS)
			MessageBox(NULL, TEXT("ローカルへのインストールを完了しました。"), TEXT("OK"), MB_OK);
		else
			MessageBox(NULL, TEXT("ローカルへのインストールに失敗しました。"), NULL, MB_ICONWARNING);
	}
}

INSTALLSTATE_ADVERTISEDは、製品がアドバタイズインストールされていることを意味しますから、 この条件の時にMsiConfigureProductを呼び出すようにします。 第3引数にINSTALLSTATE_LOCALを指定することで、ローカルにインストールされます。 続いて、製品をアドバタイズインストールする処理を見てみます。

else if (uResult == INSTALLSTATE_UNKNOWN) {
	uResult = MsiInstallProduct(TEXT("sample.msi"), TEXT("ACTION=ADVERTISE"));
	if (uResult == ERROR_SUCCESS)
		MessageBox(NULL, TEXT("アドバイタイズインストールを完了しました。"), TEXT("OK"), MB_OK);
	else
		MessageBox(NULL, TEXT("アドバイタイズインストールに失敗しました。"), NULL, MB_ICONWARNING);
}

カレントディレクトリにsample.msiがあるものと仮定し、 ACTIONプロパティにADVERTISEを指定してアドバタイズインストールを行います。 基本的には、最初にこのプログラムを実行したときにはまずこの処理が行われ、 次に実行したときに先に示したローカルへのインストールの処理が行われることになるでしょう。

アドバタイズインストールされた製品の最も大きな欠点として、 その製品をショートカットで起動したときに次のようなダイアログが表示されて しまうことが挙げられると思われます。

製品がアドバタイズされた時点では、ショートカットがインストールされているのみですから、 アプリケーションを実行するためのファイル群はまだインストールされていません。 このため、アプリケーションの起動に伴って製品をローカルにインストールするというのは、 理論的には問題ありません。 ただし、ユーザーからすれば、ショートカットが既に存在しているということは、 そのアプリケーションが直に利用できるものと解釈しますから、 インストール処理のためにアプリケーションの起動が遅れるのは煩わしいといえます。 今回のプログラムでは、MsiSetInternalUIにINSTALLUILEVEL_NONEを指定することで サイレントインストールを行うよう設定しているため、ダイアログが表示されることはありませんが、 アプリケーション起動が遅れるという問題は、依然として抱えていることになります。 なお、今回のプログラムではMsiConfigureProductに指定する製品コードをハードコードしていますが、 ディスク上にアドバタイズショートカットが作成されている場合に関しては、 そのパスをMsiGetShortcutTargetに指定して製品コードを取得するという方法が成立します。

フィーチャのアドバタイズ

アドバイタイズの機能は、どちらかというと製品よりフィーチャに利用される傾向があります。 構成例としては、まずアプリケーションの起動に必ず必要となるコンポーネントを 1つのフィーチャ(このようなフィーチャはブートストラップと呼ばれます)に関連付け、 それをローカルにインストールするようにしておきます。 そして、残りの直ぐには必要のないコンポーネントはアドバタイズに設定された フィーチャに関連付けておきます。 プログラミングにおいても、製品とフィーチャの取り扱いには似ており、 フィーチャの現在の状態はMsiQueryFeatureStateで確認することができます。 また、アドバタイズされたフィーチャをローカルにインストールする場合は、 MsiConfigureFeatureを呼び出します。



戻る