カスタムアクションは 、
WiXの最も重要な要素の1つであり、プログラムのインストールまたは削除中にアクションを実行して、WiXの機能を拡張できます。 カスタムアクションを使用して、VBScript、JScript、Dllライブラリ、実行可能モジュールをインストーラーに接続し、インストーラー中にアクションを実行できます。
例を考えてみましょう-プログラムのインストール中に、ローカルコンピューター上のファイルへのパスを指定する必要があります。たとえば、ライセンスファイルです。
まず、プロジェクトを作成し、ライセンスファイルを選択できるウィンドウを追加します(そのため、不必要なコードで記事が目立たないように、重要なコードフラグメントのみをリストします。記事の最後にあるリンクからプロジェクトソースをダウンロードして確認できます)。
プロジェクトを作成し、不要なウィンドウをインストールプロセスから除外し、ファイルが選択される新しいウィンドウを追加しました。 その結果、インストーラーには次のウィンドウが含まれます。
1.ご挨拶2.ライセンスファイルの選択
3.インストールの開始4.インストールの終了プロジェクトには次のファイルが含まれます。
- Product.wxs製品の説明
- Features.wxsインストールオプション
- Files.wxsインストール可能ファイル
- Variables.wxi変数
- [ プログラムの追加と削除 ]パネルでの製品の表示に影響するAddRemove.wxiパラメーター
- ライセンスファイルを選択できるWixUI_License.wxsウィンドウ
- インストールウィザードのステップのWixUI_Wizard.wxsの説明
プロジェクトに
カスタムアクションを追加するこれを行うには、
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要素は、プログラムのインストール/アンインストール中に実行できる動的ライブラリであるスクリプトによって実行されるアクションを記述します。 そのパラメーターを考慮してください。
BinaryKeyは
Binary要素への参照です。
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(カスタムウィンドウ)