WiXのカスタムアクション

カスタムアクションはWiXの最も重要な要素の1つであり、プログラムのインストールまたは削除中にアクションを実行して、WiXの機能を拡張できます。 カスタムアクションを使用して、VBScript、JScript、Dllライブラリ、実行可能モジュールをインストーラーに接続し、インストーラー中にアクションを実行できます。



例を考えてみましょう-プログラムのインストール中に、ローカルコンピューター上のファイルへのパスを指定する必要があります。たとえば、ライセンスファイルです。



まず、プロジェクトを作成し、ライセンスファイルを選択できるウィンドウを追加します(そのため、不必要なコードで記事が目立たないように、重要なコードフラグメントのみをリストします。記事の最後にあるリンクからプロジェクトソースをダウンロードして確認できます)。



プロジェクトを作成し、不要なウィンドウをインストールプロセスから除外し、ファイルが選択される新しいウィンドウを追加しました。 その結果、インストーラーには次のウィンドウが含まれます。



1.ご挨拶



2.ライセンスファイルの選択





3.インストールの開始



4.インストールの終了



プロジェクトには次のファイルが含まれます。



プロジェクトにカスタムアクションを追加するこれを行うには、 CustomAction.wxsファイルをプロジェクトに追加します



<? xml version ="1.0" encoding ="UTF-8" ? >

< Wix xmlns ="http://schemas.microsoft.com/wix/2006/wi" >

< Fragment >

< Binary Id ="BinBrowseForFile"

SourceFile ="BrowseForFile.vbs" />

< CustomAction Id ='BrowseForFile'

BinaryKey ="BinBrowseForFile"

VBScriptCall ="CallTheAction"

Return ="check" />

</ Fragment >

</ Wix >




* This source code was highlighted with Source Code Highlighter .






Binary要素を使用して、ターゲットコンピューターにインストールされないが、インストーラーに参加するファイルをプロジェクトに追加できます。 SourceFileパラメーターは、ファイルへパス(この場合はBrowseForFile.vbs)を設定します。



CustomAction要素は、プログラムのインストール/アンインストール中に実行できる動的ライブラリであるスクリプトによって実行されるアクションを記述します。 そのパラメーターを考慮してください。



BinaryKeyBinary要素への参照です。

VBScriptCallは、呼び出すVBScript関数の名前を指定します。

この場合checkを 返すと、スクリプト呼び出しが同期的に実行され、戻り値の成功が確認されます。 チェック値はデフォルト値です。



Returnパラメーターのその他の可能な値:

asyncNoWait-非同期呼び出し。インストーラーは実行の完了を待機しません。これはインストーラーが作業を終了した後でも発生する場合があります。 インストールプロセスの完了後にプログラムを実行する場合に便利です。

asyncWaitは非同期呼び出しですが、前の呼び出しとは異なり、インストーラーは実行が完了するまで待機します。

ignore - checkと同じですが、結果を壊しません。



CustomAction要素には、 CustomAction要素の完全な説明であるここにリストする意味がない他のパラメーターがたくさんあります。 ここで見つけることができます。



それを理解したらBrowseForFile.vbsファイルの内容に行きましょう



Function CallTheAction

Set objDialog = CreateObject( "UserAccounts.CommonDialog" )

objDialog.Filter = " |*.txt| |*.*"

objDialog.FilterIndex = 1

objDialog.InitialDir = "C:\"



Dim intResult : intResult = objDialog.ShowOpen



If intResult = 0 Then

CallTheAction = msiDoActionStatusUserExit

Exit Function

End If



Session. Property ( "LICENSE_FILE_PATH" ) = objDialog.FileName

CallTheAction = msiDoActionStatusSuccess

End Function




* This source code was highlighted with Source Code Highlighter .






このスクリプトは、ファイル選択ダイアログを表示し、結果を返します。 ユーザーがファイルを指定して[開く ]ボタンをクリックすると、スクリプトはProduct.wxsファイルで定義されたLICENSE_FILE_PATH変数の値を設定することによりファイルへのパスを返します。



< Property Id ="LICENSE_FILE_PATH" > C:\ </ Property >





CustomAction呼び出しの場合、 WixUI_License.wxsファイルで参照ボタンが定義されます



< Control Id ="ButtonBrowse"

Type ="PushButton"

...

Text ="" >

< Publish Event ="DoAction" Value ="BrowseForFile" Order ="1" > 1 </ Publish >

