我们已经使用WiX有一段时间了,尽管人们对它的易用性有一些抱怨,但它运行得相当不错。我想要的是有用的建议:

设置WiX项目(布局、引用、文件模式) 将WiX集成到解决方案中,并构建/发布流程 为新的安装和升级配置安装程序 任何你想分享的好的WiX技巧


当前回答

包括COM对象:

Heat生成所有(如果不是全部)注册表项和它们所需的其他配置。喜乐!

包括托管COM对象(又名。net或c# COM对象)

在托管COM对象上使用heat将为您提供一个几乎完整的wix文档。

If you don't need the library available in the GAC (ie, globally available: MOST of the time you do not need this with your .NET assemblies anyway - you've probably done something wrong at this point if it's not intended to be a shared library) you will want to make sure to update the CodeBase registry key to be set to [#ComponentName]. If you ARE planning on installing it to the GAC (eg, you've made some new awesome common library that everyone will want to use) you must remove this entry, and add two new attributes to the File element: Assembly and KeyPath. Assembly should be set to ".net" and KeyPath should be set to "yes".

然而,某些环境(特别是带有托管内存的环境,如脚本语言)也需要访问Typelib。确保在你的打字单上运行热度并包括它。Heat将生成所有需要的注册表项。多酷啊!

其他回答

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>

创建现场、测试、培训……使用相同源文件的版本。

简而言之:为每个安装程序创建唯一的UpgradeCode,并自动为每个安装程序定义每个Guid的第一个字符,其余31个字符是唯一的。

先决条件

MSBuild社区任务

假设

WiX变量用于定义UpgradeCode、ProductName、InstallName。 您已经有了一个工作的安装程序。你不动手我是不会动手的。 所有组件都保存在一个文件(Components.wxs)中。如果你有多个文件,这个过程是有效的,只是有更多的工作要做。

目录结构

设置。图书馆 所有wxs文件(组件,功能,UI对话框,…) Common.Config.wxi (ProductCode="*", ProductVersion, PlatformProgramFilesFolder,…) 设置。生活(wixproj) 链接所有安装程序。“添加为链接”(Visual Studio中“添加”按钮旁边的向下箭头按钮) 配置。wxi(有唯一的UpgradeCode, ProductName, InstallName,…) 设置。测试,… 按live但配置。wxi为测试环境配置。

过程

Create Setup.Library directory and move all your wxs and wxi files (except Config.wxi) from existing project. Create Setup.Live, Setup.Test, etc as per normal wixproj. Add BeforeBuild target in wixproj in Setup.Live, etc to perform MSBuild Community Task FileUpdate to modify Guids (I used A for Live, B for Test and C for training) Add AfterBuild target to revert Components.wxs Guids back to 0. Verify with Orca that each component in each MSI has the modified guid. Verify that original guids are restored. Verify that each MSI is installing (and upgrading) correct product and location.

例子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中去掉(相当于*),每个组件使用一个文件,并将文件设置为小键盘。这样就不需要调用如下所示的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=&quot;([a-f]|[A-F]|\d)" ReplacementText="Guid=&quot;A" />
</Target>
<!-- Revert the first character of every Guid back to initial value -->
<Target Name="RevertComponentsGuids">
  <FileUpdate Files="..\Setup.Library\Components.wxs" Regex="Guid=&quot;([a-f]|[A-F]|\d)" ReplacementText="Guid=&quot;0" />
</Target>

最终的想法

This process should also work for creating different installers for different merge modules (Live, Test, ... as features) for the same installer. I went with different installers as it seemed a safer option, there is more risk that someone might upgrade Live instead of Training if they're on the same box and you just use features for the different merge modules. If you use your MSI to perform upgrades as well as new installs i.e. the major upgrade only approach, and you save your installation location in the registry, remember to create a variable for the key name for each install. We also create variables in each Config.wxi to enable unique virtual directory names, application pools, database names, et cetera for each installer.

更新1:如果您为每个文件创建带有Guid="*"的组件,并将文件设置为小键盘,则自动生成组件Guid将不需要调用FileUpdate任务。

更新2:我们遇到的一个问题是,如果您没有自动生成组件Guid,并且构建失败,那么临时文件需要手动删除。

更新3:找到了一种方法来消除对svn的依赖:externals和临时文件创建。这使得构建过程更具弹性(如果您不能通配符您的guid,这是最好的选择),并且在灯光或蜡烛中构建失败时不那么脆弱。

更新4:WiX 3.0+支持使用实例转换的多实例,当然也值得一看。

修复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中,通过调整Progressdialog中各种控件的大小和位置来解决这个问题。生成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();

奇妙的问题。我希望看到一些最佳实践的展示。

我有很多要分发的文件,所以我将项目设置为几个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),但我发现维护一个大的源文件更容易。

我很想听到关于这一点的评论,或者如果有人有任何好的建议!

将所有id保存在单独的名称空间中

特性以f开头,例如:F. documentation, F. binaries, F. samplecode。 组件以c开头,例如:C. chmfile, C. releasenotes, C. licensefile, C. inifile, C. registry CustomActions是CA. Ex: CA. launchhelp, CA. updatereadydlg, CA. setpropertyx 文件为Fi。 目录是Di。 等等。

我发现这对追踪不同类别的各种本我非常有帮助。