Wix打包系列(一)如何使用wix制作安装程序

原文:Wix打包系列(一)如何使用wix制作安装程序

    最近由于项目需要,需要给客户制作安装程序,一开始使用vs2005自带的打包工程来打包,但用了一段时间发现vs打包太死板,而且使用起来问题很多。收费的商业打包工具不在我考虑范围内,于是在网上找到了wix,稍微了解了下,发现wix的确可以满足我的需求;但是网上wix中文的资料少的可怜,百度,google上搜索到关于wix中文的资料少的可怜,有的只是一些简单的应用,还不足以满足我的要求;没办法,只能靠自己了,还好项目不是很急,于是开始花时间慢慢熟悉wix了。一路熟悉下来,发现使用wix也是个不小的工程,因此我将我使用wix过程的一些主要的问题都记录下来,供以后自己和大家参考:

   1.1 安装wix

    首先,我们需要到网上下载一个wix安装文件,下载地址:http://wix.sourceforge.net/ ,这里我们下载的版本是WiX v3.0 released,以下所有示例程序都是运行在这个版本下的。

    wix是用C#写的,安装wix之前,必须安装.NET Framework 2.0
Service Pack 1 ,这是wix 工具集运行环境必须的,但并不意味着安装应用程序的机器上就一定需要使用Net Framework运行环境,这取决于你的应用程序使用的开发语言环境。使用wix不尽可以打包.net应用程序,也可以打包java应用程序和其他语言开发的应用程序。

    在开始使用wix之前,我们需要先下载示例源码,这些示例都来自WiX
tutorial
,里面每个示例在WiX tutorial中都有详细说明,E文好的可以先看看;大家会发现WiX tutorial中有些地方比较难理解,可能是因为里面涉及到一些msi sdk的知识,文中并没有详细说明,我也是刚接触wix和msi不久,文中对有些概念可能会有理解不准确的地方。该文都是我学习和使用wix的经验,考虑到wix初学者,刚开始会尽量介绍的详细些。

 

    1.2 wix简介

     wix是微软Windows Installer XML的缩写,WiX 的源代码是使用 XML 文件编写的,然后经过预处理、编译与链接,以创建 Windows Installer 数据库。我们可以在命令行上使用 WiX 工具集或使用 MSBuild,来编译与链接 WiX 源代码,而且如果我们安装wix之前已经安装了了Visual Studio(vs2005或者vs2008,据说vs2010已经集成了wix3.0),则还可以在Visual Studio IDE环境中生成安装项目。

   

    这里我们主要讨论命令行方式使用wix工具集,在第七章会提到如何使用在vs环境下使用wix;wix最常用的两个命令就是candle和light, candle是用来编译wix源文件的,light是用来链接编译后的中间文件生成msi安装包。为了了解他们的作用,我们看看wix怎么生成安装包的。

   1.3 生成Samplefirst示例安装包

   我们先来了解他们的用法,在示例源码中找到SampleFirst,用vs或者其他文本编辑器打开SampleFirst.wxs,将其中的每个YOURGUID-XXXX-XXXX-XXXX-XXXXXXXXXXXX 替换成唯一的GUID标识,我们可以使用vs自带的GUID生成器,在vs工具菜单下选择创建GUID,必须确保每个GUID都是唯一的(注意这里可以GUID既可以是带大括号的,也可以不带大括号)。接着我们可以编译了,编译之前最好将C:/Program
Files/Windows Installer XML v3/bin目录设置到系统的环境变量path中,这样我们在任何目录下都可以执行命令了,在命令提示符下,我们执行以下命令进行编译:

candle.exe Samplefirst.wxs

执行成功后会在当前目录下生成SampleFirst.wixobj文件,接着我们执行light命令:

 

light.exe Samplefirst.wixobj

 