</ Control >




* This source code was highlighted with Source Code Highlighter .






この場合、 Publish要素は、ボタンのクリックに応答する役割を果たします。 イベント (DoAction)パラメーターのは、 (BrowseForFile)パラメーターで指定された識別子でCustomActionを呼び出す必要があることを示します。



ここで、ファイルのパスを保存する変数LICENSE_FILE_PATHの値を表示する場所が必要です。 表示はテキストボックスになります。



< Control Id ="TextLicensePath"

Type ="Edit"

...

Property ="LICENSE_FILE_PATH" >

</ Control >




* This source code was highlighted with Source Code Highlighter .






インストーラをアセンブルして実行した場合、[ 参照 ]ボタンをクリックしてファイルを選択すると、テキストフィールドが更新されていないことがわかります。







問題の原因を理解するには、「 戻る」ボタンをクリックしてから 次へ 」をクリックし、フィールドが更新されました。 再度、ファイルを選択し、別のウィンドウでウィンドウを「カバー」してから、それを返します。 フィールドが再び更新されました。 ええ、それは私たちがすべてを正しく行ったことを意味し、問題は、 プロパティが変更されたときにUIがフィールドの内容を更新しないことです。 どうする



1つのオプションは、 LicenseDlgウィンドウを複製し、[ 参照 ]をクリックしたときにこれらのウィンドウを切り替えることです。 実装は簡単です。 WixUI_License.wxsファイルのコピーを作成し、名前を付けます(例: WixUI_License2.wxs)LicenseDlgダイアログの名前をLicenseDlg2に変更します。 ウィザードスクリプトで、次を追加します。



< Publish Dialog ="LicenseDlg2" Control ="Back" Event ="NewDialog" Value ="WelcomeDlg" > 1 </ Publish >

< Publish Dialog ="LicenseDlg2" Control ="Next" Event ="NewDialog" Value ="VerifyReadyDlg" > 1 </ Publish >



< Publish Dialog ="LicenseDlg" Control ="ButtonBrowse" Event ="NewDialog" Value ="LicenseDlg2" > 1 </ Publish >

< Publish Dialog ="LicenseDlg2" Control ="ButtonBrowse" Event ="NewDialog" Value ="LicenseDlg" > 1 </ Publish >



* This source code was highlighted with Source Code Highlighter .






最初の2行はLicenseDlgダイアログの動作を複製し、2行目は複製ウィンドウ間の遷移を担当します。



開始、確認、動作します。 しかし、私はそのような方法が好きではありません。 コードを複製し、その構造を変更するたびにダイアログを削除、コピー、名前変更する必要がある。



さて、ここでC ++をなんらかの方法で「ねじ込む」ことが可能であれば、ウィンドウの更新を即座に把握できます...しかし、できます。 記事の冒頭ですでに説明したように、DllライブラリをCustomActionのソースとして使用できます。 ソースコードを含む既製のライブラリは、サイトwww.installsite.orgにあります。



このライブラリのソースを見ると、 BrowseForFile関数がPATHTOFILEプロパティから初期ファイルパス値を取得し、ファイル選択ダイアログを表示し、成功した場合、取得したファイルパス値をPATHTOFILEに保存することがわかります。 次に、更新が必要なウィンドウをデフォルトウィンドウの名前で見つけます-InstallShield WizardはRichEdit20Wというクラス名を持つ要素を見つけ、選択されたファイルへのパスに等しいテキストを設定します。



このライブラリを使用して、ウィンドウ検索を微調整し、ファイル選択ダイアログでフィルタリングして、プロジェクトに接続できます。 そして、このコードを基礎として、独自のより汎用的なライブラリを作成できます。 私の意見では、ライブラリはファイル選択ダイアログのフィルターを入力パラメーターとして受け入れ、インストーラーウィンドウを正しく見つけることができるはずです。



プロジェクトのライブラリを作成しましょう。 これを行うには、プロジェクトに新しいプロジェクトを追加しますファイル-追加-新しいプロジェクト )。 プロジェクトタイプWin32プロジェクト











Msi.libライブラリをプロジェクトに接続します







次に、エクスポートされた関数の説明を含むファイルを作成し、すぐにBrowseForFileと呼ばれることに同意します



LIBRARY "BrowseForFile"

EXPORTS

BrowseForFile





