Windows Installer で Session.Message メソッドを使う

MSI を作成している最中、デバッグ用途にカスタムアクション内に VBScript で MsgBox を入れ込むということを
よくやると思います。デバッグ用にはこれで十分なのですが、リリース版で何等かのメッセージボックスを表示したいと
いう場合、同様にカスタムアクションで VBscript の MsgBox で表示すると次の現象が発生します。

  • VBScript で作成したメッセージボックスがモードレスになり、インストールウィザードの後ろに隠れてしまう
    MsgBox に 4096(vbSystemModal) を加えても変わらない
  • メッセージボックスを表示中でもインストールウィザードを操作することができてしまう

リリース版で、この動きになってしまうと、ユーザーに悪い印象を与えかねません。
この原因ですが、以前、このブログでも紹介したように、カスタムアクション用の msiexec.exe が
別に起動されてしまうことによる影響です。

    WindowsInstallerアーキテクチャ図

ところが、Custom Action Types をよく見ると、Type 19 というものがあります。説明には、

Displays a specified error message and returns failure, terminating the installation.

と記述があり、何やら、メッセージを表示することができるようです。

この投稿の続きを読む

MSI の各上限値

自分用のメモです。

  • コンポーネント数の最大数: 65535
  • 機能に関連付けできるコンポーネント数の最大数: 1600
  • 機能階層の深さ: 16階層
  • 最大ファイル数(Fileテーブルに格納できる上限): 32767
  • 1 ファイルの最大サイズ: 2GB
  • MSI データベース内部で使用される CAB ファイルの上限サイズ: 2GB

インストーラーのエクスペリエンス

突然ですが、インストーラーに UX(エクスペリエンス)は必要か?と問わせて、どう答えますか?
色々な考え/意見があると思いますが、ひとつの答えとして、Microsoft が

提供しています。
Windows 関連製品を開発する際には、ぜひとも、読んでおくべき内容だと思います。

Windows Installer のカスタムアクションをデバッグする

結構、便利なので紹介します。元ネタは

です。
今回は、MSI でカスタムアクションを実装することができる人向けということを前提としています。
ちょっとディープな内容になってしまうかもしれません。

MSI を作っているときに、標準アクションではどうしてもできない処理があり、カスタムアクションを用意することが
あります。もちろん、カスタムアクションを作る際にきちんとデバッグしておくわけですが、そのデバッグを通り抜けて
しまったバグをどうやってデバッグ/調査しようかと悩むことがあるかもしれません。
Windows Installer は、非常によくできていて、カスタムアクションのデバッグを支援する仕組みを持っています。
その方法ですが、次のような流れになります。

  1. システム環境変数に MsiBreak 変数を用意して、カスタムアクション名を値に設定する。
  2. 管理者権限でコマンドプロンプトを開き、msiexec コマンドでインストーラーを実行する。
  3. WinDBG でカスタムアクション用の msiexec.exe にプロセスアタッチしてカスタムアクションをデバッグする。

MsiBreak システム変数で設定してカスタムアクションが実行されるときに、Windows Installer が一度、Break してくれると
いう仕組みを持っているわけです。
以前、カスタムアクションは、カスタムアクション用の msiexec.exe が起動されて、その中で実行されると紹介しましたが
その msiexec.exe にデバッガでアタッチしてデバッグしようというわけです。

この投稿の続きを読む

Windows Installer でカスタムアクションを使用する際の注意

2010 年最初のネタは、Windows Installer です。
インストーラーは極力シンプルな構造にするのは鉄則ですが、どうしても内部処理を行うために
カスタムアクションを使用しなければならないという場合、重要な注意事項があります。

  • 前提条件
    Windows Installer のカスタムアクションで、EXE や DLL を使用する
  • OS
    Windows Vista 以降の OS すべて(クライアント系、サーバー系共に)

この条件に当てはまる場合には、使用する EXE や DLL は必ず、ランタイムライブラリをスタティックリンク(/MT オプション)
したものを使うようにしてください。

