WiX技巧和窍门


264

我们已经使用WiX已有一段时间了,尽管人们通常对易用性有所担忧,但它运行的还算不错。我在寻找关于以下方面的有用建议:

  • 设置WiX项目(布局,参考,文件模式)
  • 将WiX集成到解决方案中,并构建/发布流程
  • 配置安装程序以进行新安装和升级
  • 您想分享的任何出色的WiX骇客

看看gui4wix.codeplex.com
TarunG 2011年

10
封闭不是建设性的吗?我从问这个问题中学到了很多东西!从StackOverflow的一个小的一致性也将是不错...如stackoverflow.com/questions/550632/...
si618

15
它得到了203 Ups,足以证明它的用处。
TarunG 2012年

因此,问题必须有明确,正确的答案;开放式问题使人们问有关实际问题的问题从首页上掉下来。 常见问题 @Si .:该政策一直存在于AFAIK,但现在最好执行。这个问题已经快三年了。
Jim Dagg 2012年

吉姆(Jim)公平地说,这是一个开放性的问题,我想这取决于SO社区,但是我不得不说,鉴于我和其他人的看法,将其关闭不是建设性的似乎很奇怪。已发现此问题很有用(例如goo.gl/Zqp2X),并且与practical, answerable questions based on actual problems that you faceFAQ 的一部分非常吻合。
si618 2012年

Answers:


157
  1. 将变量保存在单独的wxi包含文件中。支持重用,变量查找更快,并且(如果需要)可以通过外部工具更轻松地进行操作。

  2. 为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 ?>
  3. 将安装位置存储在注册表中,以使升级能够找到正确的位置。例如,如果用户设置了自定义安装目录。

     <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)">
  4. 最简单的方法始终是进行重大升级,因为它允许在单个MSI中进行新安装和升级。UpgradeCode固定为唯一的Guid,并且永远不会更改,除非我们不想升级现有产品。

    注意:在WiX 3.5中,有一个新的MajorUpgrade元素,使生活变得更加轻松

  5. 在添加/删除程序中创建图标

    <Icon Id="Company.ico" SourceFile="..\Tools\Company\Images\Company.ico" />
    <Property Id="ARPPRODUCTICON" Value="Company.ico" />
    <Property Id="ARPHELPLINK" Value="http://www.example.com/" />
  6. 在发行版本中,我们对安装程序进行版本控制,将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>
  7. 使用通配符(*)引导加热文件。如果您想在多个项目中重用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部署输出)。

  8. 通过使用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>
  9. 减轻您的痛苦,并遵循Wim Coehen关于每个文件一个组件的建议。这也允许您省去(或通配*组件GUID

  10. Rob Mensching提供了一种巧妙的方法,可以通过搜索来快速跟踪MSI日志文件中的问题value 3。请注意有关国际化的评论。

  11. 添加条件要素时,将默认要素级别设置为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>

关于在“添加/删除程序”中添加图标,它正是我想要的。您将这三行放在哪里?+1绝对给力。
Everett

我倾向于将它们放在<Package>元素之后(显然在下面)。看一下有效性的模式 wix.sourceforge.net/manual-wix3/schema_index.htm
si618

+1,希望我能+100,这是我偶然发现的最有用的Wix信息。
蒂姆·隆

谢谢蒂姆!Rob Mensching,Bob Arson,Wim Coehen和其他人应该因分享知识而倍受赞誉。
si618 2011年

38

检查是否已安装IIS:

<Property Id="IIS_MAJOR_VERSION">
    <RegistrySearch Id="CheckIISVersion" Root="HKLM" Key="SOFTWARE\Microsoft\InetStp" Name="MajorVersion" Type="raw" />
</Property>

<Condition Message="IIS must be installed">
    Installed OR IIS_MAJOR_VERSION
</Condition>

检查是否在Vista +上安装了IIS 6元数据库兼容性:

<Property Id="IIS_METABASE_COMPAT">
    <RegistrySearch Id="CheckIISMetabase" Root="HKLM" Key="SOFTWARE\Microsoft\InetStp\Components" Name="ADSICompatibility" Type="raw" />
</Property>

<Condition Message="IIS 6 Metabase Compatibility feature must be installed">
    Installed OR ((VersionNT &lt; 600) OR IIS_METABASE_COMPAT)
</Condition>

34

将所有ID保留在单独的命名空间中

  • 功能以F. 示例开头:F.Documentation,F.Binaries,F.SampleCode。
  • 组件以C. Ex 开头:C.ChmFile,C.ReleaseNotes,C.LicenseFile,C.IniFile,C.Registry
  • CustomActions CA. 例如:CA.LaunchHelp,CA.UpdateReadyDlg,CA.SetPropertyX
  • 文件是 Fi.
  • 目录是 Di.
  • 等等。

我发现这对跟踪所有类别中的所有ID有很大帮助。


我不使用名称空间,而是附加ID。例如:ExamplesFeature,ChmFileComponent。我想我喜欢打字;-)
dvdvorle 2012年