C ++でカスタムアクションを作成するもう1つの簡単な方法がありますこれを行うには、ウィンドウで新しいプロジェクトを選択します







しかし、それがどのように機能するかを理解するために、初めてすべてを手で作成する方が良いのです。そうして初めて、マスターに作成を信頼することができます。



BrowseForFile関数を作成する



UINT __stdcall BrowseForFile(MSIHANDLE hInstall)

{

long lErrMsg = 0;



TCHAR szOriginalPath[MAX_PATH] = {0};

TCHAR szDialogFilter[MAX_PATH] = {0};

TCHAR szIndex[8] = {0};



DWORD cchValue;



// BFF_PATH_TO_FILE

cchValue = _countof(szOriginalPath);

MsiGetProperty(hInstall, TEXT( "BFF_PATH_TO_FILE" ), szOriginalPath, &cchValue);



// BFF_FILE_DIALOG_FILTER

cchValue = _countof(szDialogFilter);

MsiGetProperty(hInstall, TEXT( "BFF_FILE_DIALOG_FILTER" ), szDialogFilter, &cchValue);



size_t nFilterLength = wcslen(szDialogFilter);



for (size_t i = 0; i < nFilterLength; ++i)

{

if (szDialogFilter[i] == '|' )

{

szDialogFilter[i] = '\0' ;

}

}



OPENFILENAME ofn = {0};



// OPENFILENAME.

ofn.lStructSize = sizeof (ofn);

ofn.hwndOwner = GetForegroundWindow();

ofn.lpstrFile = szOriginalPath;

ofn.nMaxFile = _countof(szOriginalPath);

ofn.lpstrFilter = szDialogFilter;

ofn.nFilterIndex = 0;

ofn.lpstrFileTitle = NULL;

ofn.nMaxFileTitle = 0;

ofn.lpstrInitialDir = NULL;



if (GetOpenFileName(&ofn))

{

// .

MsiSetProperty(hInstall, TEXT( "BFF_PATH_TO_FILE" ), szOriginalPath);

}



return ERROR_SUCCESS;

}




* This source code was highlighted with Source Code Highlighter .






この段階で、ライブラリはスクリプトと同じすべてを実行できます。 フィールドを更新する機能を追加します。 この例では、ウィンドウ名による検索がありましたが、私の意見ではこれは正しくありません。 プロジェクトごとに、ウィンドウ名が変わる場合があります。普遍的な方法を見つけたいと思います。 このタスクを自分で設定したので、最初に思い浮かんだことを確認することにしました-インストーラーウィンドウにはどのクラスがありますか? Spy ++ユーティリティ(Visual Studioに付属)を使用してインストーラーウィンドウの情報を確認したところ、ウィンドウにMsiDialogCloseClassクラスがあることがわかりました。 それは彼に依存します。 コードを追加します。



// .

BOOL CALLBACK EnumChildProc(HWND hWnd, LPARAM lParam)

{

TCHAR szBuffer[100] = {0};



GetClassName(hWnd, (LPTSTR)&szBuffer, _countof(szBuffer));



if (_wcsicmp(szBuffer, (_T( "RichEdit20W" ))) == 0)

{

// .

*(HWND*)lParam = hWnd;



return FALSE;

}



return TRUE;

}




* This source code was highlighted with Source Code Highlighter .






そして、条件内のBrowseForFile関数に:



if (GetOpenFileName(&ofn))







追加:

// .

HWND hInstallerWnd = FindWindow(_T( "MsiDialogCloseClass" ), NULL);



if (hInstallerWnd != NULL)

{

HWND hWndChild = NULL;



EnumChildWindows(hInstallerWnd, EnumChildProc, (LPARAM)&hWndChild);



if (hWndChild != NULL)

{

SendMessage(hWndChild, WM_SETTEXT, 0, (LPARAM)szOriginalPath);

}

}




* This source code was highlighted with Source Code Highlighter .






インストールパッケージ内のライブラリを使用する準備がすべて整いました。 Product.wxsファイルで、2つの新しいプロパティ宣言します。

< Property Id ="BFF_PATH_TO_FILE" ></ Property >

< Property Id ="BFF_FILE_DIALOG_FILTER" > (*.*)|*.*| (*.txt)|*.txt|| </ Property >







BFF_PATH_TO_FILEプロパティを通じて、Dllライブラリはインストーラーと「通信」します。