この理由ですが、カスタムアクションの EXE や DLL が、再頒布インストールするランタイムを使用してしまう場合、
Windows Installer のインストール処理はトランザクションになっているために、カスタムアクションが動作する際には
再頒布インストールする C ランタイムが使える状態(再頒布インストールするランタイムのマージモジュールが
コミットされていない)になっていない場合があるからです。

エラー 1935。  An error occurred during the installation of assembly component {9DA4DC8A-9731-3F0E-8BD5-FC17CA6848AD}. HRESULT: 0x800736FD. assembly interface: IAssemblyCacheItem, function: Commit, assembly name: policy.9.0.Microsoft.VC90.CRT,publicKeyToken="1fc8b3b9a1e18e3b",type="win32-policy",version="9.0.30729.4148",processorArchitecture="x86"
MSI (s) (54:54) [11:22:00:570]: 製品 : 俺様サンプル — エラー 1935。  An error occurred during the installation of assembly component {9DA4DC8A-9731-3F0E-8BD5-FC17CA6848AD}. HRESULT: 0x800736FD. assembly interface: IAssemblyCacheItem, function: Commit, assembly name: policy.9.0.Microsoft.VC90.CRT,publicKeyToken="1fc8b3b9a1e18e3b",type="win32-policy",version="9.0.30729.4148",processorArchitecture="x86"

という具合に、1935 エラーが発生する可能性があります。

上記のブログは、VC++ 8.0 ですが、VC++ 9.0 でも同様です。
カスタムアクションを使用するのであれば、EXE や DLL を使うのではなく、VB スクリプトや J スクリプトを使う方がよいです。

Windows Installer スキーマバージョン

Windows 7 & Windows Server 2008 R2 にバンドルされている Windows Installer は バージョン 5 になります。
この辺で、スキーマ番号の一覧をアップデートしておこうと思います。

Windows Installer のバージョン スキーマ番号
5.0 500
4.5 405
4.0 400
3.1 301
3.0 300
2.0 200
1.1 110

Windows Installer のバージョンの確認方法ですが、コマンドプロンプトを開いて

msiexec

と入力してください。すると

のような画面が表示されます。(これは、Windows 7 RTM で実行した結果)
また、MSI ファイルのスキーマ番号を確認したい場合には、Orca で確認することができます。

Windows Installer 5.0 では、MSIINSTALLPERUSER プロパティが追加されたので、ALLUSERS プロパティとの
組み合わせを少し意識しておく必要があります。(スキーマ番号に 500 を使う場合ですが)

Windows Installer のカスタムアクションを管理者権限で動かしたい

8月になりました。毎日、暑いですね~。この暑さでジョギングもすっかり、ご無沙汰です。
先日、散歩していたところ、あることに気が付きました。ガソリン高騰の影響だと思うのですが、道を走っている車が
気のせいか少なくなったように思います。ガソリン高騰、一体、どうなるんでしょう。

さて、話題を変えて、今回は Windows Installer に関係するネタです。

少し前に Windows Installer 4.5 がリリースされましたが、
Windows Vista / 2008 では、Windows Installer 4.0 が標準バンドルされており、
MSI のスキーマは 400 にすることが推奨されています。(最低でも、301)インストーラーはユーザーに対して一番最初に見せるプログラムであり、失敗することは許されません。
インストーラーでエラーになったら、それだけで使うのが嫌になってしまいますよね。

  • ファイルコピー
  • レジストリ登録
  • メニュー登録

できれば、これくらいで抑えておきたいものです。
MSI 作成に限らず、インスールパッケージを作成する際、インストールパッケージ自体にあまり多くの機能を
入れ込まず、可能な限り、外出しのプログラム(セットアップやライブラリ化)にしておくのがよい実装です。

ただ、どうしても、MSI パッケージ内に独自の処理を入れなければならない場合もあります。
この場合は、カスタムアクション を用意することになります。
カスタムアクションとは、ユーザー定義のインストール動作です。
これは、標準動作では足りない部分を補うことができるように用意されているものです。
例えば、外部の EXE や DLL を実行したり、VBScript、JScript などのスクリプトを実行することもできます。

