┏第57号━━━━━━━━━━━━━━━━━━━━━━━━━━┓ ┃         .NETプログラミング研究         ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ──<メニュー>─────────────────────── ■.NET Tips ・デプロイメントプロジェクトのカスタム動作の使い方 ─────────────────────────────── ─────────────────────────────── ■.NET Tips ─────────────────────────────── ●デプロイメントプロジェクトのカスタム動作の使い方 デプロイメントプロジェクトのカスタム動作エディタを使って、作成 するWindows Installerパッケージで使用するカスタムアクションを 指定することができます。カスタムアクションとは、Windows Installerだけの機能では実現が難しい動作をEXE、DLL、スクリプト 等を使って行うものです。 ☆VBScriptによる簡単な例 早速ですが、まずはカスタムアクションがどのようなものか、簡単な 例を見てみましょう。ここでは、VBScriptを使用することにします。 はじめにカスタムアクションで使用するスクリプトを作成します。こ こでは次のようなごく簡単なスクリプトを作成し、"action.vbs"とい う名前で保存します。メッセージボックスを表示し、「こんにちは」 と表示するだけのスクリプトです。 ‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ MsgBox "こんにちは" ‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ まずは、この"action.vbs"をデプロイメントプロジェクトに追加しま す。ソリューションエクスプローラでプロジェクトを右クリックし、 メニューの[追加]-[ファイル]で追加できます。 次に「カスタム動作エディタ」を開き、スクリプトを追加します。カ スタム動作を開くには、ソリューションエクスプローラでプロジェク トを右クリックし、メニューの[表示]-[カスタム動作]を選択します。 カスタム動作エディタには4つのノードがありますが、これらの説明 は後に回し、ここでは「インストール」ノードにカスタムアクション を追加します。「インストール」ノードを右クリックし、メニューの [カスタム動作の追加]で表示される「プロジェクトから項目を選択」 ダイアログで"action.vbs"を選択し、OKをクリックします。 これでカスタムアクションが追加されました。早速ビルドして試して みましょう。 作成したMSIファイルを実行すると、「(アプリケーション名)をイ ンストールしています」というステップの途中で「こんにちは」とい うメッセージボックスが表示されるはずです。 ☆アプリケーションフォルダにスクリプトを配置しない 上記の例では、スクリプトファイルがアプリケーションフォルダに配 置されます。スクリプトファイルを配置しないようにするには、ソリ ューションエクスプローラで"action.vbs"を選択し、Excludeプロパ ティをTrueにします。 ExcludeプロパティがFalseの場合、MSIファイルのCustomActionテー ブルに登録されるカスタムアクションのTypeは22となります。 Custom Action Type 22 http://msdn.microsoft.com/library/en-us/msi/setup/custom_action_type_22.asp これに対してExcludeプロパティをTrueにすると、VBSファイルは Binaryテーブルに埋め込まれ、Custom Action Typeは6となります。 Custom Action Type 6 http://msdn.microsoft.com/library/en-us/msi/setup/custom_action_type_6.asp ☆カスタム動作の4つのノードとその詳細 カスタム動作エディタには4つのノード「インストール」、「確定」、 「ロールバック」、「アンインストール」があります。それぞれが何 を意味しているかについては、ヘルプの「カスタム動作エディタ」で 説明されています。 [URL]カスタム動作エディタ http://www.microsoft.com/japan/msdn/library/ja/vsintro7/html/vxurfCustomActionsEditor.asp この説明を以下に引用させていただきます。 ・インストール このノードの下のカスタム動作は、インストールのインストール フ ェーズの最後 (すべてのファイルがインストールされた後) に実行さ れます。 ・確定 このノードの下のカスタム動作は、インストールの確定フェーズの最 後に実行されます。確定フェーズは、インストール フェーズが問題 なく終了すると発生します。 ・ロールバック このノードの下のカスタム動作は、インストールのロールバック フ ェーズの最後に実行されます。ロールバック フェーズは、インストー ル中にエラーが発生すると発生します。 ・アンインストール このノードの下のカスタム動作は、インストールのアンインストール フェーズの最後に実行されます。アンインストール フェーズは、ア プリケーションがアンインストールされると発生します。 さらに詳しく調べるには、MSIファイルの中身をOrcaで覗いてみる必 要があるでしょう。という訳で、実際に調べてみました。 「インストール」、「確定」、「ロールバック」に追加されたカスタ ムアクションは、すべてInstallExecuteSequenceテーブルに Conditionが「$(スクリプトファイルのコンポーネントのID)>2」(注)、 Sequenceが5999(複数登録すると、5998、5997...となる)として登 録されます。「$(スクリプトファイルのコンポーネントのID)>2」 の"2"は"msiInstallStateAbsent"を表しており、2より大きいという ことは、このコンポーネントがインストールされたということを示し ています。また、Sequenceが5999というのは、StartServicesの後、 RegisterUserの前ということになります。詳しくは、「Suggested InstallExecuteSequence」をご覧ください。 [URL]Suggested InstallExecuteSequence http://msdn.microsoft.com/library/en-us/msi/setup/suggested_installexecutesequence.asp 注.スクリプトファイルを配置しない設定にしたときは、「NOT REMOVE~="ALL"」となります。つまり、完全に削除する以外のときと いうことになります。詳しくは、次のページをご覧ください。 [URL]Conditional Statement Syntax http://msdn.microsoft.com/library/en-us/msi/setup/conditional_statement_syntax.asp [URL]REMOVE Property http://msdn.microsoft.com/library/en-us/msi/setup/remove.asp 「アンインストール」に追加されたカスタムアクションは、 InstallExecuteSequenceテーブルにConditionが「$(スクリプトファ イルのコンポーネントのID)=2」(注)、Sequenceが1699(複数登録 すると、5998、5997...となる)として登録されます。Sequenceが 1699というのは、MsiUnpublishAssembliesの後、 UnpublishComponentsの前ということになります。 注.スクリプトファイルを配置しない設定にしたときは、「REMOVE ~="ALL" AND ProductState <> 1」となります。 「インストール」、「確定」、「ロールバック」の違いは、 CustomActionテーブルのTypeに表れます。「インストール」、「確定」、 「ロールバック」のTypeはそれぞれ、1046、1558、1302となります。 これらの数字が意味しているものは、それぞれの数字から22を引いて、 「Custom Action In-Script Execution Options」を調べれば分かり ます。 [URL]Custom Action In-Script Execution Options http://msdn.microsoft.com/library/en-us/msi/setup/custom_action_in_script_execution_options.asp つまり、「インストール」、「確定」、「ロールバック」のカスタム アクションにはそれぞれmsidbCustomActionTypeInScript、 msidbCustomActionTypeInScript + msidbCustomActionTypeCommit、 msidbCustomActionTypeInScript + msidbCustomActionTypeRollback のオプションフラッグが付いており、それぞれの意味するところは、 「Deferred Execution Custom Actions」、「Commit Custom Actions」、 「Rollback Custom Actions」ということになります。詳しくは、下 記のリンク先をご覧ください。 [URL]Deferred Execution Custom Actions http://msdn.microsoft.com/library/en-us/msi/setup/deferred_execution_custom_actions.asp [URL]Commit Custom Actions http://msdn.microsoft.com/library/en-us/msi/setup/commit_custom_actions.asp [URL]Rollback Custom Actions http://msdn.microsoft.com/library/en-us/msi/setup/rollback_custom_actions.asp ☆EXE、DLLのカスタムアクション 上記で紹介したようなスクリプト以外に、EXEやDLLファイルをカスタ ムアクションに使用することもできます。EXEはマネージコードでも 構いませんが(注)、DLLはアンマネージコードである必要がありま す。しかし、Installクラスを使用したカスタムアクションの場合は、 DLLでも大丈夫です。ここからは、Installクラスを使った方法を紹介 します。 注.EXEファイルをそのまま実行する場合は、InstallerClassプロパテ ィをFalseにする必要があります。 ☆Installerクラス .NET Frameworkには、「Installerクラス」というクラスが用意され ており、これをカスタムアクションに使用することができます。 [URL]Installerクラス http://www.microsoft.com/japan/msdn/library/ja/cpref/html/frlrfsystemconfigurationinstallinstallerclasstopic.asp はじめに、簡単な例から紹介します。 まず、新しいクラスライブラリのプロジェクトを作成します。ここで はデプロイメントプロジェクトと同じソリューション内に作成し、名 前を「CustomAction」としました。このクラスの参照設定に、「 System.Configuration.Install.dll」を追加します(ここでは MessageBoxを使用しているため、「System.Windows.Forms.dll」も追 加します)。 次に、Installerクラスの派生クラスを作成し、 RunInstallerAttribute属性を追加し、Trueにします。さらに、必要 に応じてInstall、Commit、Rollback、Uninstallメソッドをオーバー ライドします。これらのメソッドは、それぞれ「インストール」、「確 定」、「ロールバック」、「アンインストール」の時に呼び出されま す。これらのメソッドでは、まずはじめに基本クラスのメソッドを呼 び出します。 [URL]インストール コンポーネントの既定のメソッドのオーバーライド http://www.microsoft.com/japan/msdn/library/ja/vbcon/html/vbtskoverridingdefaultmethodsoninstallationcomponent.asp ここでは以下のようなコードでビルドし、DLLファイル「 CustomAction.dll」を作成しました。 ‥‥▽VB.NET ここから▽‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ _ Public Class Installer1 Inherits System.Configuration.Install.Installer Public Overrides Sub Install( _ ByVal stateSaver As System.Collections.IDictionary) MyBase.Install(stateSaver) System.Windows.Forms.MessageBox.Show("Install") End Sub Public Overrides Sub Commit( _ ByVal savedState As System.Collections.IDictionary) MyBase.Commit(savedState) System.Windows.Forms.MessageBox.Show("Commit") End Sub Public Overrides Sub Rollback( _ ByVal savedState As System.Collections.IDictionary) MyBase.Rollback(savedState) System.Windows.Forms.MessageBox.Show("Rollback") End Sub Public Overrides Sub Uninstall( _ ByVal savedState As System.Collections.IDictionary) MyBase.Uninstall(savedState) System.Windows.Forms.MessageBox.Show("Uninstall") End Sub End Class ‥‥△VB.NET ここまで△‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ ‥‥▽C# ここから▽‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ [System.ComponentModel.RunInstaller(true)] public class Installer1 : System.Configuration.Install.Installer { public override void Install(System.Collections.IDictionary stateSaver) { base.Install(stateSaver); System.Windows.Forms.MessageBox.Show("Install"); } public override void Commit(System.Collections.IDictionary savedState) { base.Commit(savedState); System.Windows.Forms.MessageBox.Show("Commit"); } public override void Rollback(System.Collections.IDictionary savedState) { base.Rollback(savedState); System.Windows.Forms.MessageBox.Show("Rollback"); } public override void Uninstall(System.Collections.IDictionary savedState) { base.Uninstall(savedState); System.Windows.Forms.MessageBox.Show("Uninstall"); } } ‥‥△C# ここまで△‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ このカスタムアクションをデプロイメントプロジェクトで使用するた めには、先ほどと同じように、作成したDLLをデプロイメントプロジ ェクトに追加してから(ここでは、「プロジェクト出力」から、「 CustomActionプロジェクトのプライマリ出力」を追加しました)、カ スタム動作エディタに追加します。ここでは、4つのノードすべてに 追加するために、「カスタム動作」のノードを右クリックし、メニュー の[カスタム動作の追加]を選び、「CustomActionのプライマリ出力」 を選択します。 Installerクラスをカスタムアクションとして使用するには、カスタ ム動作のInstallerClassプロパティをTrueとする必要がありますが、 デフォルトでTrueのため、このままでOKです。 ビルドしてMSIファイルを作成し、実行してみましょう。VBScriptの 例と同じように、インストールの最後の方で「Install」と表示され、 その後「Commit」と表示されます。また、アンインストール時には、 「Uninstall」と表示されます。 補足.ここではInstall、Commit、Rollback、Uninstallメソッドをす べてオーバーライドしましたが、必要なものだけで大丈夫です。 補足.ここでは、Installerクラスを独自に作成しましたが、VS.NETで は、「コンポーネントの追加」で追加することができます。具体的に は、VS.NETのソリューションエディタでInstallerクラスを追加した いプロジェクトを右クリックし、メニューの[追加]-[コンポーネント の追加]から「新しい項目の追加」ダイアログを表示し、「インストー ラクラス」を選択します。 補足.ここではカスタムアクションのためだけのDLLを作成しましたが、 配布するアプリケーションにInstallerクラスの派生クラスを追加す る方法もあり、MSDN等では、主にこの方法が紹介されています。 補足.この方法では、先ほどのVBScriptの例と違い、Excludeプロパテ ィをTrueにしてDLLファイルをMSIファイルに埋め込むことはできませ ん。 ☆Installerクラスを使ったカスタムアクションの詳細 例によってOrcaにより、作成されたMSIファイルの詳細を見てみます。 Installerクラスを使った場合、カスタム動作を一つ追加すると、 CustomActionテーブルに2つの列が追加されます。「インストール」 ノードに追加されたカスタム動作の場合、例えば、次のような2つの 列が追加されます。 Action: _B2459EE5_658C_4EF0_BCE3_810CFFFBC274.uninstall.SetProperty Type: 51 Source: _B2459EE5_658C_4EF0_BCE3_810CFFFBC274.uninstall Target: /installtype=notransaction /action=uninstall /LogFile= "[#_A367697580D6A572E31AB7AA55DFF20A]" "[VSDFxConfigFile]" Action: _B2459EE5_658C_4EF0_BCE3_810CFFFBC274.uninstall Type: 1025 Source: InstallUtil Target: ManagedInstall まず、Type51のカスタムアクションにより、プロパティが設定されま す。 [URL]Custom Action Type 51 http://msdn.microsoft.com/library/en-us/msi/setup/custom_action_type_51.asp 次に、Type1025のカスタムアクションにより、InstallUtilの ManagedInstall関数が呼び出され、この時に先に設定されたプロパテ ィが引数として渡されます。Type1025は、Type1 +msidbCustomActionTypeInScriptですので、埋め込まれたDLLを呼び 出すカスタムアクションであることが分かりますが、実際に InstallUtilはBinaryテーブルに埋め込まれています。InstallUtilと いう名前からInstallUtil.exeが埋め込まれていると思われるかもし れませんが、ここに埋め込まれているのはInstallUtilLib.dllです。 [URL]Custom Action Type 1 http://msdn.microsoft.com/library/en-us/msi/setup/custom_action_type_1.asp [URL]インストーラ ツール (Installutil.exe) http://www.microsoft.com/japan/msdn/library/ja/cptools/html/cpconinstallerutilityinstallutilexe.asp なお、「/LogFile= "[#_A367697580D6A572E31AB7AA55DFF20A]"」の「 _A367697580D6A572E31AB7AA55DFF20A」は、Installerクラスのあるフ ァイル(ここでは、CustomAction.dll)のIDであり、先頭に「#」が 付いていることから、ファイルのパスを意味していることになります。 これらのカスタムアクションは、InstallExecuteSequenceテーブルで 使用されます。もちろん、Type51のカスタムアクションの後で、 Type1のカスタムアクションが実行されます。 「インストーラ」ノード以外に追加されたカスタム動作でも同様の列 がCustomActionテーブルに追加されます。ただし、「install」の文 字列がすべて「commit」「rollback」「uninstall」に置き換わりま す。また、TypeはVBScriptの例で紹介したのと同じオプションフラッ グが付きます。 ☆カスタム動作にデータを渡す カスタム動作にデータ(文字列)を渡すには、CustomActionDataプロ パティを使います。まずは、Installerクラスを使ったときの方法か ら説明します。 Installerクラスを使ったカスタムアクションにデータを渡すときは、 CustomActionDataプロパティに「/name=value」という形式で指定し ます。ここで「name」は名前で、「value」はその値を示します。複 数のデータを渡す場合は、スペース文字で区切ります。つまり、「 /name1=value1 /name2=value2」のようにします。valueにスペースが 含まれる場合は、"value 1"のように"で囲みます。"で囲まれた文字 列の最後が\となるときは、さらに\を付けて、"\\"とします。また、 Windows Installerのプロパティを使うときは、[]で囲みます。 Installerクラス内でデータを取得するには、InstallContext. Parametersプロパティを使います。 次に例を示します。まずカスタム動作のCustomActionDataプロパティ に /name=value /dir="[TARGETDIR]\" と入力します。[TARGETDIR]はインストール先のフォルダのパスを示 すWindows Installerのプロパティです。[TARGETDIR]が"で囲まれて いるのは、[TARGETDIR]にスペースが含まれる可能性があるからで、 また最後に\が付いているのは、[TARGETDIR]の最後に\が付くからで す。 このデータを取得するために、InstallerクラスのInstallメソッドを 次のようにオーバーライドします。 ‥‥▽VB.NET ここから▽‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ Public Overrides Sub Install( _ ByVal stateSaver As System.Collections.IDictionary) MyBase.Install(stateSaver) Dim val As String = Me.Context.Parameters("name") Dim targetdir As String = Me.Context.Parameters("dir") System.Windows.Forms.MessageBox.Show(("name = " + val + _ vbCrLf + "TARGETDIR = " + targetdir)) End Sub ‥‥△VB.NET ここまで△‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ ‥‥▽C# ここから▽‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ public override void Install(System.Collections.IDictionary stateSaver) { base.Install(stateSaver); string val = this.Context.Parameters["name"]; string targetdir = this.Context.Parameters["dir"]; System.Windows.Forms.MessageBox.Show("name = " + val + "\nTARGETDIR = " + targetdir); } ‥‥△C# ここまで△‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ 変数valは"value"となり、targetにはインストール先のフォルダのパ スが入ります。 このようにCustomActionDataプロパティを指定した時、作成される MSIファイルのCustomActionテーブルは次のようになります。つまり、 CustomActionDataプロパティで指定された文字列が引数として渡され ていることが分かります。 Action: _B2459EE5_658C_4EF0_BCE3_810CFFFBC274.uninstall.SetProperty Type: 51 Source: _B2459EE5_658C_4EF0_BCE3_810CFFFBC274.uninstall Target: /installtype=notransaction /action=install /LogFile= /name=value /dir="[TARGETDIR]\" "[#_A367697580D6A572E31AB7AA55DFF20A]" "[VSDFxConfigFile]" VBScriptの場合は、CustomActionDataプロパティで指定された文字列 をそのままSession.Propertyで取得できます。例えば、 CustomActionDataプロパティを「[TARGETDIR]」とした場合、次のよ うなVBScriptにより、インストール先のフォルダのパスを表示できま す。 ‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ Dim val val = Session.Property("CustomActionData") MsgBox val ‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ 複数のデータを渡すには、独自に区切り文字を決めるしかないようで す。 参考. [URL]チュートリアル : カスタム動作にデータを渡す http://www.microsoft.com/japan/msdn/library/ja/vsintro7/html/vxwlkwalkthroughpassingdatatocustomaction.asp [URL]CustomActionData プロパティ http://www.microsoft.com/japan/msdn/library/ja/vsintro7/html/vxgrfcustomactiondataproperty.asp [URL]Session.Property http://msdn.microsoft.com/library/en-us/msi/setup/session_session.asp ☆エラーを発生させる Installerクラスを使ったカスタムアクションでエラーを発生させて、 インストールが失敗するようにするには、InstallExceptionオブジェ クトをスローします。具体例として、次のようなカスタムアクション を試してみましょう。 ‥‥▽VB.NET ここから▽‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ _ Public Class Installer1 Inherits System.Configuration.Install.Installer Public Overrides Sub Install( _ ByVal stateSaver As System.Collections.IDictionary) MyBase.Install(stateSaver) Throw New System.Configuration.Install.InstallException("テストエラー") End Sub Public Overrides Sub Rollback( _ ByVal savedState As System.Collections.IDictionary) MyBase.Rollback(savedState) System.Windows.Forms.MessageBox.Show("Rollback") End Sub End Class ‥‥△VB.NET ここまで△‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ ‥‥▽C# ここから▽‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ [System.ComponentModel.RunInstaller(true)] public class Installer1 : System.Configuration.Install.Installer { public override void Install(System.Collections.IDictionary stateSaver) { base.Install(stateSaver); throw new System.Configuration.Install.InstallException("テストエラー"); } public override void Rollback(System.Collections.IDictionary savedState) { base.Rollback(savedState); System.Windows.Forms.MessageBox.Show("Rollback"); } } ‥‥△C# ここまで△‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ 作成されるMSIファイルを実行すると、「テストエラー」というダイ アログが表示され、その後、「Rollback」というメッセージボックス が表示されます。つまり、ロールバックが行われます。 VBScriptではこのようにWindows Installerにエラーを報告し、コー ルバックすることはできませんが、次のようにしてエラーダイアログ を表示することはできます。 ‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ msiMessageTypeError = &H01000000 Set record = Session.Installer.CreateRecord(0) record.StringData(0) = "テストエラー" Session.Message msiMessageTypeError, record ‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ 参考. [URL]カスタム動作でのエラー処理 http://www.microsoft.com/japan/msdn/library/ja/vsintro7/html/vxconerrorhandlingincustomactions.asp カスタム動作に関して、ヘルプにも幾つかの例が載っていますので、 参考にしてください。 [URL]チュートリアル : カスタム動作を使用して、インストール中に アセンブリをプリコンパイルする http://www.microsoft.com/japan/msdn/library/ja/vsintro7/html/vxwlkwalkthroughusingcustomactiontoprecompileassermblyduringinstallation.asp [URL]チュートリアル : カスタム動作を使用して、インストール中に データベースを作成する http://www.microsoft.com/japan/msdn/library/ja/vsintro7/html/vxwlkWalkthroughUsingCustomActionToCreateDatabaseDuringInstallation.asp [URL]チュートリアル : インストール時にアプリケーションを別の XML Web サービスにリダイレクトする http://www.microsoft.com/japan/msdn/library/ja/vsintro7/html/ vxwlkWalkthroughRedirectingApplicationToTargetDifferentXMLWebServiceDuringInstallation.asp =============================== ■ここで示したコードの多くはまずC#で書き、それを「C# to VB.NET Translator」でVB.NETのコードに変換し、修正を加えたものです。 [URL]C# to VB.NET Translator http://authors.aspalliance.com/aldotnet/examples/translate.aspx ■このマガジンの購読、購読中止、バックナンバー、説明に関しては  次のページをご覧ください。  http://www.mag2.com/m/0000104516.htm ■発行人・編集人:どぼん!  (Microsoft MVP for Visual Basic, Oct 2004-Oct 2005)  http://dobon.net  dobon_info@yahoo.co.jp ■ご質問等はメールではなく、掲示板へお願いいたします。  http://dobon.net/vb/bbs.html ■上記メールアドレスへのメールは確実に読まれる保障はありません  (スパム、ウィルス対策です)。メールは下記URLのフォームメール  から送信してください。  http://dobon.net/mail.html Copyright (c) 2003 - 2005 DOBON! All rights reserved. ===============================