25

很棒的问题。我很乐意看到一些最佳实践。

我分发了很多文件,因此我将项目设置为几个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文件由包含ComponentGroup的片段组成,这些片段在Product.wxs的Feature标签中引用。我的项目包含对我分发的文件的良好逻辑分组

<Fragment>
   <ComponentGroup>
     <ComponentRef>
     ....
    </ComponentGroup>
    <DirectoryRef>
      <Component... for each file
      .... 
    </DirectoryRef>
</Fragment>

这不是完美的,我的OO蜘蛛感觉有点麻木,因为这些片段必须引用Product.wxs文件中的名称(例如DirectoryRef),但是我发现维护单个大型源文件更容易。

我希望听到对此的评论,或者如果有人也有任何好的建议!


我们的设置也与此方法非常相似。很好,因为我们可以将等效的Products.wxs用作各种产品的基本设置。
si618

@Peter Tate:您的蜘蛛意识是正确的。请参阅我关于目录别名的答案。
Wim Coenen

我采用相同的方法:具有布局的Product.wxs是静态的,并且构建任务(heat.exe)生成我的Content.wxs文件
timvw 2010年

20

在退出对话框中添加一个复选框以启动应用程序或帮助文件。

...

<!-- 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控件的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();

在MSI生成之后(从light.exe)运行此代码作为命令行脚本(使用cscript.exe)将产生一个看起来更专业的ExitDialog:

替代文字http://www.dizzymonkeydesign.com/blog/misc/adding-and-customizing-dlgs-in-wix-3/images/exit_dlg_2.gif


哈!不是我的博客。我也读过。我在上面的文本中有一个指向博客条目的链接。但是他们做的和我做的不同。我更喜欢我的方式!!
Cheeso

1
感谢您的js,非常有帮助!有一两件事,我不得不改变在WXS是替换WIXUI_EXITDIALOGOPTIONALCHECKBOXWIXUI_EXITDIALOGOPTIONALCHECKBOX = 1 and NOT Installed<Publish>
亚历山大Kojevnikov

有没有一种方法可以使复选框默认为选中状态?
Alek Davis

默认情况下,要选中此框,我使用了此属性:<Property Id =“ WIXUI_EXITDIALOGOPTIONALCHECKBOX” Value =“ 1” />
Alek Davis 2010年

似乎是一个不错的解决方案,但是我该如何使用呢?有什么方法可以将js放入wixproj的<AfterBuild>元素中?还是因为您是指从命令行运行它,所以它作为构建后事件会更好,在这种情况下,Windows的js命令行解释器是什么?
vanmelle

18

使用相同的源文件创建Live,Test,Training等版本。

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

先决条件

假设条件

  • WiX变量用于定义UpgradeCode,ProductName,InstallName。
  • 您已经有一个可以使用的安装程序。除非您这样做,否则我不会尝试。
  • 您所有的组件都保存在一个文件(Components.wxs)中。如果您有多个文件,则此过程将起作用,仅要做更多的工作。