では、Windows Vista / 2008 で管理者権限のカスタムアクションを実装する際のポイントを解説します。
まず、カスタムアクションを使うために必ず知っておかなければならないことがあります。

  1. カスタムアクションの種類
  2. カスタムアクションがどのようなアーキテクチャで動作しているのか
  3. カスタムアクションの実装方法

では、まずは (1) から解説します。Windows Installer で用意されているカスタムアクションは、以下のものがあります。

この一覧は非常に重要なもので、カスタムアクションを実装する際には必ず参照すると思います。
この Custom Action Types 一覧では、どのような Custom Action Type があるのかをじっくりと読んでおいてください。
例えば、Custom Action Type 1 はバイナリテーブルに格納した DLL をカスタムアクションで
使用することです。同様に Custom Action Type 2 は、DLL ではなく EXE を使用することです。

余談ですが、最新の Windows Installer 4.5 ではカスタムアクションに .NET で作成したマネージコードの
DLL や EXE を実行することもできます。

では、カスタムアクションはどのようなアーキテクチャで動作しているのでしょう。
(2) を解説していきます。

Windows Installer は、クライアント/サーバー型のサービスプログラムであると、以前 (Windows Installer その1) に
記載しました。 Windows Installer がカスタムアクションを実行する際、カスタムアクション用にクライアントプロセスの
msiexec.exe をもう一つ起動します。

つまり、

————————————————————————–
msiexec.exe (サーバープロセス:Local System)

  ↓ プロセス起動

  msiexec.exe (クライアントプロセス:管理者権限 or 各ユーザー権限)

   →  カスタムアクション用の msiexec.exe (クライアントプロセス:各ユーザー権限)
————————————————————————–

という構造をとります。
例えば、カスタムアクションには DLL を実行する機能があります。その DLL をホストする EXE が必要になりますよね。
そこで、クライアントプロセス用の msiexec.exe を使ってもいいのですが、それでは、クライアントプロセス用の
msiexec.exe が止まってしまいます(メッセージループが止まる)。スレッドにしてもいいのですが、それでは複雑になり
エラー要因が増えてしまいます。何よりも DLL に問題が起った際には、クライアントプロセス用の msiexec.exe 自体が
落ちてしまいます。これでは信頼性を保つことはできません。

カスタムアクション用の msiexec.exe が動作しているかどうかは、タスクマネージャーでも確認できます。
ただ、msiexec.exe が複数起動しているので、識別するのは困難ですけど。

カスタムアクションの種類とアーキテクチャを理解したら、いよいよ (3) の実装です。
これを説明するために、以下をサンプルシナリオにします。ただし、簡単に説明するためにエラー時のロールバックは無視します。

(シナリオ)

  • Windows Vista 用の MSI パッケージを作成する。Vista 専用であるため、スキーマは 400 を指定する。
  • 標準アクションで不足する部分をカスタムアクションにて実装する。カスタムアクションは
    DLL(MyExpt.dll) を呼び出すことで動作させる。使用する DLL の関数名は MyDataApi() とする。
    なお、MyDataApi() の実行は管理者権限でなければならない。
  • スクリプト内実行は、即時実行とする。
  • 実行スケジュールは、1 プロセスにつき、1 回実行とする。
  • 戻り値の処理は、終了コードを確認して同期処理を行う。
  • カスタムアクションのシーケンス名は MYDATA と定義する。

このシナリオでは、カスタムアクションを DLL で実装するとありますので
Custom Action Types 一覧から、Custom Action Type 1 になることがわかります。
そして、Type が 1 であることを覚えておいてください。

つぎに、Custom Action Reference から Custom Action In-Script Execution Options を参照します。
スクリプト内実行は、即時実行とするので、表から、0 であることがわかります。
そして、DLL を管理者権限で実行する必要があるので、msidbCustomActionTypeNoImpersonate も指定することになります。

先ほど、アーキテクチャを説明しましたが、Windows Vista では以下のようになります。(Windows Server 2008 も同じ)

