しばらくの間 WiX を使用しており、使いやすさについての通常の不満にもかかわらず、かなりうまくいっています。私が探しているのは、次に関する有用なアドバイスです。
- WiX プロジェクトのセットアップ (レイアウト、参照、ファイル パターン)
- ソリューションへの WiX の統合、およびビルド/リリース プロセス
- 新規インストールおよびアップグレードのためのインストーラーの構成
- 共有したい優れた WiX ハック
変数を別のwxi
インクルード ファイルに保持します。再利用を可能にし、変数をすばやく見つけ、(必要に応じて) 外部ツールによる操作を容易にします。
x86 および x64 ビルドのプラットフォーム変数を定義する
<!-- Product name as you want it to appear in Add/Remove Programs-->
<?if $(var.Platform) = x64 ?>
<?define ProductName = "Product Name (64 bit)" ?>
<?define Win64 = "yes" ?>
<?define PlatformProgramFilesFolder = "ProgramFiles64Folder" ?>
<?else ?>
<?define ProductName = "Product Name" ?>
<?define Win64 = "no" ?>
<?define PlatformProgramFilesFolder = "ProgramFilesFolder" ?>
<?endif ?>
インストール場所をレジストリに保存して、アップグレードが正しい場所を見つけられるようにします。たとえば、ユーザーがカスタム インストール ディレクトリを設定したとします。
<Property Id="INSTALLLOCATION">
<RegistrySearch Id="RegistrySearch" Type="raw" Root="HKLM" Win64="$(var.Win64)"
Key="Software\Company\Product" Name="InstallLocation" />
</Property>
注: WiX の第一人者であるRob Menschingは、優れたブログ エントリを投稿しました。このエントリでは、コマンド ラインからプロパティを設定する際の、より詳細に説明し、エッジ ケースを修正しています。
1. 2. および 3. を使用した例
<?include $(sys.CURRENTDIR)\Config.wxi?>
<Product ... >
<Package InstallerVersion="200" InstallPrivileges="elevated"
InstallScope="perMachine" Platform="$(var.Platform)"
Compressed="yes" Description="$(var.ProductName)" />
と
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="$(var.PlatformProgramFilesFolder)">
<Directory Id="INSTALLLOCATION" Name="$(var.InstallName)">
最も簡単な方法は、常にメジャー アップグレードを行うことです。これは、単一の MSI で新規インストールとアップグレードの両方が可能になるためです。UpgradeCodeは一意の Guid に固定されており、既存の製品をアップグレードしたくない場合を除き、変更されることはありません。
注: WiX 3.5 には、作業をさらに簡単にする新しいMajorUpgrade要素があります。
アプリケーションの追加と削除でアイコンを作成する
<Icon Id="Company.ico" SourceFile="..\Tools\Company\Images\Company.ico" />
<Property Id="ARPPRODUCTICON" Value="Company.ico" />
<Property Id="ARPHELPLINK" Value="http://www.example.com/" />
リリース ビルドでは、msi ファイルを配置ディレクトリにコピーして、インストーラーをバージョン管理します。AfterBuild ターゲットから呼び出される wixproj ターゲットを使用した例:
<Target Name="CopyToDeploy" Condition="'$(Configuration)' == 'Release'">
<!-- Note we append AssemblyFileVersion, changing MSI file name only works with Major Upgrades -->
<Copy SourceFiles="$(OutputPath)$(OutputName).msi"
DestinationFiles="..\Deploy\Setup\$(OutputName) $(AssemblyFileVersion)_$(Platform).msi" />
</Target>
heat を使用して、ワイルドカード (*) Guid を含むファイルを収集します。複数のプロジェクトで WXS ファイルを再利用する場合に便利です (同じ製品の複数のバージョンに関する私の回答を参照してください)。たとえば、このバッチ ファイルは RoboHelp の出力を自動的に収集します。
@echo off
robocopy ..\WebHelp "%TEMP%\WebHelpTemp\WebHelp" /E /NP /PURGE /XD .svn
"%WIX%bin\heat" dir "%TEMP%\WebHelp" -nologo -sfrag -suid -ag -srd -dir WebHelp -out WebHelp.wxs -cg WebHelpComponent -dr INSTALLLOCATION -var var.WebDeploySourceDir
robocopy
ハーベストの前に Subversion の作業コピーのメタデータを削除しています。ルート ディレクトリの参照は、-dr
デフォルトの TARGETDIR ではなく、インストール場所に設定されます。-var
ソースディレクトリを指定する変数を作成するために使用されます (Web デプロイメント出力)。
Strings.wxl を使用してローカライズすることにより、ウェルカム ダイアログのタイトルに製品バージョンを簡単に含めることができます。(クレジット: saschabeaumont . この素晴らしいヒントはコメントに隠されているため追加)
<WixLocalization Culture="en-US" xmlns="http://schemas.microsoft.com/wix/2006/localization">
<String Id="WelcomeDlgTitle">{\WixUI_Font_Bigger}Welcome to the [ProductName] [ProductVersion] Setup Wizard</String>
</WixLocalization>
ファイルごとに 1 つのコンポーネントというWim Coehen のアドバイスに従ってください。これにより、コンポーネント GUID*
を除外 (またはワイルドカード) することもできます。
Rob Mensching は、MSI ログ ファイル内の問題をすばやく追跡するために、value 3
. 国際化に関するコメントに注意してください。
条件付き機能を追加するときは、デフォルトの機能レベルを 0 (無効) に設定してから、条件レベルを目的の値に設定する方が直感的です。デフォルトの機能レベル >= 1 を設定した場合、それを無効にするには条件レベルを 0 にする必要があります。
<Feature Id="NewInstallFeature" Level="0" Description="New installation feature" Absent="allow">
<Condition Level="1">NOT UPGRADEFOUND</Condition>
</Feature>
<Feature Id="UpgradeFeature" Level="0" Description="Upgrade feature" Absent="allow">
<Condition Level="1">UPGRADEFOUND</Condition>
</Feature>
すべての ID を個別の名前空間に保持する
F.
例で始まります: F.Documentation、F.Binaries、F.SampleCode。C.
: C.ChmFile、C.ReleaseNotes、C.LicenseFile、C.IniFile、C.RegistryCA.
例: CA.LaunchHelp、CA.UpdateReadyDlg、CA.SetPropertyX です。Fi.
Di.
これは、さまざまなカテゴリすべてのさまざまな ID をすべて追跡するのに非常に役立ちます。
素晴らしい質問です。いくつかのベストプラクティスが示されるのを楽しみにしています。
配布するファイルがたくさんあるので、プロジェクトをいくつかの wxs ソース ファイルにセットアップしました。
私は Product.wxs と呼ぶ最上位のソース ファイルを持っています。これには、基本的にインストール用の構造が含まれていますが、実際のコンポーネントは含まれていません。このファイルにはいくつかのセクションがあります。
<Product ...>
<Package ...>
<Media>...
<Condition>s ...
<Upgrade ..>
<Directory>
...
</Directory>
<Feature>
<ComponentGroupRef ... > A bunch of these that
</Feature>
<UI ...>
<Property...>
<Custom Actions...>
<Install Sequences....
</Package>
</Product>
残りの .wix ファイルは、Product.wxs の Feature タグで参照される ComponentGroups を含むフラグメントで構成されます。私のプロジェクトには、配布するファイルの適切な論理グループが含まれています
<Fragment>
<ComponentGroup>
<ComponentRef>
....
</ComponentGroup>
<DirectoryRef>
<Component... for each file
....
</DirectoryRef>
</Fragment>
これは完璧ではありません。フラグメントは Product.wxs ファイル (DirectoryRef など) 内の名前を参照する必要があるため、私の OO スパイダーの感覚は少しうずきますが、単一の大きなソース ファイルを維持する方が簡単だと思います。
これについてのコメントを聞きたいです。または、誰かが良いヒントもあれば!
終了ダイアログにチェックボックスを追加して、アプリまたはヘルプファイルを起動します。
...
<!-- CA to launch the exe after install -->
<CustomAction Id ="CA.StartAppOnExit"
FileKey ="YourAppExeId"
ExeCommand =""
Execute ="immediate"
Impersonate ="yes"
Return ="asyncNoWait" />
<!-- CA to launch the help file -->
<CustomAction Id ="CA.LaunchHelp"
Directory ="INSTALLDIR"
ExeCommand ='[WindowsFolder]hh.exe IirfGuide.chm'
Execute ="immediate"
Return ="asyncNoWait" />
<Property Id="WIXUI_EXITDIALOGOPTIONALCHECKBOXTEXT"
Value="Launch MyApp when setup exits." />
<UI>
<Publish Dialog ="ExitDialog"
Control ="Finish"
Order ="1"
Event ="DoAction"
Value ="CA.StartAppOnExit">WIXUI_EXITDIALOGOPTIONALCHECKBOXTEXT</Publish>
</UI>
このようにすると、「標準」の外観は正しくありません。チェックボックスは常に灰色の背景ですが、ダイアログは白です:
これを回避する 1 つの方法は、独自のカスタム ExitDialog を指定し、別の場所にチェックボックスを配置することです。これは機能しますが、1 つのコントロールの色を変更するだけでも大変な作業のように思えます。同じことを解決する別の方法は、生成された MSI を後処理して、その特定の CheckBox コントロールの Control テーブルの X、Y フィールドを変更することです。JavaScript コードは次のようになります。
var msiOpenDatabaseModeTransact = 1;
var filespec = WScript.Arguments(0);
var installer = new ActiveXObject("WindowsInstaller.Installer");
var database = installer.OpenDatabase(filespec, msiOpenDatabaseModeTransact);
var sql = "UPDATE `Control` SET `Control`.`Height` = '18', `Control`.`Width` = '170'," +
" `Control`.`Y`='243', `Control`.`X`='10' " +
"WHERE `Control`.`Dialog_`='ExitDialog' AND " +
" `Control`.`Control`='OptionalCheckBox'";
var view = database.OpenView(sql);
view.Execute();
view.Close();
database.Commit();
(light.exe から) MSI が生成された後に、このコードをコマンド ライン スクリプトとして (cscript.exe を使用して) 実行すると、よりプロフェッショナルに見える ExitDialog が生成されます。
簡単に言うと、インストーラーごとに一意の UpgradeCode を作成し、インストーラーごとに各 Guid の最初の文字を自動的に定義し、残りの 31 文字を一意のままにします。
Config.wxi の例
<?xml version="1.0" encoding="utf-8"?>
<Include>
<!-- Upgrade code should not change unless you want to install
a new product and have the old product remain installed,
that is, both products existing as separate instances. -->
<?define UpgradeCode = "YOUR-GUID-HERE" ?>
<!-- Platform specific variables -->
<?if $(var.Platform) = x64 ?>
<!-- Product name as you want it to appear in Add/Remove Programs-->
<?define ProductName = "Foo 64 Bit [Live]" ?>
<?else ?>
<?define ProductName = "Foo [Live]" ?>
<?endif ?>
<!-- Directory name used as default installation location -->
<?define InstallName = "Foo [Live]" ?>
<!-- Registry key name used to store installation location -->
<?define InstallNameKey = "FooLive" ?>
<?define VDirName = "FooLive" ?>
<?define AppPoolName = "FooLiveAppPool" ?>
<?define DbName = "BlahBlahLive" ?>
</Include>
例 Config.Common.wxi
<?xml version="1.0" encoding="utf-8"?>
<Include>
<!-- Auto-generate ProductCode for each build, release and upgrade -->
<?define ProductCode = "*" ?>
<!-- Note that 4th version (Revision) is ignored by Windows Installer -->
<?define ProductVersion = "1.0.0.0" ?>
<!-- Minimum version supported if product already installed and this is an upgrade -->
<!-- Note that 4th version (Revision) is ignored by Windows Installer -->
<?define MinimumUpgradeVersion = "0.0.0.0" ?>
<!-- Platform specific variables -->
<?if $(var.Platform) = x64 ?>
<?define Win64 = "yes" ?>
<?define PlatformProgramFilesFolder = "ProgramFiles64Folder" ?>
<?else ?>
<?define Win64 = "no" ?>
<?define PlatformProgramFilesFolder = "ProgramFilesFolder" ?>
<?endif ?>
<?define ProductManufacturer = "Foo Technologies"?>
<!-- Decimal Language ID (LCID) for the Product. Used for localization. -->
<?define ProductLanguage = "1033" ?>
<?define WebSiteName = "DefaultWebSite" ?>
<?define WebSitePort = "80" ?>
<?define DbServer = "(local)" ?>
</Include>
例 Components.wxs
<?xml version="1.0" encoding="utf-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<!-- The pre-processor variable which allows the magic to happen :) -->
<?include $(sys.CURRENTDIR)\Config.wxi?>
<?include ..\Setup.Library\Config.Common.wxi?>
<Fragment Id="ComponentsFragment">
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="$(var.PlatformProgramFilesFolder)">
<Directory Id="INSTALLLOCATION" Name="$(var.InstallName)">
<Component Id="ProductComponent" Guid="0XXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX" KeyPath="yes">
...
注: ここでは、Guid 属性を Component ( と同等*
) から除外し、コンポーネントごとに 1 つのファイルを使用し、そのファイルをキーパスとして設定することをお勧めします。これにより、以下に示す呼び出しModifyComponentsGuids
とターゲットの必要がなくなります。RevertComponentsGuids
ただし、これはすべてのコンポーネントで可能であるとは限りません。
例 Setup.Live.wixproj
<Import Project="$(MSBuildExtensionsPath)\MSBuildCommunityTasks\MSBuild.Community.Tasks.Targets" />
<Target Name="BeforeBuild">
<CallTarget Targets="ModifyComponentsGuids" />
</Target>
<Target Name="AfterBuild">
<CallTarget Targets="RevertComponentsGuids" />
</Target>
<!-- Modify the first character of every Guid to create unique value for Live, Test and Training builds -->
<Target Name="ModifyComponentsGuids">
<FileUpdate Files="..\Setup.Library\Components.wxs" Regex="Guid="([a-f]|[A-F]|\d)" ReplacementText="Guid="A" />
</Target>
<!-- Revert the first character of every Guid back to initial value -->
<Target Name="RevertComponentsGuids">
<FileUpdate Files="..\Setup.Library\Components.wxs" Regex="Guid="([a-f]|[A-F]|\d)" ReplacementText="Guid="0" />
</Target>
最終的な考え
更新 1: コンポーネント Guid の自動生成により、ファイルごとに Guid="*" を使用してコンポーネントを作成し、ファイルをキーパスとして設定する場合、FileUpdate タスクを呼び出す必要がなくなります。
更新 2:私たちが直面した問題の 1 つは、コンポーネントの GUID を自動生成せずにビルドが失敗した場合、一時ファイルを手動で削除する必要があることです。
更新 3: svn:externals と一時ファイルの作成への依存を取り除く方法を見つけました。これにより、ビルド プロセスの回復力が向上し (Guid をワイルドカード化できない場合に最適なオプションです)、照明やろうそくでビルドが失敗した場合の脆弱性が軽減されます。
更新 4:インスタンス トランスフォームを使用した複数インスタンスのサポートは WiX 3.0+ にあり、これも一見の価値があります。
Msi 診断ログを使用して詳細な障害情報を取得する
msiexec /i Package.msi /l*v c:\Package.log
どこ
パッケージ.msiはパッケージの名前であり、
c:\Package.logログの出力が必要な場所です
Wix イントロ ビデオ
ああ、ランダム Wix イントロ ビデオ「Mr. WiX」Rob Mensching は「概念的な全体像」に役立ちます。
とても簡単なので、Javascript CustomActions を使用してください
Javascript を MSI CustomActions に使用するのは間違っていると言われています。与えられた理由: デバッグが難しい、信頼できるものにするのが難しい。同意しません。デバッグは難しくありません。C++ よりも難しくありません。それはただ違う。Javascript での CustomActions の記述は、C++ を使用するよりもはるかに簡単であることがわかりました。はるかに高速。そして同じくらい信頼できます。
欠点が 1 つだけあります。Javascript の CustomActions は Orca を介して抽出できますが、C/C++ CA ではリバース エンジニアリングが必要になります。インストーラー マジックが保護された知的財産であると考える場合は、スクリプトを避ける必要があります。
スクリプトを使用する場合は、何らかの構造から始める必要があります。ここにあなたが始めるためのいくつかがあります.
CustomAction の Javascript「ボイラープレート」コード:
//
// CustomActions.js
//
// Template for WIX Custom Actions written in Javascript.
//
//
// Mon, 23 Nov 2009 10:54
//
// ===================================================================
// http://msdn.microsoft.com/en-us/library/sfw6660x(VS.85).aspx
var Buttons = {
OkOnly : 0,
OkCancel : 1,
AbortRetryIgnore : 2,
YesNoCancel : 3
};
var Icons = {
Critical : 16,
Question : 32,
Exclamation : 48,
Information : 64
};
var MsgKind = {
Error : 0x01000000,
Warning : 0x02000000,
User : 0x03000000,
Log : 0x04000000
};
// http://msdn.microsoft.com/en-us/library/aa371254(VS.85).aspx
var MsiActionStatus = {
None : 0,
Ok : 1, // success
Cancel : 2,
Abort : 3,
Retry : 4, // aka suspend?
Ignore : 5 // skip remaining actions; this is not an error.
};
function MyCustomActionInJavascript_CA() {
try {
LogMessage("Hello from MyCustomActionInJavascript");
// ...do work here...
LogMessage("Goodbye from MyCustomActionInJavascript");
}
catch (exc1) {
Session.Property("CA_EXCEPTION") = exc1.message ;
LogException(exc1);
return MsiActionStatus.Abort;
}
return MsiActionStatus.Ok;
}
// Pop a message box. also spool a message into the MSI log, if it is enabled.
function LogException(exc) {
var record = Session.Installer.CreateRecord(0);
record.StringData(0) = "CustomAction: Exception: 0x" + decimalToHexString(exc.number) + " : " + exc.message;
Session.Message(MsgKind.Error + Icons.Critical + Buttons.btnOkOnly, record);
}
// spool an informational message into the MSI log, if it is enabled.
function LogMessage(msg) {
var record = Session.Installer.CreateRecord(0);
record.StringData(0) = "CustomAction:: " + msg;
Session.Message(MsgKind.Log, record);
}
// http://msdn.microsoft.com/en-us/library/d5fk67ky(VS.85).aspx
var WindowStyle = {
Hidden : 0,
Minimized : 1,
Maximized : 2
};
// http://msdn.microsoft.com/en-us/library/314cz14s(v=VS.85).aspx
var OpenMode = {
ForReading : 1,
ForWriting : 2,
ForAppending : 8
};
// http://msdn.microsoft.com/en-us/library/a72y2t1c(v=VS.85).aspx
var SpecialFolders = {
WindowsFolder : 0,
SystemFolder : 1,
TemporaryFolder : 2
};
// Run a command via cmd.exe from within the MSI
function RunCmd(command)
{
var wshell = new ActiveXObject("WScript.Shell");
var fso = new ActiveXObject("Scripting.FileSystemObject");
var tmpdir = fso.GetSpecialFolder(SpecialFolders.TemporaryFolder);
var tmpFileName = fso.BuildPath(tmpdir, fso.GetTempName());
LogMessage("shell.Run("+command+")");
// use cmd.exe to redirect the output
var rc = wshell.Run("%comspec% /c " + command + "> " + tmpFileName, WindowStyle.Hidden, true);
LogMessage("shell.Run rc = " + rc);
// here, optionally parse the output of the command
if (parseOutput) {
var textStream = fso.OpenTextFile(tmpFileName, OpenMode.ForReading);
while (!textStream.AtEndOfStream) {
var oneLine = textStream.ReadLine();
var line = ParseOneLine(oneLine);
...
}
textStream.Close();
}
if (deleteOutput) {
fso.DeleteFile(tmpFileName);
}
return {
rc : rc,
outputfile : (deleteOutput) ? null : tmpFileName
};
}
次に、カスタム アクションを次のように登録します。
<Fragment>
<Binary Id="IisScript_CA" SourceFile="CustomActions.js" />
<CustomAction Id="CA.MyCustomAction"
BinaryKey="IisScript_CA"
JScriptCall="MyCustomActionInJavascript_CA"
Execute="immediate"
Return="check" />
</Fragmemt>
もちろん、複数のカスタム アクションに対して、好きなだけ Javascript 関数を挿入できます。1 つの例: Javascript を使用して IIS で WMI クエリを実行し、ISAPI フィルターをインストールできる既存の Web サイトのリストを取得しました。次に、このリストを使用して、後で UI シーケンスに表示されるリストボックスにデータを入力しました。すべて非常に簡単です。
IIS7 では IIS 用の WMI プロバイダーがないため、shell.Run()
appcmd.exe を呼び出して作業を実行する方法を使用しました。簡単。
関連する質問: Javascript CustomActions について
ビルド中にT4を使用してWXSファイルを生成することについて誰も言及していないことに驚いています。私はHenryLee@ NewAgeSolutionsを通じてこれについて学びました。
基本的に、T4テンプレートを実行するカスタムMSBuildタスクを作成し、そのテンプレートはWixプロジェクトがコンパイルされる直前にWXSを出力します。これにより、(実装方法に応じて)別のソリューションのコンパイルから出力されたすべてのアセンブリを自動的に含めることができます(つまり、新しいアセンブリを追加するたびにwxを編集する必要がなくなります)。
Peter Tateは、再利用可能なComponentGroup定義を個別のwixフラグメントで定義する方法をすでに示しています。これに関連するいくつかの追加のトリック:
ディレクトリエイリアシング
コンポーネントグループフラグメントは、メイン製品wxsによって定義されたディレクトリについて知る必要はありません。コンポーネントグループフラグメントでは、次のようなフォルダについて話すことができます。
<DirectoryRef Id="component1InstallFolder">
...
</DirectoryRef>
次に、メイン製品は次のようにそのディレクトリの1つ(「productInstallFolder」など)にエイリアスを付けることができます。
<Directory Id="productInstallFolder" Name="ProductName">
<!-- not subfolders (because no Name attribute) but aliases for parent! -->
<Directory Id="component1InstallFolder"/>
<Directory Id="component2InstallFolder"/>
</Directory>
依存関係グラフ
ComponentGroup要素には、ComponentGroupRef子要素を含めることができます。これは、再利用可能なコンポーネントの大きなプールがあり、それらの間に複雑な依存関係グラフがある場合に最適です。コンポーネントごとに独自のフラグメントでComponentGroupを設定し、次のように依存関係を宣言します。
<ComponentGroup Id="B">
<ComponentRef Id="_B" />
<ComponentGroupRef Id="A">
</ComponentGroup>
アプリケーションの直接の依存関係であるため、セットアップでコンポーネントグループ「B」を参照すると、アプリケーションの作成者が「B」の依存関係であることに気づかなかった場合でも、コンポーネントグループ「A」が自動的に取り込まれます。循環依存関係がない限り、「正常に機能」します。
再利用可能なwixlib
上記の依存関係グラフのアイデアは、big-pool-o-reusable-componentsをlit.exeを使用して再利用可能なwixlibにコンパイルする場合に最適に機能します。アプリケーションセットアップを作成するときは、wixobjファイルのようにこのwixlibを参照できます。Candle.exeリンカーは、メイン製品のwxsファイルによって「プルイン」されていないフラグメントを自動的に削除します。
Heat.exe を使用して顔を壊し、痛々しいほど大規模なインストールに「Epic Pwnage」を与える
熱に関するSiと
Robert-P の回答
を拡張します。
翻訳: (プロジェクトに手動で個々のファイルを入力することを避けるため、およびビルドを自動化して全体的なプロセスをより簡単にするために、heat を使用します。)
WiX 2.0 Heat Syntax の詳細
新しいバージョンの場合(古いバージョンとそれほど変わらないが、面倒な構文の変更がある可能性があります....)cmd.exeからHeatがあるディレクトリに移動し、heatと入力するだけですが、ここに例があります。必要に応じて新しいバージョンで。
Visual Studio 2010 のビルド イベントに以下を追加します。
(右クリック [プロジェクト] -> [プロパティ] -> [ビルド イベント] -> [ビルド前のイベント])
$(WIX)bin\heat.exe" dir "$(EnviromentVariable)" -cg GroupVariable -gg -scom -sreg -sfrag -
srd -dr INSTALLLOCATION -var env.LogicPath -out "$(FragmentDir)\FileName.wxs
-gg
heat の実行時に Guid を生成します (上記のコマンドを実行したときのように)
-scom
「COMファイル」を取得しないでください
-sreg
「レジストリファイル」を取得しないでください
-スフラグ
「フラグメント」を取得しないでください
-srd
「ルートディレクトリ」を取得しないでください
dir
dir は、Heat がフォルダー内を検索することを示します
「$(環境変数)」
(プロジェクトを右クリックし、プロパティに移動) プロジェクトのプロパティ -> プリプロセッサ変数を定義するビルド セクションのプリプロセッサ変数に追加する変数の名前 (Visual Studio 2010 を想定)
例: 環境変数=C:\Project\bin\Debug;二重引用符はありませんが、セミコロンで終わります
-cg グループ変数
作成されたフラグメントからメインの wxs ファイルに参照される ComponentGroup
FragmentDir
出力 wxs フラグメントが格納されるフラグメント ディレクトリ
ファイル名.wxs
ファイルの名前
ここに完全なチュートリアルがあります。
COMオブジェクトを含む:
heat
ほとんどの(すべてではないにしても)レジストリエントリとそれらに必要なその他の構成を生成します。喜んで!
管理対象COMオブジェクト(別名、.NETまたはC#COMオブジェクト)を含む
管理対象COMオブジェクトで使用heat
すると、ほぼ完全なwixドキュメントが得られます。
GACで利用可能なライブラリが必要ない場合(つまり、グローバルに利用可能:ほとんどの場合、.NETアセンブリではこれは必要ありません-意図されていない場合は、この時点で何か間違ったことをしている可能性があります。共有ライブラリ)CodeBase
に設定するレジストリキーを必ず更新する必要があります[#ComponentName]
。それをGACにインストールすることを計画している場合(たとえば、誰もが使用したいと思う新しい素晴らしい共通ライブラリを作成した場合)、このエントリを削除し、File
要素に2つの新しい属性を追加する必要があります:Assembly
とKeyPath
。アセンブリは「.net」KeyPath
に設定し、「yes」に設定する必要があります。
ただし、一部の環境(特に、スクリプト言語などのマネージメモリを備えたもの)では、Typelibにもアクセスする必要があります。 typelibで実行heat
し、それを含めるようにしてください。 heat
必要なすべてのレジストリキーを生成します。それはどれくらいクールですか?
へのインストールC:\ProductName
一部のアプリケーションは、C:\ProductName
または類似のものにインストールする必要がありますが、ネットの例の 99.9% (100% ではない場合) は にインストールされC:\Program Files\CompanyName\ProductName
ます。
次のコードを使用して、プロパティをドライブTARGETDIR
のルートに設定できます ( WiX-users リストから取得)。C:
<CustomAction Id="AssignTargetDir" Property="TARGETDIR" Value="C:\" Execute="firstSequence" />
<InstallUISequence>
<Custom Action="AssignTargetDir" Before="CostInitialize">TARGETDIR=""</Custom>
</InstallUISequence>
<InstallExecuteSequence>
<Custom Action="AssignTargetDir" Before="CostInitialize">TARGETDIR=""</Custom>
</InstallExecuteSequence>
注:デフォルトでは、 は!を指してTARGETDIR
いません。C:\
むしろ、空き容量が最も多いドライブROOTDRIVE
のルートを指すものを指します(ここを参照) - これは必ずしもドライブではありません。別のハード ドライブ、パーティション、または USB ドライブがある可能性があります。C:
次に、タグの下のどこかに<Product ...>
、通常どおり次のディレクトリ タグが必要です。
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="APPLICATIONFOLDER" Name="$(var.ProductName)">
<!-- your content goes here... -->
</Directory>
</Directory>
Votiveなしでマネージコード(C#)で記述されたWIXのカスタムアクションを作成する
RobM の特別な「Remember Property」パターンの使用
http://robmensching.com/blog/posts/2010/5/2/The-WiX-toolsets-Remember-Property-pattern
環境変数
Wxs ドキュメントを wixobj コードにコンパイルする場合、環境変数を使用してさまざまな情報を確認できます。たとえば、プロジェクトに含めるファイルを変更したいとします。MSI をビルドする直前に設定する RELEASE_MODE という環境変数があるとします (スクリプトを使用しても手動でも問題ありません)。wix ソースでは、次のようなことができます。
<define FILESOURCE = c:\source\output\bin\$(env.RELEASE_MODE) >
コードの後半で、その場で使用して wxs ドキュメントをオンザフライで変更します。たとえば、次のようになります。
<Icon Id="myicon.ico" SourceFile="$(var.FILESOURCE)" />
ダイアログの編集
ダイアログを編集する優れた機能の 1 つは、バージョン 4.0.1.7090 (またはそれ以降) で SharpDevelop を使用することです。このツールを使用すると、スタンドアロン ダイアログ (InstallDirDlg.wxs などの WiX ソースからの wxs ファイル) を開き、デザイン ビューでプレビューおよび編集できます。
IIS enable32BitAppOnWin64 フラグの設定http://trycatchfail.com/blog/post/WiX-Snippet-change-enable32BitAppOnWin64.aspx
<InstallExecuteSequence>
<RemoveExistingProducts After="InstallFinalize" />
<Custom Action="ConfigureAppPool" After="InstallFinalize" >
<![CDATA[NOT Installed AND VersionNT64 >= 600]]>
</Custom>
</InstallExecuteSequence>
<CustomAction Id="ConfigureAppPool" Return="check" Directory="TARGETDIR" ExeCommand="[SystemFolder]inetsrv\appcmd set apppool /apppool.name:[APPPOOLNAME] /enable32BitAppOnWin64:false" />
インストールパッケージをデプロイする前に、私は常にそのコンテンツを制御します。
コマンドラインで(Terrencesの投稿によると)コマンドラインを開いて次のように入力するだけです。
msiexec /a Package.msi /qb TARGETDIR="%CD%\Extract" /l*vx "%CD\install.log%"
これにより、パッケージの内容が現在のパスでサブディレクトリ'Extract'に抽出されます。
「インストールの準備はできましたか?」を変更します。ダイアログ (別名 VerifyReadyDlg) を使用して、行われた選択の概要を提供します。
次のようになります:
alt テキスト http://i46.tinypic.com/s4th7t.jpg
Javascript CustomAction でこれを行います。
Javascript コード:
// http://msdn.microsoft.com/en-us/library/aa372516(VS.85).aspx
var MsiViewModify =
{
Refresh : 0,
Insert : 1,
Update : 2,
Assign : 3,
Replace : 4,
Merge : 5,
Delete : 6,
InsertTemporary : 7, // cannot permanently modify the MSI during install
Validate : 8,
ValidateNew : 9,
ValidateField : 10,
ValidateDelete : 11
};
// http://msdn.microsoft.com/en-us/library/sfw6660x(VS.85).aspx
var Buttons =
{
OkOnly : 0,
OkCancel : 1,
AbortRetryIgnore : 2,
YesNoCancel : 3
};
var Icons=
{
Critical : 16,
Question : 32,
Exclamation : 48,
Information : 64
}
var MsgKind =
{
Error : 0x01000000,
Warning : 0x02000000,
User : 0x03000000,
Log : 0x04000000
};
// http://msdn.microsoft.com/en-us/library/aa371254(VS.85).aspx
var MsiActionStatus =
{
None : 0,
Ok : 1, // success
Cancel : 2,
Abort : 3,
Retry : 4, // aka suspend?
Ignore : 5 // skip remaining actions; this is not an error.
};
function UpdateReadyDialog_CA(sitename)
{
try
{
// can retrieve properties from the install session like this:
var selectedWebSiteId = Session.Property("MSI_PROPERTY_HERE");
// can retrieve requested feature install state like this:
var fInstallRequested = Session.FeatureRequestState("F.FeatureName");
var text1 = "This is line 1 of text in the VerifyReadyDlg";
var text2 = "This is the second line of custom text";
var controlView = Session.Database.OpenView("SELECT * FROM Control");
controlView.Execute();
var rec = Session.Installer.CreateRecord(12);
rec.StringData(1) = "VerifyReadyDlg"; // Dialog_
rec.StringData(2) = "CustomVerifyText1"; // Control - can be any name
rec.StringData(3) = "Text"; // Type
rec.IntegerData(4) = 25; // X
rec.IntegerData(5) = 60; // Y
rec.IntegerData(6) = 320; // Width
rec.IntegerData(7) = 85; // Height
rec.IntegerData(8) = 2; // Attributes
rec.StringData(9) = ""; // Property
rec.StringData(10) = vText1; // Text
rec.StringData(11) = ""; // Control_Next
rec.StringData(12) = ""; // Help
controlView.Modify(MsiViewModify.InsertTemporary, rec);
rec = Session.Installer.CreateRecord(12);
rec.StringData(1) = "VerifyReadyDlg"; // Dialog_
rec.StringData(2) = "CustomVerifyText2"; // Control - any unique name
rec.StringData(3) = "Text"; // Type
rec.IntegerData(4) = 25; // X
rec.IntegerData(5) = 160; // Y
rec.IntegerData(6) = 320; // Width
rec.IntegerData(7) = 65; // Height
rec.IntegerData(8) = 2; // Attributes
rec.StringData(9) = ""; // Property
rec.StringData(10) = text2; // Text
rec.StringData(11) = ""; // Control_Next
rec.StringData(12) = ""; // Help
controlView.Modify(MsiViewModify.InsertTemporary, rec);
controlView.Close();
}
catch (exc1)
{
Session.Property("CA_EXCEPTION") = exc1.message ;
LogException("UpdatePropsWithSelectedWebSite", exc1);
return MsiActionStatus.Abort;
}
return MsiActionStatus.Ok;
}
function LogException(loc, exc)
{
var record = Session.Installer.CreateRecord(0);
record.StringData(0) = "Exception {" + loc + "}: " + exc.number + " : " + exc.message;
Session.Message(MsgKind.Error + Icons.Critical + Buttons.btnOkOnly, record);
}
Javascript CA を宣言します。
<Fragment>
<Binary Id="IisScript_CA" SourceFile="CustomActions.js" />
<CustomAction Id="CA.UpdateReadyDialog"
BinaryKey="IisScript_CA"
JScriptCall="UpdateReadyDialog_CA"
Execute="immediate"
Return="check" />
</Fragment>
CA をボタンにアタッチします。この例では、CustomizeDlg から [次へ] をクリックすると、CA が起動されます。
<UI ...>
<Publish Dialog="CustomizeDlg" Control="Next" Event="DoAction"
Value="CA.UpdateReadyDialog" Order="1"/>
</UI>
関連する SO の質問:実行時に、VerifyReadyDlg に表示されるテキストを設定するにはどうすればよいですか?
GUI の最初の画面のどこかに (小さい) 製品バージョンを表示します。人々は毎回正しいバージョンを選択する際に間違いを犯す傾向があるため. (そして、私たち開発者は年齢を探し続けます..)
さまざまな環境の構成で変換 (.mst ファイル) も生成するように TFSBuild をセットアップしました。(デプロイする必要があるすべての環境を把握しています)。
Grant Holliday による元のウェブログ投稿がダウンしているため、その内容をコピーしてここに貼り付けます。
XML から MSI 変換ファイルを生成する MSBuild タスク 2008 年 3 月 11 日
前回の投稿では、MSI Transform (*.mst) ファイルを使用して、環境固有の構成設定を一般的な MSI パッケージから分離する方法について説明しました。
これにより構成に一定の柔軟性がもたらされますが、変換ファイルには次の 2 つの欠点があります。
幸いなことに、Microsoft Windows Installer Object Library (c:windowssystem32msi.dll) を使用して、MSI「データベース」を開き、変換ファイルを作成できます。
VbScript を使用してこれを実現する方法を示してくれた Alex Shevchuk – From MSI to WiX – Part 7 – Customizing installation using Transforms に再度謝意を表します。基本的に、私が行ったのは Alex の例を参考にして、Interop.WindowsInstaller.dll を使用して MSBuild タスクを実装したことだけです。MSBuild タスク
ソース コードとサンプルの transforms.xml をここからダウンロードしてください (~7Kb 圧縮された VS2008 ソリューション)
独自のフラグメント内に個別にパッチを適用できるコンポーネントを配置する
フラグメントにコンポーネントを含める場合は、そのフラグメントにすべてのコンポーネントを含める必要があるということは、製品インストーラーとパッチの作成の両方に当てはまります。インストーラーをビルドする場合、コンポーネントの参照が欠落していると、light.exe からリンク エラーが発生します。ただし、パッチを作成するときに、単一のコンポーネント参照をフラグメントに含めると、そのフラグメントから変更されたすべてのコンポーネントがパッチに表示されます。
このような:
<Fragment>
<DirectoryRef Id="SampleProductFolder">
<Component Id="SampleComponent1" Guid="{C28843DA-EF08-41CC-BA75-D2B99D8A1983}" DiskId="1">
<File Id="SampleFile1" Source=".\$(var.Version)f\Sample1.txt" />
</Component>
</DirectoryRef>
</Fragment>
<Fragment>
<DirectoryRef Id="SampleProductFolder">
<Component Id="SampleComponent2" Guid="{6CEA5599-E7B0-4D65-93AA-0F2F64402B22}" DiskId="1">
<File Id="SampleFile2" Source=".\$(var.Version)f\Sample2.txt" />
</Component>
</DirectoryRef>
</Fragment>
<Fragment>
<DirectoryRef Id="SampleProductFolder">
<Component Id="SampleComponent3" Guid="{4030BAC9-FAB3-426B-8D1E-DC1E2F72C2FC}" DiskId="1">
<File Id="SampleFile3" Source=".\$(var.Version)f\Sample3.txt" />
</Component>
</DirectoryRef>
</Fragment>
これの代わりに:
<Fragment>
<DirectoryRef Id="SampleProductFolder">
<Component Id="SampleComponent1" Guid="{C28843DA-EF08-41CC-BA75-D2B99D8A1983}" DiskId="1">
<File Id="SampleFile1" Source=".\$(var.Version)\Sample1.txt" />
</Component>
<Component Id="SampleComponent2" Guid="{6CEA5599-E7B0-4D65-93AA-0F2F64402B22}" DiskId="1">
<File Id="SampleFile2" Source=".\$(var.Version)\Sample2.txt" />
</Component>
<Component Id="SampleComponent3" Guid="{4030BAC9-FAB3-426B-8D1E-DC1E2F72C2FC}" DiskId="1">
<File Id="SampleFile3" Source=".\$(var.Version)\Sample3.txt" />
</Component>
</DirectoryRef>
</Fragment>
また、WiX.chm ヘルプ ファイルの「純粋に WiX を使用する」トピックを使用してパッチを適用する場合は、次の手順を使用してパッチを生成します。
torch.exe -p -xi 1.0\product.wixpdb 1.1\product.wixpdb -out patch\diff.wixmst
candle.exe patch.wxs
light.exe patch.wixobj -out patch\patch.wixmsp
pyro.exe patch\patch.wixmsp -out patch\patch.msp -t RTM patch\diff.wixmst
個別のフラグメントのコンポーネントを使用してビルドされた product.wixpdb の 1.1 バージョンを持っているだけでは十分ではありません。そのため、出荷前に製品を正しく分割してください。
1) Wix ソース コードをコンパイルするとき、light.exe はコマンド ラインで WixUIExtension.dll を参照する必要があります。これには、コマンド ライン スイッチ -ext を使用します。
2) WixUIExtension.dll への参照を追加するときにプロジェクトがコンパイルに失敗する場合、これはダイアログ ID の衝突が原因である可能性が最も高いです。つまり、プロジェクトが WixUIExtension.dll のいくつかの標準ダイアログと同じダイアログの ID を使用していました。ダイアログに異なる ID を与えます。これは非常に一般的な問題です。
3) ライセンス ダイアログには、ID "LicenseText" の ScrollableText コントロールが必要です。Wix は印刷時に正確にこのコントロール名を検索します。
<Control Id="LicenseText" Type="ScrollableText" X="20" Y="60" Width="330" Height="160" Sunken="yes" TabSkip="no">
<Text SourceFile="License.rtf" />
</Control>
カスタムアクションを参照するプッシュボタン
<Control Type="PushButton" Id="PrintButton" Width="57" Height="17" X="19" Y="244" Text="Print">
<Publish Event="DoAction" Value="PrintEula">1</Publish>
</Control>
4) Id="PrintEula" で CustomAction を次のように定義します。
<CustomAction Id="PrintEula" BinaryKey="WixUIWixca" DllEntry="PrintEula" Return="ignore" Execute="immediate" />
注: BinaryKey は、Wix2.0 と比較して Wix3.0 では異なり、正確に「WixUIWixca」でなければなりません (大文字と小文字が区別されます)。
ユーザーがボタンを押すと、標準のプリンターの選択ダイアログが表示され、そこから印刷できるようになります。
ORCA の代わりに、MSI テーブルを表示するための優れたツールであるInstEdを使用します。また、Transform -> Compare To... によって 2 つのパッケージを比較する機能もあります。
さらに、機能が追加されたPlus バージョンもご利用いただけます。しかし、無料版も Orca の優れた代替手段を提供します。
注意: このフラグメントは基本的にREGASM Assembly.dll /codebaseと同じです。
このサンプルではいくつかのことが行われているので、ここにコードを示します。後で説明します...
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<?include $(sys.CURRENTDIR)\Config.wxi?>
<?if $(var.Win64) ?>
<?define CLSIDRoots = "CLSID;Wow6432Node\CLSID"?>
<?else ?>
<?define CLSIDRoots = "CLSID"?>
<?endif?>
<!-- ASCOM Driver Assembly with related COM registrations -->
<Fragment>
<DirectoryRef Id="INSTALLLOCATION" />
</Fragment>
<Fragment>
<ComponentGroup Id="cgAscomDriver">
<Component Id="cmpAscomDriver" Directory="INSTALLLOCATION" Guid="{0267031F-991D-4D88-A748-00EC6604171E}">
<File Id="filDriverAssembly" Source="$(var.TiGra.Astronomy.AWRDriveSystem.TargetPath)" KeyPath="yes" Vital="yes" Assembly=".net" AssemblyApplication="filDriverAssembly" />
<RegistryKey Root="HKCR" Key="$(var.DriverId)" Action="createAndRemoveOnUninstall">
<RegistryValue Type="string" Value="$(var.DriverTypeName)"/>
<RegistryKey Key="CLSID">
<RegistryValue Type="string" Value="$(var.DriverGuid)" />
</RegistryKey>
</RegistryKey>
<?foreach CLSID in $(var.CLSIDRoots) ?>
<RegistryKey Root="HKCR" Key="$(var.CLSID)" Action="none">
<RegistryKey Key="$(var.DriverGuid)" Action="createAndRemoveOnUninstall">
<RegistryValue Type="string" Value="$(var.DriverTypeName)"/>
<RegistryKey Key="InprocServer32">
<RegistryValue Type="string" Value="mscoree.dll" />
<RegistryValue Type="string" Name="ThreadingModel" Value="Both"/>
<RegistryValue Type="string" Name="Class" Value="$(var.DriverTypeName)"/>
<RegistryValue Type="string" Name="Assembly" Value="!(bind.assemblyFullname.filDriverAssembly)" />
<RegistryValue Type="string" Name="RuntimeVersion" Value="v2.0.50727"/>
<RegistryValue Type="string" Name="CodeBase" Value="file:///[#filDriverAssembly]" />
<RegistryKey Key="!(bind.fileVersion.filDriverAssembly)" >
<RegistryValue Type="string" Name="Class" Value="$(var.DriverTypeName)"/>
<RegistryValue Type="string" Name="Assembly" Value="!(bind.assemblyFullname.filDriverAssembly)" />
<RegistryValue Type="string" Name="RuntimeVersion" Value="v2.0.50727"/>
<RegistryValue Type="string" Name="CodeBase" Value="file:///[#filDriverAssembly]" />
</RegistryKey>
</RegistryKey>
<RegistryKey Key="ProgId" Action="createAndRemoveOnUninstall">
<RegistryValue Type="string" Value="$(var.DriverId)" />
</RegistryKey>
<RegistryKey Key="Implemented Categories" Action="createAndRemoveOnUninstall" >
<RegistryKey Key="{62C8FE65-4EBB-45e7-B440-6E39B2CDBF29}" Action="createAndRemoveOnUninstall" />
</RegistryKey>
</RegistryKey>
</RegistryKey>
<?endforeach?>
</Component>
</ComponentGroup>
</Fragment>
</Wix>
ご参考までに、これは実際にはASCOM Telescope Driver用です。
まず、上記のアドバイスに従って、別のファイルにいくつかの platforma 変数を作成しました。XML に散らばっている変数を見ることができます。
上部近くの if-then-else 部分は、x86 と x64 の互換性を扱います。私のアセンブリは「すべての CPU」を対象としているため、x64 システムでは、64 ビット レジストリで 1 回、32 ビットWow6432Node
領域で 1 回、2 回登録する必要があります。if-then-else はこれを設定します。値はforeach
後でループで使用されます。この方法では、レジストリ キーを 1 回作成するだけで済みます (DRY 原則)。
file 要素は、インストールおよび登録される実際のアセンブリ dll を指定します。
<File Id="filDriverAssembly" Source="$(var.TiGra.Astronomy.AWRDriveSystem.TargetPath)" KeyPath="yes" Vital="yes" Assembly=".net" AssemblyApplication="filDriverAssembly" />
革新的なAssembly=".net"
ものは何もありませんが、この属性だけでアセンブリが GAC に入れられることに注意してください。これは私が望んでいたことではありません。属性を使用しAssemblyApplication
てそれ自体を指すことは、Wix がファイルを GAC に入れるのを止める方法にすぎません。Wix はそれが .net アセンブリであることを認識したので、XML 内で特定のバインダー変数 (!(bind.assemblyFullname.filDriverAssembly)
アセンブリの完全な名前を取得する など) を使用できます。
プロパティを設定してDISABLEADVTSHORTCUTS
、インストーラーで宣伝されているすべてのショートカットを強制的に通常のショートカットにします。キーパスとして使用するダミーのレジストリ キーを含める必要はありません。
<Property Id="DISABLEADVTSHORTCUTS" Value="1"/>
Windows Installer 4.0 以上が必要だと思います。
変数を設定するカスタム アクションを持つ UI を作成します。UI は、カスタム アクションで設定された変数に基づいて次のボタン (または同様のボタン) を無効/有効にします。
あなたが思っているほど単純ではなく、どこにも文書化されていないだけで、それほど難しくもありません!
インストールでアンインストールまたは再インストールが許可されず、ロールバックされない場合に強制再インストールを実行する。
何らかの理由でアンインストールされていないインストールを上書きするために使用される VBscript スクリプト..
Dim objShell
set objShell = wscript.createObject("wscript.shell")
iReturn = objShell.Run("CMD /K MsiExec.exe /I ""C:\Users\TheUser\Documents\Visual Studio 2010\Projects\InstallationTarget\HelloInstaller\bin\Debug\HelloInstaller.msi"" REINSTALLMODE=vomus REINSTALL=ALL",,True)
大規模な Web プロジェクトで、展開されたファイルの数が MSI (またはマージ モジュール) に組み込まれたファイルの数と一致することを確認するのに役立つ方法を次に示します。メイン アプリケーション (まだ開発中) に対してカスタム MSBuild タスクを実行したところ、不足しているファイル (主に画像) がかなり検出されましたが、いくつかの JavaScript ファイルがすり抜けていました。
このアプローチ (WiX プロジェクトの AfterBuild ターゲットにフックすることによって MSI のファイル テーブルを覗く) は、予想されるファイルの完全なリストにアクセスできる他のアプリケーション タイプで機能する可能性があります。
それは素晴らしい構造ですが、私の経験に基づいて、これらの条件にどのように対処するのだろうかと思います:
A. あなたのインストールはすべて同じ場所にあるように見えます。ユーザーが一度に 3 つのバージョンすべてをインストールする必要がある場合、プロセスでこれが許可されます。トリガーしているすべての実行可能ファイルのバージョンを明確に識別できますか?
B. TEST および/または TRAINING に存在するが LIVE にはまだ存在しない新しいファイルをどのように処理しますか?
正しく表示されるように ProgressDlg を修正します。
インストーラーのフォント サイズを 8 から 10 に増やして、フォントをより人間的で、高解像度モニターで使用できるスケールにしました。私はこの XML マジックでこれを行います:
<UI Id="MyCustomUI">
<TextStyle Id="WixUI_Font_Normal" FaceName="Tahoma" Size="10" />
<TextStyle Id="WixUI_Font_Big" FaceName="Tahoma" Size="12" />
<TextStyle Id="WixUI_Font_Bigger" FaceName="Tahoma" Size="14" />
<TextStyle Id="WixUI_Font_Title" FaceName="Tahoma" Size="12" Bold="yes" />
<Property Id="DefaultUIFont" Value="WixUI_Font_Normal" />
</UI>
しかし、これは ProgressDlg が適切に表示されなくなったことを意味します。これは、インストールの進行状況を最後に表示するものです。ActionText がクリップされるため、g や j などの文字のディセンダーは表示されません。後処理 Javascript で、進行状況ダイアログのさまざまなコントロールのサイズと位置を調整して、これを修正します。MSI を生成した後、次のスクリプトを実行します。
var msiOpenDatabaseModeTransact = 1;
var filespec = WScript.Arguments(0);
var installer = new ActiveXObject("WindowsInstaller.Installer");
var database = installer.OpenDatabase(filespec, msiOpenDatabaseModeTransact);
// The text on the exit dialog is too close to the title. This
// step moves the text down from Y=70 to Y=90, about one line.
sql = "UPDATE `Control` SET `Control`.`Y` = '90' " +
"WHERE `Control`.`Dialog_`='ExitDialog' AND `Control`.`Control`='Description'";
view = database.OpenView(sql);
view.Execute();
view.Close();
// The progressbar is too close to the status text on the Progress dialog.
// This step moves the progressbar down from Y=115 to Y=118, about 1/3 line.
sql = "UPDATE `Control` SET `Control`.`Y` = '118' " +
"WHERE `Control`.`Dialog_`='ProgressDlg' AND `Control`.`Control`='ProgressBar'";
view = database.OpenView(sql);
view.Execute();
view.Close();
// The StatusLabel and ActionText controls are too short on the Progress dialog,
// which means the bottom of the text is cut off. This step
// increases the height from 10 to 16.
sql = "UPDATE `Control` SET `Control`.`Height` = '16' " +
"WHERE `Control`.`Dialog_`='ProgressDlg' AND `Control`.`Control`='StatusLabel'";
view = database.OpenView(sql);
view.Execute();
view.Close();
sql = "UPDATE `Control` SET `Control`.`Height` = '16' " +
"WHERE `Control`.`Dialog_`='ProgressDlg' AND `Control`.`Control`='ActionText'";
view = database.OpenView(sql);
view.Execute();
view.Close();
database.Commit();