light命令执行成功后我们就可以看到生成的安装文件SampleFirst.msi,这样一个简单的msi安装包就制作成功了。

 

    1.4 SampleFirst代码  

    SampleFirst示例展示了最基本的wix应用,如果想要生成功能更丰富的安装包,还必须知道如何编写wix代码;wix源代码是标准的xml标记语言,我们打开SampleFirst.wxs文件,来看看代码的构成:

 

    <Product>标签是我们发布的产品的基本信息,Language和 Codepage是设置我们生成安装包的语言环境,以后章节我们会详细讲到本地化语言的配置;Name和Manufacturer很好理解;需要注意的是Id和UpgradeCode属性,他们是具有唯一标识的GUID编号,还有Version属性是发布产品的版本号(不包括修订版本号)。

 

     另外<Package>标签的ID也是具有唯一标识的GUID编号,所不同的是对于每次生成的安装包,我们都需要一个不同GUID,因此这里设置成*号,每次编译时时候编译器会自动为它生成一个GUID,关于这几个属性的作用我们将在制作升级程序和更新包的时候详细讲解。Description属性是安装包的全名或者叫描述;Comments可以理解成是注解,它是可选的;InstallerVersion则是指定运行该安装包所需要的Windows
Installer的最低版本;Compressed指定是否压缩安装包中的文件,一般我们都设置成yes,可以节省不少空间。

 

    <Media>标签设置文件存储方式,Cabinet是压缩包的名称,它必须和Package的Compressed属性配合使用,如果不设置CabinetEmbedCab属性,则Compressed必须设置为no;EmbedCab决定压缩包是否嵌入到安装文件中(.msi);如果安装的文件比较多比较大,我们也可以定义多个Media,将不同文件压缩到不同的Media中。

   

    <Property> 标签我们可以看成一个静态的变量,它的值是它的Value属性,一般的Property 我们在安装过程中以至于自定义的Action中都可以改变和使用它的值,后面我们会陆续介绍。

   

    <Directory>标签囊括了所有要安装的内容,包括应用程序,快捷方式,动态链接库,文档,注册表信息等;<Directory>嵌套代表了安装程序的目录结构:

    关于最上层的Directory,它包含一个预定义的标识符”TARGETDIR”,而且它的Name也是预定义的值”SourceDir”;TARGETDIR是所有Directory目录的父目录,在每个Package安装包中必须包含一个并且只能有一个TARGETDIR