————————————————————————–
msiexec.exe (サーバープロセス:Trusted Installer)

  ↓ プロセス起動

  msiexec.exe (クライアントプロセス:管理者権限(PA) or 各ユーザー権限(LUA))

   → カスタムアクション用の msiexec.exe (クライアントプロセス:各ユーザー権限(LUA))

PA:Privileged Administrator
LUA:Least-privilege User Access
————————————————————————–

上記を見ると、カスタムアクション用の msiexec.exe が LUA で動作することがわかります。
つまり、MSI に管理者権限が必要だという設定を行い、msiexec.exe の起動時に UAC により管理者権限に
昇格しても、カスタムアクション用の msiexec.exe は LUA(中 IL)で動作してしまうのです。
そのため、PA にならず、クライアントプロセス用の msiexec.exe と同じ権限で動作するように
msidbCustomActionTypeNoImpersonate を指定するわけです。

続いて、実行スケジュールですが、1 プロセスにつき、1 回実行としたので、
msidbCustomActionTypeOncePerProcess を指定します。最後に、戻り値の処理は、終了コードを確認して同期処理を行うとしたので、0 を指定します。以上より、今回のシナリオでは、Custom Action In-Script Execution Options
 : 即時実行(0)
 : msidbCustomActionTypeNoImpersonate(0x00000800)Custom Action Execution Scheduling Options
 : msidbCustomActionTypeOncePerProcess(0x00000200)Custom Action Return Processing Options
 : 同期実行(0)が必要なオプションになります。これらの数値を加えて、0 + 0x00000800 + 0x00000200 + 0 = A00
10進数に変換すると、2560 になります。最後に、Custom Action Type 1 の Type は 「1」 であるため、2560 に 1 を加えて 2561 になります。
あとは、この 2561 を CustomAction Table で該当する カスタムアクション の Type に指定すれば完了です。

つまり、CustomAction Table には

 Action  MYDATA
 Type   2561
 Source  MyExpt.dll
 Target  MyDataApi

という定義を行うことで、実装完了です。
今回の例を応用して、管理者権限が必要なカスタムアクションを実装してください。ただ、初めにもお話しましたが、可能な限りカスタムアクションは使わない方がいいものです。
複雑なものを作りこめば、その分、エラーは発生しやすくなります。
プログラムは、 Simple is best !! です。それでは、今回はこの辺で。

Windows Installer その2

今回は、MSI のバージョンアップについてのネタです。

Windows Installer MSI パッケージには、3 タイプのバージョンアップが用意されています。
一度、リリースした製品に対して、バージョンアップ用のインストールパッケージを
作成する際には、この 3 つの中から、どれが適切なのかを選択して、作成する必要があります。

  • メジャーアップグレード
    メジャーアップグレード処理では、旧バージョンをサイレントアンインストールしてから新バージョンを
    インストールします(REMOVE="ALL"でサイレント実行する)。
    もし、何かの情報を引き継ぎたいということであれば、自前で何らかの処理を加える必要もあります。
    どんなときにメジャーアップグレードを使うかというと、
    ・製品のバージョンアップ(v1.0 → v2.0)
    ・既存の製品からファイルが増えた(新しいコンポーネントが増えた)
    ・既存の製品からファイルが減った
    ・MSI の構成(インストールする機能の構成)が変わった
    こんな場合になるかと思います。
  • マイナーアップグレード
    マイナーアップグレードの目的は、製品自体のマイナーアップグレードです。
    製品のマイナーバージョン番号以降を更新して、ファイルを更新します。
    マイナーアップグレードでは、ファイルの増減はできません。必ず、旧バージョンと同じファイル数にしておく
    必要があります。
  • スモールアップデート
    スモールアップグレードは、特定のファイルのみを変更するアップグレードタイプです。
    製品自体のバージョンは一切変更しません。
    スモールアップグレードは、マイナーアップグレードと同様にファイルの増減はできません。
    必ず、旧バージョンと同じファイル数にしておく必要があります。

    特定のファイルのみということでパッチに近い考え方なのですが、「パッチは差分のみを提供するもの」
    「アップグレードはフルパッケージを提供するもの」という違いがあります。
    すべてのアップグレードタイプは、旧バージョンがインストール済みでなければ、そのバージョンのみを
    インストールするフルパッケージになります。
    パッチは、差分のみの提供になりますので、必ず、対象製品がインストール済みでなければなりません。
    (パッチについては別の機会で書きたいと思います)

