我们已经使用WiX有一段时间了,尽管人们对它的易用性有一些抱怨,但它运行得相当不错。我想要的是有用的建议:
设置WiX项目(布局、引用、文件模式) 将WiX集成到解决方案中,并构建/发布流程 为新的安装和升级配置安装程序 任何你想分享的好的WiX技巧
我们已经使用WiX有一段时间了,尽管人们对它的易用性有一些抱怨,但它运行得相当不错。我想要的是有用的建议:
设置WiX项目(布局、引用、文件模式) 将WiX集成到解决方案中,并构建/发布流程 为新的安装和升级配置安装程序 任何你想分享的好的WiX技巧
当前回答
Peter Tate已经展示了如何在单独的wix片段中定义可重用的ComponentGroup定义。一些与此相关的额外技巧:
目录别名
组件组片段不需要知道主产品wxs定义的目录。在你的组件组片段中,你可以这样描述一个文件夹:
<DirectoryRef Id="component1InstallFolder">
...
</DirectoryRef>
然后主产品可以别名它的一个目录(例如。"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”,因为它是应用程序的直接依赖项,那么它将自动拉入组件组“a”,即使应用程序作者从未意识到它是“B”的依赖项。只要你没有任何循环依赖,它就“可以工作”。
Reusable wixlib
如果您使用lit.exe将大池-o-可重用组件编译为可重用的wixlib,则上述依赖关系图的想法效果最好。在创建应用程序设置时,可以像引用wixobj文件一样引用这个wixlib。exe链接器将自动消除没有被主产品wxs文件“拉入”的任何片段。
其他回答
奇妙的问题。我希望看到一些最佳实践的展示。
我有很多要分发的文件,所以我将项目设置为几个wxs源文件。
我有一个顶级源文件,我称之为产品。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标签中引用的componentgroup的片段组成。我的项目包含我分发的文件的一个很好的逻辑分组
<Fragment>
<ComponentGroup>
<ComponentRef>
....
</ComponentGroup>
<DirectoryRef>
<Component... for each file
....
</DirectoryRef>
</Fragment>
这并不完美,我的OO蜘蛛感觉有点刺痛,因为片段必须引用产品中的名称。wxs文件(例如DirectoryRef),但我发现维护一个大的源文件更容易。
我很想听到关于这一点的评论,或者如果有人有任何好的建议!
从Wix3.0及更高版本打印EULA
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>
以及指向自定义动作的PushButton
<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在Wix3.0和Wix2.0中是不同的,必须准确地为“WixUIWixca”(区分大小写)。
当用户按下按钮时,他/她将出现标准的选择打印机对话框,并能够从那里打印。
使用RobM特殊的“记住属性”模式
http://robmensching.com/blog/posts/2010/5/2/The-WiX-toolsets-Remember-Property-pattern
在退出对话框中添加一个复选框来启动应用程序或帮助文件。
...
<!-- 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>
如果这样做,“标准”外观就不太正确。复选框总是灰色背景,而对话框是白色的:
可选文字 http://www.dizzymonkeydesign.com/blog/misc/adding-and-customizing-dlgs-in-wix-3/images/exit_dlg_1.gif
解决这个问题的一种方法是指定您自己的自定义ExitDialog,使用一个位置不同的复选框。这是可行的,但似乎很多工作只是改变一个控件的颜色。另一种解决相同问题的方法是对生成的MSI进行后处理,以更改控制表中特定CheckBox控件的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();
在MSI生成后(从light.exe)运行这段代码作为命令行脚本(使用cscript.exe)将产生一个看起来更专业的ExitDialog:
可选文字 http://www.dizzymonkeydesign.com/blog/misc/adding-and-customizing-dlgs-in-wix-3/images/exit_dlg_2.gif
Keep variables in a separate wxi include file. Enables re-use, variables are faster to find and (if needed) allows for easier manipulation by an external tool. Define Platform variables for x86 and x64 builds <!-- 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 ?> Store the installation location in the registry, enabling upgrades to find the correct location. For example, if a user sets custom install directory. <Property Id="INSTALLLOCATION"> <RegistrySearch Id="RegistrySearch" Type="raw" Root="HKLM" Win64="$(var.Win64)" Key="Software\Company\Product" Name="InstallLocation" /> </Property> Note: WiX guru Rob Mensching has posted an excellent blog entry which goes into more detail and fixes an edge case when properties are set from the command line. Examples using 1. 2. and 3. <?include $(sys.CURRENTDIR)\Config.wxi?> <Product ... > <Package InstallerVersion="200" InstallPrivileges="elevated" InstallScope="perMachine" Platform="$(var.Platform)" Compressed="yes" Description="$(var.ProductName)" /> and <Directory Id="TARGETDIR" Name="SourceDir"> <Directory Id="$(var.PlatformProgramFilesFolder)"> <Directory Id="INSTALLLOCATION" Name="$(var.InstallName)"> The simplest approach is always do major upgrades, since it allows both new installs and upgrades in the single MSI. UpgradeCode is fixed to a unique Guid and will never change, unless we don't want to upgrade existing product. Note: In WiX 3.5 there is a new MajorUpgrade element which makes life even easier! Creating an icon in Add/Remove Programs <Icon Id="Company.ico" SourceFile="..\Tools\Company\Images\Company.ico" /> <Property Id="ARPPRODUCTICON" Value="Company.ico" /> <Property Id="ARPHELPLINK" Value="http://www.example.com/" /> On release builds we version our installers, copying the msi file to a deployment directory. An example of this using a wixproj target called from AfterBuild target: <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> Use heat to harvest files with wildcard (*) Guid. Useful if you want to reuse WXS files across multiple projects (see my answer on multiple versions of the same product). For example, this batch file automatically harvests RoboHelp output. @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 There's a bit going on, robocopy is stripping out Subversion working copy metadata before harvesting; the -dr root directory reference is set to our installation location rather than default TARGETDIR; -var is used to create a variable to specify the source directory (web deployment output). Easy way to include the product version in the welcome dialog title by using Strings.wxl for localization. (Credit: saschabeaumont. Added as this great tip is hidden in a comment) <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> Save yourself some pain and follow Wim Coehen's advice of one component per file. This also allows you to leave out (or wild-card *) the component GUID. Rob Mensching has a neat way to quickly track down problems in MSI log files by searching for value 3. Note the comments regarding internationalization. When adding conditional features, it's more intuitive to set the default feature level to 0 (disabled) and then set the condition level to your desired value. If you set the default feature level >= 1, the condition level has to be 0 to disable it, meaning the condition logic has to be the opposite to what you'd expect, which can be confusing :) <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>