BFF_FILE_DIALOG_FILTERプロパティには、ファイル選択ダイアログ用のフィルターが含まれています。



CustomActionの説明を次のように変更します。



< Binary Id ="BinBrowseForFile" SourceFile ="..\Debug\BrowseForFile.DLL" />

< CustomAction Id ="BrowseForFile"

BinaryKey ="BinBrowseForFile"

DllEntry ="BrowseForFile"

Return ="check" />




* This source code was highlighted with Source Code Highlighter .






そして、新しいCustomActionを追加します 。これは、値LICENSE_FILE_PATHを、Dllを呼び出した結果として必要なプロパティに割り当てます( BFF_PATH_TO_FILE



< CustomAction Id ='AssignPathToProperty'

Property ='LICENSE_FILE_PATH'

Value ='[BFF_PATH_TO_FILE]' />




* This source code was highlighted with Source Code Highlighter .






最後にやるべきことは、ボタンを押す役割を担うコードを変更することです。新しいCustomActionに呼び出しを追加します



< Control Id ="ButtonBrowse"

...

Text ="" >

< Publish Event ="DoAction" Value ="BrowseForFile" Order ="1" > 1 </ Publish >

< Publish Event ="DoAction" Value ="AssignPathToProperty" Order ="2" > 1 </ Publish >

</ Control >




* This source code was highlighted with Source Code Highlighter .






完了し、収集して確認できます。



カスタムアクションは、ユーザー側のアクションに応答するだけでなく、インストーラーのプロセスで自動的に呼び出すこともできます。 プログラムのインストール中にプログラムフォルダーにConfigフォルダーを作成し、削除プロセス中にこのフォルダーを削除する必要がある簡単な例を見てみましょう(子Directoryセクションを使用して同じことを行う別の方法があります。この場合、この方法は例のために選択されました)



インストールプロセス中にフォルダーを作成することから始めましょう。 新しいCustomActionを作成します。



< CustomAction Id ="CreateConfigFolder" Script ="vbscript" >

<! [CDATA[

On Error Resume Next

Set objFso = CreateObject("Scripting.FileSystemObject")

strFolderPath = Session.Property("INSTALLLOCATION") & "\Config"

objFso.CreateFolder(strFolderPath)

]] >

</ CustomAction >




* This source code was highlighted with Source Code Highlighter .






例からわかるように、スクリプトコードはCustomAction要素の本文に直接記述でき、Binary要素を作成して参照する必要はありません。



アンインストールプロセス中にフォルダーを削除するには:



< CustomAction Id ="RemoveConfigFolder" Script ="vbscript" >

<! [CDATA[

On Error Resume Next

Set objFso = CreateObject("Scripting.FileSystemObject")

strFolderPath = Session.Property("INSTALLLOCATION") & "\Config"

objFso.DeleteFolder(strFolderPath)

]] >

</ CustomAction >




* This source code was highlighted with Source Code Highlighter .






アクションが追加され、誰かがそれらを呼び出して起動する必要があります。 Product.wxsファイルにコードを追加します。



< InstallExecuteSequence >

< Custom Action ="CreateConfigFolder" After ="InstallFinalize" > Not Installed </ Custom >

< Custom Action ="RemoveConfigFolder" Before ="RemoveFiles" > Installed </ Custom >

</ InstallExecuteSequence >




* This source code was highlighted with Source Code Highlighter .






InstallExecuteSequenceセクションを使用すると、他のセクションを自分の内部に配置できます。これらのセクションは、関与するタイミングを正確に指定できます。 この例では、 InstallFinalizeステージが完了 (インストールが完了)した後にフォルダーが作成され、 RemoveFilesステージ(ファイルの削除)の前に削除が実行されます。



カスタムセクションにはいくつかのテキストが含まれていることに注意してください。 これらは、アクションを実行するかどうかを決定する論理条件です。 インストール済み -製品がすでにインストールされていることを示すサイン。 つまり 製品がまだインストールされていない場合、 CreateConfigFolderは機能します 。 製品がインストールされている場合は、 RemoveConfigFolder 、つまり プログラムのアンインストール中。



それだけです。退屈すぎないことを望みます。 質問がある場合-恥ずかしがらないで、聞いてください。



プロジェクトソース

使用のための簡単な指示を含むDLLバイナリ



前の記事へのリンク: パート1(はじめに)パート2(プロジェクトの整理)パート3(カスタムウィンドウ)



All Articles