目录结构

  • 设置库
    • 所有wxs文件(组件,功能,UI对话框等)
    • Common.Config.wxi(ProductCode =“ *”,ProductVersion,PlatformProgramFilesFolder,...)
  • Setup.Live(wixproj)
    • 使用“添加现有文件”->“添加为链接”(Visual Studio中“添加”按钮旁边的小向下箭头按钮)链接所有Setup.Library文件。
    • Config.wxi(具有唯一的UpgradeCode,ProductName,InstallName等)
  • Setup.Test,...
    • 按照实时进行,但Config.wxi配置为测试环境。

处理

  • 创建Setup.Library目录,并从现有项目中移动所有wxs和wxi文件(Config.wxi除外)。
  • 按照常规wixproj创建Setup.Live,Setup.Test等。
  • 在Setup.Live等的wixproj中添加BeforeBuild目标,以执行MSBuild社区任务FileUpdate修改Guid(我使用A进行实时测试,B进行测试,C进行训练)
  • 添加AfterBuild目标以将Components.wxs向导恢复为0。
  • 与Orca一起验证每个MSI中的每个组件都具有修改过的GUID。
  • 验证原始指南是否已还原。
  • 验证每个MSI是否正在安装(和升级)正确的产品和位置。

示例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之外(相当于*),每个组件使用一个文件并将该文件设置为键路径。这消除了如下所示的调用ModifyComponentsGuidsRevertComponentsGuids目标的需要。但是,这可能对您的所有组件都不可行。

示例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>

最后的想法

  • 此过程还应该为同一安装程序的不同合并模块(实时,测试等功能)创建不同的安装程序。我选择了不同的安装程序,因为这似乎是一个更安全的选择,如果他们位于同一个盒子中,而您只是将功能用于不同的合并模块,那么有人可能会升级Live而不是Training,这会带来更大的风险。
  • 如果您使用MSI执行升级以及新安装,即仅进行主要升级的方法,并且将安装位置保存在注册表中,请记住为每次安装的键名创建一个变量。
  • 我们还在每个Config.wxi中创建变量,以为每个安装程序启用唯一的虚拟目录名称,应用程序池,数据库名称等。

更新1: 如果为每个文件创建带有Guid =“ *”的组件并将文件设置为键路径,则自动生成组件Guids无需调用FileUpdate任务。

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

更新3:找到了一种消除对svn:externals和临时文件创建的依赖的方法。这样可以使构建过程更具弹性(如果无法对Guid进行通配,则是最佳选择),而在灯光或烛光发生构建失败时则不那么脆弱。

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


+1以获取MSBuild社区任务参考,喜欢该软件包
BozoJoe 2010年

17

使用Msi诊断日志记录来获取详细的故障信息

msiexec /i Package.msi /l*v c:\Package.log

哪里

包.msi
是您包裹的名称,
c:\ Package.log
是您要输出日志的地方

Msi错误代码

维克斯介绍视频
哦,随机维克斯介绍视频特色的“威克斯先生”罗布Mensching是“概念上的大画面”有帮助的。


2
+1如果我们可以从Wix而不是命令行中启用日志记录,那就更好了。
si618 2010年

3
WiX可以。设置MsiLogging属性。仅Windows Installer 4.0+支持。
Rob Mensching 2010年

非常感谢“威克斯先生”。要检查出来。
Terrance

17

使用Javascript CustomActions,因为它们非常容易

人们曾经说过Javascript是用于MSI CustomActions的错误做法。给出的原因:难以调试,难以使其可靠。我不同意 调试起来并不难,当然也比C ++难。只是不同而已。我发现用Javascript编写CustomActions非常容易,比使用C ++容易得多。快多了。并且同样可靠。

有一个缺点:可以通过Orca提取Javascript CustomAction,而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函数。一个示例:我使用Javascript在IIS上进行WMI查询,以获取现有网站列表,可以在该网站上安装ISAPI筛选器。然后,此列表用于填充UI序列中稍后显示的列表框。一切都很容易。

在IIS7上,没有IIS的WMI提供程序,因此我使用了shell.Run()调用appcmd.exe 的方法来执行工作。简单。

相关问题:关于Javascript CustomActions


2
+1我发现DTF方法易于设置,但是javascript也可能有用。
2009年

