製品をアップグレードするとは、既にインストール済みの製品を新しい製品に置き換えることです。 原理的に考えればこれは、その既にインストールしてある製品をアンインストールし、 その後に新しい製品をインストールするということですから、 次のようなコードで簡単に実装できるように思えます。
uResult = MsiQueryProductState(szOldProductCode); if (uResult == INSTALLSTATE_DEFAULT) MsiConfigureProduct(szOldProductCode, INSTALLLEVEL_DEFAULT, INSTALLSTATE_ABSENT); MsiSetInternalUI(INSTALLUILEVEL_FULL, NULL); MsiInstallProduct(szNewPackage, NULL);
新しい製品を作成したメーカーは当然、旧バージョンの製品の製品コードを把握しているため、 それを利用してMsiQueryProductStateで製品のインストール状態を確認できます。 このとき、旧バージョンの製品がインストールされていれば、 MsiConfigureProductにINSTALLSTATE_ABSENTを指定してアンインストールを行います。 そして、最終的にはMsiInstallProductで新しい製品をインストールしようとしています。 確かにこの実装は問題のないものですが、 実際にはMSIが提供するアップグレードのメカニズムを利用したほうがはるかに効率的です。 このメカニズムでは、新しい製品のMSIファイルにしかるべき値を設定してさえいれば、 自動で旧バージョンの製品をアンインストールしてくれるため、 単純にMSIファイルを公開するだけでアップグレードを行えます。
アップグレードを行うためには新バージョンのMSIファイルのプロパティの一部が、 旧バージョンのMSIファイルのプロパティのそれと異なっていなければなりません。 具体的には、PackageCode、ProductVersion、ProductCodeのプロパティが異なっている必要があるのですが、 UpgradeCodeプロパティに関しては同一にしておかなければなりません。 このプロパティは、アプリケーションの複数のバージョンを表す共有識別子と働き、 製品コードと同じようにGUID形式で一意の値となります。 アップグレードコードが同一である製品が複数個インストールされている場合、 それらは互いに製品コードやバージョンが違うものの、 アップグレードコードで識別することのできる1つの製品グループということになります。
既存製品からアップグレードコードを確認したい場合、 その製品のMSIファイルのUpgradeCodeプロパティにアクセスしなければなりません。 以前、フィーチャの情報を取得するときに説明したように、 これはMSIのデータベース系の関数を呼び出すということではなく、 MsiOpenProductで取得したハンドルを使うということなります。 Propertyテーブルにアクセスするには、MsiGetProductPropertyを呼び出します。
UINT MsiGetProductProperty( MSIHANDLE hProduct, LPCTSTR szProperty, LPTSTR lpValueBuf, DWORD *pcchValueBuf );
hProductは、MsiOpenProductで取得したハンドルを指定します。 szPropertyは、Propertyテーブルから取得したいプロパティの名前を指定します。 lpValueBufは、プロパティのデータを受け取るバッファのアドレスを指定します。 pcchValueBufは、lpValueBufのサイズを格納した変数のアドレスを指定します。
今回のプログラムは、既存製品の製品コードからアップグレードコードを取得します。 szProductCodeを既存製品の製品コードで初期化しておいてください。
#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(""); TCHAR szUpgradeCode[256]; DWORD dwSize; MSIHANDLE hProduct; MsiSetInternalUI(INSTALLUILEVEL_NONE, NULL); if (MsiOpenProduct(szProductCode, &hProduct) != ERROR_SUCCESS) return 0; dwSize = sizeof(szUpgradeCode); MsiGetProductProperty(hProduct, TEXT("UpgradeCode"), szUpgradeCode, &dwSize); MessageBox(NULL, szUpgradeCode, TEXT("OK"), MB_OK); MsiCloseHandle(hProduct); return 0; }
MsiOpenProductの呼び出しではMSIのダイアログが表示されるため、 MsiSetInternalUIにINSTALLUILEVEL_NONEを指定することでこれを無効にします。 MsiGetProductPropertyではアップグレードコードを取得したいので、 第2引数にUpgradeCodeプロパティを指定します。
関連する製品の列挙 |
新しいバージョンの製品をインストールするとなったとき、 旧バージョンの製品は依然として残しておきたい場合があります。 この様な場合、アップグレードは行わないということになりますが、 敢えてアップグレードコードを同一にしておくのもよいかもしれません。 このようにした場合、その2つの製品はアップグレードコードで識別できる製品グループということになりますから、 MsiEnumRelatedProductsを利用してグループ内の製品を列挙することができます。 Visual Studioの2002と2003は、この例に当てはまります。 for (i = 0;; i++) { uResult = MsiEnumRelatedProducts(szUpgradeCode, 0, i, szRelatedProductCode); if (uResult != ERROR_SUCCESS) break; MessageBox(NULL, szRelatedProductCode, TEXT("OK"), MB_OK); } MsiEnumRelatedProductsの第1引数にアップグレードコードを指定すると、 そのアップグレードコードを持つ製品の製品コードが第4引数に返ることになります。 第2引数は常に0を指定し、第3引数はゼロベースのインデックスを指定します。 MsiEnumRelatedProductsを利用する場合は、_WIN32_WINNTを0x0500と定義します。 |