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

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

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

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

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

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

では、試しにやってみましょう。 
何はともあれ、まず、Windows Installer 用のカスタムアクションを用意します。今回は、カスタムアクションを DLL で
実装します(ビルドの際には、MT オプションで)。
Windows Installer のカスタムアクションで使う DLL は、次のプロトタイプ宣言です。(細かな説明は割愛します)

#include <windows.h>
#include <msi.h>
#include <Msiquery.h>
#pragma comment(lib, "msi.lib")

UINT __stdcall CustomAction(MSIHANDLE hInstall)

今回は、サンプルなので、敢えて、バグ入り!! のカスタムアクションです。
何をやっているわけではなく、単にヒープ破壊するだけの処理です。

UINT __stdcall CallMyCrashSample(MSIHANDLE hInstall)
{
    HANDLE hHeap = HeapCreate(0, 128, 0);
    LPVOID pMem = HeapAlloc(hHeap, 0, 10);

    // 10バイトブロックに12バイト書き込む(オーバーライト)
    memset(pMem, 0xAC, 12);

    // 新たに20バイトのブロックを割り当てる
    LPVOID pMem2 = HeapAlloc(hHeap, 0, 20);

    // 第2ブロックの1バイト分下位のメモリに書き込む(アンダーライト)
    char * pUnder = (char *)( (DWORD)pMem2 – 1);
    *pUnder = ‘P’;

    // 最初のブロックを開放する
    // この呼び出しでOSデバッグヒープコード内でブレイクが発生する
    HeapFree(hHeap, 0, pMem);

    // 第2ブロックを開放する(ここでは問題発生しない)
    HeapFree(hHeap, 0, pMem2);

    // 余分なブロックを開放する(ここでは問題発生しない)
    HeapFree(hHeap, 0, (LPVOID)0x1);

    HeapDestroy(hHeap);

    return ERROR_SUCCESS;
}

今回のカスタムアクション(Custom Action テーブル)は、以下のようにしました。

Action Type Source Target ExtendedType
CallMyCrashTest 513 MyInstallerAction.dll CallMyCrashSample  

シーケンスですが、UI シーケンスの PatchWelcome の後、InstallWelcome の前に配置して、MSI2011.MSI という名前で
ビルドしました。

では、最初にシステム環境変数の設定を行います。

MsiBreak環境変数の設定

先に説明したように、MsiBreak 変数にカスタムアクション名である CallMyCrashTest を設定します。

次にコマンドプロンプトを管理者権限で起動して インストーラー(すなわち、msiexec.exe)をコマンドラインで実行します。

msiexec /i msi2011.msi

MSI2011のコマンドライン実行

すると、この MSI ではすぐに CallMyCrashTest カスタムアクションを実行するようにしていますので

WIのカスタムアクションでBreak

とダイアログが表示されます。これが、今回紹介する Windows Installer のデバッグ支援です。
内容を見ると、 プロセス ID 4496 にデバッガでアタッチしなさいと書いてあるので WinDBG でアタッチしてみます。

WinDBGでカスタムアクションにアタッチ

msiexec.exe にアタッチしたら、OK ボタンでダイアログを閉じます。
WinDBG の g コマンドでカスタムアクションをホストしている msiexec.exe を実行させます。
あとは、いつもの WinDBG と同じ使い方でデバッグすることができます。

WinDBGでカスタムアクションをデバッグ

カスタムアクションをスクリプト系で実装する場合には、十分なテストもできる思いますが、
DLL で実装すると、予期せぬ事態が発生するとも限りません。
普段は使うことはないかもしれませんが、知っておいて損は無いと思います。

コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト / 変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト / 変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト / 変更 )

Google+ フォト

Google+ アカウントを使ってコメントしています。 ログアウト / 変更 )

%s と連携中

%d人のブロガーが「いいね」をつけました。