そして、バージョンアップの話を進める前に、Windows Installer 内部では
どのように製品を区別しているのかを理解する必要があります。

Windows Installer では、3 つの GUID と製品バージョン を使用しています。

  • パッケージコード
    MSI ファイルを一意に識別するためのコードです。
  • 製品コード
    製品(プロダクト)を一意に識別するためのコードです。
    例えば、製品A v1.5 を製品A v2.0 をバージョンアップしたいという場合には、
    同じ製品コードのままにしておきます。もし、製品A v1.5 を製品A v2.0 を共存させたいと
    いった場合には、異なる製品コードにします。
  • アップグレードコード
    アップグレードコードは、製品ファミリーを一意に識別するために使用します。
    このコードは、旧バージョンのアンインストールの際に判定する材料です。
  • 製品バージョン
    Windows Installer では、aa.bb.cccc という形式で数値のみで識別します。
    aa は、メジャーバージョン番号を、bb は、マイナーバージョン番号を、cccc は、ビルド番号を示します。
    aa と bb は、255 以下でなければなりません。cccc は、65535 以下になります。

Windows Installer では、これらの識別子を用いて、バージョンアップの判定を行います。
では、これらをどのように使ってバージョンアップ用の MSI を作るのかというと…

  パッケージコード 製品バージョン 製品コード アップグレードコード
メジャーアップグレード 変更する 変更する 変更する そのまま
マイナーアップグレード 変更する 変更する そのまま そのまま
スモールアップグレード 変更する そのまま そのまま そのまま

のように、設定します。

ちなみに、マイナーアップグレードとスモールアップグレードの MSI ファイルは、そのまま実行する
(ダブルクリック)とエラーになります。
Windows Installer は、インストール実行時に同一の製品コードがあるかどうかを検索して、もし、
同一の製品コードが見つかった場合には、すでにインストールキャッシュされている MSI を使ってしまいます。
このため、マイナーアップグレードとスモールアップグレードを作成する場合には、セットアップランチャー
(setup.exe)を用意するか、以下のコマンドで実行する必要がありますので、注意しましょう。

msiexec /i product.msi REINSTALLMODE=voums REINSTALL=ALL

