ということで、ニッチな製品のさらにニッチな部分の記事なんかを書いてみる。
そもそも Burn ってなに?
Burn は、WiXToolset に含まれるいろいろなプログラムの一つで、ブートストラッパーと呼ばれる種類のプログラムになります。
ブートストラッパーなので、作成時には出てきません。そのため、名前は聞いたことあるけどよくわからないなぁ…なんて人もいるかもしれませんね。
WiX では Bootstrapper Project でビルドした際の exe そのもの部分が、Burn になります。すでに出来上がっている exe に、BootstrapperApplication で設定した UI サポートモジュールと、にビルドイメージを埋め込んで作られます(pdb とソースを落とせばステップ実行も可能、ただし F5 実行には対応していない)。
今回はそんな、Burn というあまり陽の当たる機会のないプログラムについてのお話。
プログラムから Burn を実行する場合に意識しておくべきたった一つのこと。
Burn はブートストラッパーですが、プログラムとしてはごく一般的な Windows 用 GUI プログラムであり、exe としてビルドされます。
当然、Windows から見れば、ワードやエクセルと同じ普通のプログラムとして動きます。
ただ、インストーラという特性上、セキュリティリスクへの対応から DLL Injection へのセキュリティ対策が施されています。
Burn の場合、DLL Injection 対応で、自身を専用のフォルダにコピーしてから動かす形になっているのですが、この時のプログラムの構造が「シングルスレッドの Windows GUI アプリだが UI スレッドを持たない」という構造になっており、Burn自身が、コピーした実際のインストーラ本体が終了するまで「WaitForSingleObject」で無限待機するという構造になっています。
その結果どうなるか?というと、Burn なブートストラッパーを起動したプログラム側で、UIが動き出すまで待つつもりで WaitForInputIdle で無限待機すると、UIが待機状態(メッセージループが稼働する)ことがないため、アプリ終了まで帰ってこないという状態になってしまい、待機する側が意図しない待機状態になってしまうという特性があります。
実際のところテストせずにリリースするなんてことはないと思うので、事前に発覚すると思いますが、この状態を把握していないと呼び出し元アプリは、メッセージ処理を受け付けない状態でスリープ状態で待機してしまうことになるため、いざ外部制御しようとしても反応しない(実際デッドロック状態)という状況になってしまいます。
実際のところ Burn のプロセスってどうなってるの?
そんな待機方法をしてるんだ?となると気になるのが一体いくつのプロセスが動くの?という素朴な疑問だと思います。
Burn では、まず最初の起動プロセス(1つ目)が、自身を専用フォルダにコピーして、そこでUIプロセスとして起動してねとパラメータを渡して起動(2つ目)し、自身は待機状態なります。
2つ目のUIプロセスは、実際に画面表示したり(非表示設定の場合は当然何も画面表示しません)しつつ、実際のインストールコマンドを実行します。
インストールコマンドが実行されると、インストーラ実行プロセス(3つ目)を必要に応じて昇格を伴いつつ実行します。
3つ目の実行プロセスは、Chainにあるリストに沿って、インストーラを粛々と実行していくという処理になっています(この先の実行プロセスは、Burn とは別のものが動いていく)。
ちなみに、昇格するかどうかは、ExePackage の場合は、PerMachine のフラグを参照、MsiPackage の場合は ForcePerMachine フラグか、msiそのものの設定を見て自動判定します。
この流れを把握しておくだけでも、いざ問題が出たというときの調査などはだいぶ変わると思います。Burnの内部をデバッグする必要がぁ~なんてのはほとんどないと思いますが、ある程度把握できてれば、カスタムBAを作るんだ!なんてなった時に助けになるかな?と思います。