12

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”是因为它是应用程序的直接依赖项,则即使应用程序作者从未意识到它是“ B”的依赖项,它也会自动拉入组件组“ A”。只要您没有任何循环依赖关系,它就“有效”。

可重用的wixlib

如果您使用lit.exe将big-pool-o-reusable-components编译成可重用的wixlib,则以上依赖关系图的想法将发挥最佳作用。创建应用程序设置时,可以像wixobj文件一样引用此wixlib。Candle.exe链接器将自动消除主产品wxs文件未“拉入”的所有片段。


12

我很惊讶没有人提到在构建过程中使用T4生成WXS文件。我是通过Henry Lee @ New Age Solutions得知的。

本质上,您创建了一个自定义MSBuild任务来执行T4模板,并且该模板在编译Wix项目之前就输出WXS。这使您能够(取决于实现方式)自动包括编译另一种解决方案所输出的所有程序集(这意味着您每次添加新程序集时都不再需要编辑wx)。


2
+1的确不错,我不太担心程序集,但是我们的Web项目可能会遇到aspx页面和添加到项目中但没有WiX的其他工件(图像,css)的问题。
si618

4
对于未来的访问者,Wix 3.5具有实用程序heat.exe可以自动执行此收获
Mrchief 2011年

@Mrchief-我不相信Heat会选择在本地复制的引用程序集-尽管显然计划在4.0中使用。参考:sourceforge.net/tracker/...
彼得T. LaComb小

热量不会吸收参考的装配件。
tofutim

使用T4生成WXS文件的一些好的示例是什么?
tofutim

12

使用Heat.exe砸脸并在痛苦的大安装中造成“史诗般的伪装”

扩展SiRobert-P关于热答案。

转换:(使用Heat避免手动将单个文件键入项目,并自动进行构建以简化整体流程。)

详细的WiX 2.0 Heat语法

对于较新的版本(并非与旧版本有所不同,但是可能会有令人讨厌的语法更改....)转到目录从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 

运行热量时生成向导(如执行上述命令时一样)

-scom 

不要抓取“ COM文件”

-渣 

不要抓取“注册表文件”

-sfrag 

不要抓住“碎片”

-srd 

不要抓住“根目录”

目录

dir表示您希望Heat在文件夹中查找

“ $(环境变量)”

您将在(右键单击项目,转到属性)项目属性->“生成”部分的“预处理器变量”中添加的变量名称,其中显示“定义预处理器变量”(假定Visual Studio 2010)

例:
EnviromentVariable = C:\ Project \ bin \ Debug;
没有双引号,但以分号结尾

-cg GroupVariable 

从创建的片段引用到主wxs文件的ComponentGroup

片段目录

将存储输出wxs片段的片段目录

FileName.wxs

文件名

完整的教程在这里,所以freakin很有帮助

第1 部分第2部分