さて、アップグレードの際には、ファイルを更新するわけですから、ファイルのバージョンアップルールを
簡単に書いておきます。(詳細は、http://msdn.microsoft.com/en-us/library/aa368599(VS.85).aspx

Windows Installer では、バージョン、日付、言語の 3 つを主に判定材料にしています。

File Versioning Rule

  1. 比較対象ファイルの両方にバージョンがある
    ・バージョンの高い方を保持する
     バージョンの低いファイルよりもバージョンの高いファイルが優先される。
    ・バージョンが同じ場合
     インストール済みファイルの言語とインストールするファイルの言語を調べ
     異なる場合には、インストールするファイルを優先する。
  2. 比較対象ファイルのバージョンがない
    Windows Installer 2.0 以降はファイルハッシュ値も判定材料にしています。
    ・既存ファイルの作成日付と更新日付が異なる
     既存ファイルを優先(残し)し、ファイルのインストールは行わない。(ユーザーが更新したと見なします)
    ・既存ファイルの作成日付と更新日付が同じ
     ファイルハッシュが同じ場合には、既存ファイルを残し、ファイルハッシュが異なる場合には
     インストールするファイルを優先する。
  3. 比較対象ファイルの片方のみにバージョンがある
    ・バージョンがある方を保持する
     リソースにバージョンがあるファイルの方がバージョンの無いファイルよりも優先される。

非常に簡単に書きましたが、実際には、いろいろなことをやっています。

この辺を理解した上で、どのような動きになるのかをきちんとドキュメントに残しておくと保守の際に役立つと思います。


今回は、MSI のバージョンアップについての概要を紹介しました。
製品をリリースした以上は、次の何からのリリースがあると思います。
きちんと内部動作を理解した上で、作成するバージョンアップインストーラーをどのタイプにするかを
よーく考える必要があります。
ただ、迷ったら、メジャーアップグレードにしておくのが無難です。

Windows Installer その1

しばらくぶりの更新です。
MCP 試験 (MCPD EA へのアップグレード) 受けたり、年度末だったりで、時間が無かったというのを言い訳に…。

さて、今回は、あまりドキュメントが無いであろう Windows Installer ネタです。

まず、MSDN の Windows Installer ドキュメントは、ここ (http://msdn2.microsoft.com/en-us/library/aa372866.aspx) です。
初回ということで、すこし基本的なことを書いておきます。

Windows Installer は、クライアント/サーバー型のサービスプログラムということをご存じでしょうか。
サーバーの実体は、Local System アカウントとして実行されているサービスプログラムで、msiexec.exe という
プロセスになります。
Windows Installer 対応ファイルを実行する際に、サーバープロセスの msiexec.exe から、クライアントプロセス用に
msiexec.exe を起動して、対応ファイルを処理 (つまり、インストール) します。

代表的な対応ファイルには、以下のものがあります。

拡張子名 名称 内容
msi Windows Installer データベース ファイル インストーラーデータベースの本体です。MSI ファイルは、COM 構造化ストレージになっています。
mst Windows Installer トランスフォームファイル カスタマイズインストール実行を行うために、使用します。
msp Windows Installer パッチインストール ファイル

2 つのバージョンの MSI ファイルの差分のみを格納した MSI データベースファイルです。主に、アップデートの際に使用します。

msm Windows Installer マージモジュール ファイル 再利用/再頒布可能モジュールをパッケージングした MSI データベースファイルです。

さて、上記の中で MSI ファイルは、COM 構造化ストレージだということを書きました。
COM 構造化ストレージということは、概要情報ストリーム を必ず、持っていることになりますね。
MSI ファイルの"概要情報ストリーム"には、作成者、プラットフォーム、言語、製品コード、スキーマというような
情報が格納されています。

かなり、前置きが長くなりましたが、今回は、このスキーマについて書いておこうと思います。

…と、その前に、便利なツールの紹介をしておきます。
Microsoft のツールなのですが、Windows Installer ファイルの参照/変更ができる Orca というユティリティが
ありますので、ぜひ、ダウンロードしておくことをオススメします。

まずは、何か適当な MSI ファイルを Orca で開いてみてください。

orcaでMSIを開く

ここの スキーマ という部分に注目です。
Orca の画面上では、スキーマ と表示されていますが、MSI ファイルの中では、"Page Count Summary" プロパティと呼びます。
スキーマ番号は、その MSI データベースが動作することができる最低限の Windows Installer バージョンを指定します。
では、Windows Installer のスキーマバージョンがどうなっているかというと…

Windows Installer のバージョン スキーマ番号
4.0 400
3.1 301
3.0 300
2.0 200
1.1 110

上記画面では、スキーマは 400 になっていますので、Windows Installer 4.0 が最低限となり
Windows Vista 以降でないとインストールすることができないということがわかります。
つまり、Windows XP ( Windows Installer 3.1 ) では、起動することができません。
Windows Installer 3.1 のスキーマ番号が、310 ではなく、301 という点に注意が必要です。
なお、MSI ファイルのスキーマ情報と、インストール環境の Windows Installer のバージョンが不一致であると
このような画面が表示され、インストールは中止されてしまいます。

MSIスキーマ未対応

なお、Windows Vista/Windows Server 2008 に対応する MSI パッケージを作成する際には、
Windows Installer のバージョンは、3.1 を最低にしておくことをオススメします。
3.1 よりも低いスキーマバージョンを指定した場合には、インストールに失敗、もしくは、期待した動作にならないことが
ありますので、注意してくださいね。

MSI パッケージを作成する場合には、きちんと設計した スキーマバージョン にしましょう。

今回はこの辺にて。
少しの間、Windows Installer ネタが続くかもしれません。。。


そういえば、少し前になりますが、無限プチプチを買ってしまいました。

無限プチプチ