Directory

     第二层的Directory标识符是ProgramFilesFolder,它也是一个预定义的标识,它指定软件默认的安装目录在系统盘的Program Files目录下;

    第三层是我们自定义的Directory,默认情况它会在它的上一级Directory(ProgramFilesFolder
下生成一个自定义的目录,目录名是Name属性的值;

    最底层Directory标示符是INSTALLDIR,也是我们自定义的,根据我们的定义,默认情况下程序会安装在以下目录:系统盘符:/Program Files/Acme/Foobar 1.0/;INSTALLDIR的属性值是安装文件的绝对路径,在默认情况下它的值依赖于我们定义的目录结构,如果在安装过程中我们改变了安装路径,则INSTALLDIR属性值也会改变,此时它将不依赖于我们定义的目录结构,而将取决于我们选择的安装路径。

   

    这里要注意的是,INSTALLDIR和ProgramFilesFolder是Directory的一个标识符,实际上我们定义的所有Directory的标识符都可以看做一个是Property 属性;而ProgramFilesFolder是Windows Installer中自带的一个Property 属性,Windows Installer
会在启动时设置它的值;同样,Windows Installer 也会设置INSTALLDIR的值,也可以在其他地方引用它,即使你没有显式定义一个INSTALLDIR
Property。(关于Windows Installer 提供的属性列表,可以在msi sdk document中查到,参考
Property Reference;从其中我们可以看到下面的ProgramMenuFolder和DesktopFolder都是Windows
Installer 已经提供的属性。)

    另外这里Directory的默认的INSTALLDIR值是根据目录结构自动计算出来的,如果我们要将某些文件安装在固定位置,比如E盘根目录下,则我们需要在显式定义一个PropertyProperty 的Value属性就是要存放目录的位置。大家可以尝试一下在Fragment下添加如下代码,会出现什么效果:

<Property Id ="INSTALLDIR" Value="E:/"/>

   

    ProgramMenuFolder
Directory中定义了安装在开始菜单->程序中的快捷方式。

  

    <Component>标签中定义所有要安装的元素,它的子元素可以是File(文件)、Shortcut(快捷方式)、RemoveFolder、RegistryValue(注册表键)等,一个Component可以包含多个子元素,但是必须有一个并且只能有一个元素的KeyPath='yes'

 

    <Feature>标签是定义安装的部件,我们可以将不同用处的安装文件放到不到的Feature部件中,然后在安装过程中定制安装不同的部件,关于Feature的具体功能和部件定制在第2章介绍用户界面时详细介绍;

 

    <Icon>标签定义的图标文件,这是应用程序执行文件里的图标,也可以是单独的图标文件。

 

    1.5 优化后的Sample代码  

   

    为了增加源文件的可读性,我将SampleFirst源文件做了一些优化,对照SampleFirst,我们来看看有什么变化:

<?xml version='1.0' encoding='windows-1252'?>   
<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi'>   
    <Product Name='Foobar 1.0' Id='{2C9A3180-95F0-4cdd-B02C-A0ABCAAE3413}' UpgradeCode='{F4F8195E-E907-42dd-BB90-CC2403FA7384}'   
      Language='1033' Codepage='1252' Version='$(var.Version)' Manufacturer='Acme Ltd.'>   
    
        <Package Id='*' Keywords='Installer' Description="Acme's Foobar 1.0 Installer"   
          Comments='Foobar is a registered trademark of Acme Ltd.' Manufacturer='Acme Ltd.'   
          InstallerVersion='100' Languages='1033' Compressed='yes' SummaryCodepage='1252' />   
   
         <Media Id='1' Cabinet='Sample.cab' EmbedCab='yes' DiskPrompt="CD-ROM #1" />  
         <Property Id='DiskPrompt' Value="Acme's Foobar 1.0 Installation [1]" />  
    
         <Icon Id="Foobar10.exe" SourceFile="$(var.Version)/FoobarAppl10.exe" />  
         <Icon Id="word.ico" SourceFile="$(var.Version)/word.ico" />  
    
         <Feature Id='Complete' Title='Foobar 1.0' Description='The complete package.'  
           Display='expand' Level='1' ConfigurableDirectory='INSTALLDIR'>  
             <Feature Id='MainProgram' Title='Program' Description='The main executable.' Level='1'>  
                 <ComponentGroupRef Id='MainCompGroup' />  
                 <ComponentRef Id='compHelperLibrary' />  
             </Feature>  
             <Feature Id='Documentation' Title='Description' Description='The instruction manual.' Level='1000'>  
                 <ComponentRef Id='compManual' />  
             </Feature>  
         </Feature>  
    
     </Product>  
     <Fragment>  
         <ComponentGroup Id ="MainCompGroup">  
             <Component Id='compMainExecutable' Guid='{0008EE68-EA36-4d5d-8BC5-713029E1909A}' Directory='INSTALLDIR'>  
                 <File Id='filFoobarEXE' Name='FoobarAppl10.exe' DiskId='1' Source='$(var.Version)/FoobarAppl10.exe' KeyPath='yes'>  
                     <Shortcut Id="startmenuFoobar10" Directory="ProgramMenuDir" Name="Foobar 1.0" WorkingDirectory='INSTALLDIR' Icon="Foobar10.exe" IconIndex="0" Advertise="yes" />  
                     <Shortcut Id="desktopFoobar10" Directory="DesktopFolder" Name="Foobar 1.0" WorkingDirectory='INSTALLDIR' Icon="Foobar10.exe" IconIndex="0" Advertise="yes" />  
                 </File>  
             </Component>  
             <Component Id="compProgramMenuDir" Guid="{6886685C-E1B1-48d9-B6A7-548175BD8F17}" Directory="ProgramMenuDir">  
                 <Shortcut Id="UninstallProduct"  Name="Uninstall My Application" Directory="ProgramMenuDir" Target="[SystemFolder]msiexec.exe" Arguments="/x [ProductCode]" Description="Uninstall"/>  
                 <RemoveFolder Id='rmvProgramMenuDir' On='uninstall' />  
                 <RegistryValue Root='HKCU' Key='Software/[Manufacturer]/[ProductName]' Type='string' Value='' KeyPath='yes' />  
             </Component>  
         </ComponentGroup>  
         <ComponentGroup Id='ExtraGroup'>  
             <Component Id='compHelperLibrary' Guid='{A30DAC3F-2902-479c-B530-B90A7BA8E514}' Directory='INSTALLDIR'>  
                 <File Id='filHelperDLL' Name='Helper.dll' DiskId='1' Source='$(var.Version)/Helper.dll' KeyPath='yes' />  
             </Component>  
    
             <Component Id='compManual' Guid='{25518565-2E48-415c-B4FD-A20E2EA869D5}' Directory='INSTALLDIR'>  
                 <File Id='filManual' Name='Manual.pdf' DiskId='1' Source='$(var.Version)/Manual.pdf' KeyPath='yes'>  
                     <Shortcut Id="startmenuManual" Directory="ProgramMenuDir" Name="Instruction Manual" Icon="word.ico" Advertise="yes" />  
                 </File>  
             </Component>  
         </ComponentGroup>  
     </Fragment>  
     <Fragment>  
         <Directory Id='TARGETDIR' Name='SourceDir'>  
             <Directory Id='ProgramFilesFolder' Name='PFiles'>  
                 <Directory Id='ManufacturerDir' Name='Acme'>  
                     <Directory Id='INSTALLDIR' Name='Foobar 1.0'>  
                     </Directory>  
                 </Directory>  
             </Directory>  
               
             <Directory Id="ProgramMenuFolder" Name="Programs">  
                 <Directory Id="ProgramMenuDir" Name="Foobar 1.0">  
                 </Directory>  
             </Directory>  
               
             <Directory Id="DesktopFolder" Name="Desktop" />  
         </Directory>  
     </Fragment>  
 </Wix>

   首先,为了方便版本管理,我把ProductVersion属性改成变量形式,在candle命令中将Version的值传给编译器进行解析,这样我们不用在每次编译之前都去检查和修改源文件中的版本号了,而且我们以后会在源文件中多次使用这个版本号(特别是在发布升级包或更新补丁时),也可以减少出错的概率。

 

    接着我们可以看到多出来2个跟Product并列的Fragment标签,它们是一些代码片段,这里跟SampleFirst很不一样,我们把SampleFirst中的一些结构搬到了Fragment中,但这并不影响我们最终生成安装包;我们可以使用*Ref元素来引用Fragment中定义的元素,比如我们把SampleFirst中复杂的Directory嵌套放到Fragment中,一切都显得更清晰明了了。

    我们再看另一个Fragment标签,我把所有的Component都放在这里面,而且可以根据不同的模块分成多个ComponentGroup,与SampleFirst中不同的是,这里所有的Component标签并非嵌套在Directory标签中,因此所有的Component都需要指定Directory属性,它是我们之前定义的Directory的Id标识符;

       

    为了避免跟其他属性混淆,这里我把元素的id等属性做了修改,对于Component compMainExecutable ,它包含执行文件File filFoobarEXE和文件的快捷方式,我们可以在File标记下直接使用Shortcut定义快捷方式,可以看到他是一个advertised
Shortcut,因此Component的KeyPath可以是FILE;

   

    而对于Component compProgramMenuDir ,它包含3个子元素:

  

    UninstallProduct Shortcut会在ProgramMenuDir下给我们安装程序添加一个卸载程序的快捷方式;

  

    RemoveFolder 标签告诉我们会在uninstall的删除这个目录;我们可以看到ShortcutRemoveFolder 标签都没有KeyPath属性,而Component必须有一个包含KeyPath的子标签,另外这里不能File
作为KeyPath,因为对于UninstallProduct 
Shortcut
来说,因为指定了Target属性,所以他是一个non-advertised Shortcut,必须使用注册表键作为KeyPath;

  

    一开始,对于Shortcut标签的advertised 属性感觉很是费解,后来仔细推敲了下,大概是这个意思:对于应用程序(filFoobarEXE)的快捷方式,它是通过ProductCode来标识的,只属于当前的软件产品,不能在不同应用程序之间共享,所以此时advertised 等于yes;而对于卸载程序(msiexec.exe),它是Windows Installer自带的卸载程序,可以被任何安装程序共享使用,所以advertised 等于no,但同时必须指定卸载程序的路径Target。一般来说,如果指定了Target属性,我们就认为它属于non-advertised

 

    到这里我把大家容易出现编译问题的地方都说明了一下,如果仍遇到问题可以参考Wix Help文档和msi sdk document。

    最后我们回到Product标签,这里有两个Icon,Foobar10.exe
Icon 使用的是执行文件的Icon,我们在Shortcut中使用IconIndex
指定Icon;另一个Icon是文件Icon,我们可以复制一个word.ico到我们打包目录下,在Shortcut中只需指定Icon的标识符就可以了。

 

    至于Feature标记的作用,我们在下一章节再讲,下面我们来编译链接我们的源文件。

我们在编译源代码之前,先新建一个版本目录1.0.0,所有要安装的文件均放在该目录下,该版本的安装包也会生成到该目录,将前面我给的代码复制到编辑器中,保存到该目录下Sample.wxs文件中;源代码中FILEIcon 中的Source属性就是源文件的路径,这里我们都使用的相对路径。

   

所有准备工作都已经做完了,在Sample.wxs 所在的目录执行candle命令进行编译,将Version属性传给编译器:

candle.exe -dVersion=1.0.0  Sample.wxs -out 1.0.0/

WiX tutorial中不同的是,这里我们需要指定输入文件和输出文件的路径,这样方便我们以后制作升级包的时候区分不同版本

然后执行light命令进行链接,生成msi安装包。

light.exe -out 1.0.0/Sample.msi 1.0.0/Sample.wixobj

 

下一章节,我们将介绍如何定义安装时用户界面,以及如何进行语言本地化的操作。

时间: 2024-10-29 06:40:11

Wix打包系列(一)如何使用wix制作安装程序的相关文章

Wix打包系列 (六)制作升级和补丁包

原文:Wix打包系列 (六)制作升级和补丁包     前面我们已经知道怎么制作一个完整安装包了,但我们的软件往往不能一次性就满足客户的需要,当客户需要我们给软件进行升级的时候,我们应该怎么做呢?       在这之前,我们有必要了解下Windows Installer中的Upgrades定义:       6.1 关于Windows Installer Upgrades       在Windows Installer中将软件产品的更新划分为3类: Small updates    它意味着安装

Wix打包系列(七) 添加系统必备组件的安装程序

原文:Wix打包系列(七) 添加系统必备组件的安装程序     我们知道在vs的打包工程中添加系统必备组件是一件很容易的事情,那么在wix中如何检测系统必备组件并在安装过程中安装这些组件.这里以.Net Framework组件作为必备组件为例:         7.1 检测.Net Framework 版本         步骤1:为安装项目添加WiX .NET extensions库的引用     如果是命令行方式使用wix,那么需要在candle和light的时候添加如下参数: -ext W

Wix打包系列(五) 部署数据库

原文:Wix打包系列(五) 部署数据库     很多人在使用vs进行打包的时候,经常会为数据库部署的问题犯愁,即便是重写Installer类的方法,也不是很可靠方便,下面我们来看看在wix中如何部署数据库.         5.1 创建数据库         wix包含数据库的相关链接库,需要为wix添加如下命名空间: <Wix xmlns="http://schemas.microsoft.com/wix/2006/wi" xmlns:util='http://schemas.

Wix打包系列(四) 自定义UI

原文:Wix打包系列(四) 自定义UI     除了标准的安装界面,如果我们要在安装时需要提供一些额外的信息时,这时就需要自定义界面来显示和录入这些信息.         4.1  自定义对话框     如上一章中我们测试数据库的连接串在源文件中定义的,如果我们需要用户安装时输入数据库信息,就需要添加一个如图的对话框.     开始之前,我们先看看标准的WixUI_Mondo UI包含哪些对话框: 1: BrowseDlg 2: CustomizeDlg 3: DiskCostDlg 4: Li

Wix打包系列(三)自定义Action(Custom Action)

原文:Wix打包系列(三)自定义Action(Custom Action)    3.1 关于Action     我们已经知道如何生成具有标准安装界面的安装程序了,Windows Installer按照我们的界面设置使用标准的安装步骤进行安装,它的安装过程是由一系列标准的Action组成,通过这些Action来完成对计算机的安装配置:如果我们想自定义安装步骤或者在安装过程中执行自定义的操作,就需要使用自定义的Action.当然,使用Custom Action之前,我们应该先了解一下msi中这些

安装程序制作-制作安装程序 怎样判断 是否安装了 SQLServer 以及安装的是哪个版本

问题描述 制作安装程序 怎样判断 是否安装了 SQLServer 以及安装的是哪个版本 制作安装程序时怎样判断用户机是否安装了SQLServer以及安装的是哪个版本? 解决方案 查询分析器里执行SELECT SERVERPROPERTY('productversion'), SERVERPROPERTY ('productlevel'), SERVERPROPERTY ('edition')

请教一个制作安装程序的问题

问题描述 我用InstallShieldDeveloper8.0制作安装程序,我的程序里包括帮助文件的,但是安装完后快捷方式的起始位置为空,程序调用不了帮助文件,有人知道怎么解决吗?怎么让起始位置不为空,指为安装目录呢? 解决方案 解决方案二:学习一下,UP一下解决方案三:指定快捷方式的Target属性即可!@同时应该保证被指向的文件存在!

关于制作安装程序的疑惑

问题描述 1:安装程序安装完成后,重起计算机.如何实现更好?目前的做法是在"确定"node上加一个customaction来重起.可是在重起的时候安装程序窗口实际并没有结束(是被系统关闭).2:卸载分3步(1)清理注册表的内容--〉(2)重起计算机--〉(3)删除实际文件初次接触,关于这个问题非常茫然): 解决方案 解决方案二:我都等了一天了?呜呜呜解决方案三:安装卸载可以参考 http://blog.csdn.net/downmoon/archive/2007/04/24/15811

Visual Studio DSL 入门14---用Wix制作安装程序

我们到现在为止已经对Vs.net dsl的基础知识进行了介绍,并且一步一步的完成了我们的状态机设计器的设计,在上一节结合T4模板生成了代码.但是我们一直还是在调试的项目中来做的, 怎么能够让我们做的这个状态机设计器能够提供给其它人使用呢,在这一节,我们就来做一个安装程序,这样在只安装了VS.NET的机器上,不用安装SDK就可以使用我们的设计器了. VS.NET DSL专门提供了针对DSL的安装项目,我们直接基于这个项目来制作我们的安装程序: 1.首先,向我们的解决方案中添加一个Dsl 安装项目,