没有为略有不同目的的另一个有用的工具:石蜡(wintellect.com/CS/blogs/jrobbins/archive/2010/03/10/4107.aspx
ralf.w.

9

包括COM对象:

heat生成所有大多数(如果不是全部)注册表项以及它们所需的其他配置。麾!

包括托管COM对象(aka,.NET或C#COM对象)

使用heat托管的COM对象会给你一个几乎完全威克斯文件。

如果您不需要GAC中可用的库(即,全局可用:大多数情况下,无论如何,您都不需要.NET程序集使用该库-此时,如果您不打算这样做,则可能做错了什么共享库),则需要确保将CodeBase注册表项更新为[#ComponentName]。如果您打算将其安装到GAC(例如,您已经制作了一些新的令人敬畏的公共库,每个人都想使用),则必须删除此条目,并向File元素添加两个新属性:AssemblyKeyPath。程序集应设置为“ .net”,并且KeyPath应设置为“是”。

但是,某些环境(尤其是具有托管内存的任何环境,例如脚本语言)也将需要访问Typelib。 确保heat在您的typelib 上运行并包含它。 heat将生成所有需要的注册表项。多么酷啊?


8

安装到 C:\ProductName

某些应用程序需要安装到C:\ProductName或类似的东西,但是净安装示例中的99.9%(如果不是100%)C:\Program Files\CompanyName\ProductName

以下代码可用于将TARGETDIR属性设置为C:驱动器的根目录(从WiX用户列表中获取):

<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是依次指向具有最大可用空间的驱动器请参阅此处)-不一定是C:驱动器。可能还有另一个硬盘驱动器,分区或USB驱动器!

然后,在<Product ...>标签下面的某个位置,您照常需要以下目录标签:

<Directory Id="TARGETDIR" Name="SourceDir">
    <Directory Id="APPLICATIONFOLDER" Name="$(var.ProductName)">
        <!-- your content goes here... -->
    </Directory>
</Directory>

只是安装会更简单WindowsVolume吗?
Wim Coenen 2010年

1
是的,但是您必须使用一种解决方法,因为该WindowsVolume属性可能无法用作Directory(编译器会给出错误/警告),如此此处所指出。我个人认为该解决方法令人困惑。
gehho

7

环境变量

将Wxs文档编译为wixobj代码时,可以利用环境变量来确定各种信息。例如,假设您要更改哪些文件包含在项目中。假设您有一个名为RELEASE_MODE的环境变量,该变量是在构建MSI之前设置的(无论是脚本还是手动,都没关系)在wix源中,您可以执行以下操作:

<define FILESOURCE = c:\source\output\bin\$(env.RELEASE_MODE) >

然后在您的代码中稍后使用它来即时更改您的wxs文档,例如:

<Icon Id="myicon.ico" SourceFile="$(var.FILESOURCE)" />

1
还可以使用$(Configuration)和$(Platform)等编译变量。在msdn.microsoft.com/zh-cn/library/aa302186.aspx上
si618,2009年

1
@Si-今天之前的某个时间,该链接不再有效。我找不到最新的。
彼得M,2010年



7

编辑对话框

编辑对话框的一项出色功能是在4.0.1.7090版(或更高版本)中使用SharpDevelop。借助该工具,可以在“设计”视图中打开,预览和编辑独立对话框(来自WiX来源的wxs文件,例如InstallDirDlg.wxs)。


太棒了,不知道SharpDevelop支持这一点。
anton.burger 2012年

6

设置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" />

5

修改“准备安装?” 对话框(又名VerifyReadyDlg)以提供所做选择的摘要。

看起来像这样:
替代文字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中显示的文本?


当然,这不应该是Windows脚本语言JScript,而应该是DHTML脚本语言JavaScript。可能有点书呆子,但可能会使某些人感到困惑。
Caveman_dick 2011年

5

将可以单独修补的组件放入自己的片段中

如果要在片段中包含任何组件,则必须进行产品安装程序和修补程序,因此必须在该片段中包含所有组件。在构建安装程序的情况下,如果您错过任何组件引用,则会从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版本是不够的。因此,请确保在运输前正确分割您的产品。


5

从Wix3.0及更高版本打印EULA

1)编译wix源代码时,light.exe必须在命令行中引用WixUIExtension.dll。为此,请使用命令行开关-ext。

2)如果将引用添加到WixUIExtension.dll时,您的项目无法编译,这很可能是由于对话框ID冲突造成的,即您的项目使用的对话框ID与WixUIExtension.dll中的某些标准对话框相同,为对话框提供不同的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" />

注意:Wix3.0中的BinaryKey与Wix2.0不同,并且必须完全是“ WixUIWixca”(区分大小写)。

当用户按下按钮时,他/她将看到标准的“选择打印机对话框”,并且可以从那里进行打印。


5
  • 我们在GUI的第一个屏幕中的某个地方(小)显示产品版本。因为人们往往会在每次选择正确的版本时犯错误。(并让我们的开发人员寻找年龄。)

  • 我们已经将TFSBuild设置为还使用针对不同环境的配置生成转换(.mst文件)。(我们知道需要部署到的所有环境)。

由于Grant Holliday撰写的原始网络日志已关闭,因此我将其内容复制到此处:


MSBuild任务,用于从XMLMarch 11 2008生成MSI转换文件

在我以前的文章中,我描述了如何使用MSI转换(* .mst)文件将特定于环境的配置设置与通用MSI软件包分开。

尽管这为您的配置提供了一定程度的灵活性,但是Transform文件有两个缺点:

  1. 它们是二进制格式
  2. 您不能“编辑”或“查看”转换文件。您必须应用它或重新创建它才能查看其中包含的更改。

幸运的是,我们可以使用Microsoft Windows Installer对象库(c:windowssystem32msi.dll)打开MSI“数据库”并创建转换文件。

再次归功于Alex Shevchuk –从MSI到WiX –第7部分 –使用Transforms自定义安装,以向我们展示如何使用VbScript实现此目的。本质上,我所做的只是以Alex为例,并使用Interop.WindowsInstaller.dll实现了MSBuild任务。MSBuild任务

在此处下载源代码和示例transforms.xml(〜7Kb压缩VS2008解决方案)



2
我们在我的本地化文件中重新定义了WelcomeDlgTitle-效果很好!<String Id =“ WelcomeDlgTitle”> {\ WixUI_Font_Bigger}欢迎使用[ProductName] [ProductVersion]安装向导</ String>
saschabeaumont 2009年

5

在部署安装软件包之前,我总是控制其内容。

这只是在命令行中的简单调用(根据Terrences发布),打开命令行并输入

msiexec /a Package.msi /qb TARGETDIR="%CD%\Extract" /l*vx "%CD\install.log%"

这会将包内容提取到当前路径的子目录“提取”中。


4

代替ORCA,使用InstEd,它是查看MSI表的好工具。它还具有通过Transform-> Compare To ...比较两个包的能力。

此外,还提供具有附加功能的Plus版本。但是免费版本为Orca提供了一个很好的选择。


4

为具有x86 / x64兼容性的COM Interop注册.NET程序集

注意:此片段与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望远镜驱动程序

首先,我从上面得到建议,并在单独的文件中创建了一些platforma变量,您可以看到这些变量分散在XML中。

顶部附近的if-then-else部分涉及x86与x64兼容性。我的程序集以“任何CPU”为目标,因此在x64系统上,我需要将其注册两次,一次在64位注册表中,一次在32位Wow6432Node区域中。if-then-else为此设置了我,这些值用于foreach循环。这样,我只需要编写一次注册表项(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)获取该程序集的全名。


3

设置该DISABLEADVTSHORTCUTS属性以强制安装程序中所有公告的快捷方式都变为常规快捷方式,并且您无需包括虚拟的reg键即可用作键路径。

<Property Id="DISABLEADVTSHORTCUTS" Value="1"/>

我认为Windows Installer 4.0或更高版本是必需的


2

这是一个很好的结构,但是根据我的经验,我想知道您如何解决这些情况:

答:您的安装似乎都落在了同一目的地。如果用户需要一次安装所有3个版本,您的过程将允许这样做。他们能否明确地告诉他们正在触发的每个可执行文件的版本?

B.如何处理TEST和/或TRAINING中已存在但尚未存在的新文件?


嗨,布莱恩,A。不,他们没有。InstallName位于Config.wxi中,这是svn:externals未引用的唯一文件。因此,这对于每次安装(即每种产品)都是唯一的。这就是为什么我们为每个版本修改Guid的原因。B. GOTO A. :)他们是单独的MSI,带有自己的UpgradeCode。
si618

1
顺便说一句,我理解您为什么用一个问题回答了我的问题,但是一旦您获得了足够的代表点,请将您的问题移到答案注释中,否则将很难遵循主题。
si618

2

这是一种帮助大型Web项目验证已部署文件数与MSI(或合并模块)内置文件数匹配的方法。我刚刚对我们的主应用程序运行了自定义MSBuild任务(仍在开发中),它拾取了很多丢失的文件,大部分是图像,但是一些javascript文件已经泄漏了!

这种方法(通过连接到WiX项目的AfterBuild目标来查看MSI的File表)可能适用于其他应用程序类型,您可以在其中访问所需文件的完整列表。


2

当安装不允许卸载或重新安装且不会回滚时,执行强制重新安装。

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)

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.