WIX 技巧-使用 MSBuild 和 Windows Installer XML 执行自动发布

告别繁琐!用WiX Toolset v3.11为你的WPF项目制作专业安装包(附中文界面配置) 本文详细介绍了如何使用WiX Toolset v3.11为WPF项目制作专业安装包,包括环境搭建、文件部署策略、快捷方式优化及中文界面配置。通过WiX的深度定制能力,开发者可以创建符合企业级标准的安装程序,提升用户体验和产品专业性。 阅读详情
WIX 技巧

使用 MSBuild 和 Windows Installer XML 执行自动发布


Sayed Ibrahim Hashimi



本文讨论:
  • Windows Installer XML 概述
  • 创建 WiX 打包说明
  • 集成 WiX 和 MSBuild
  • 自动执行生成和打包
本文使用了以下技术:
Visual Studio, Windows Installer XML (WiX), MSBuild


Download ImageGet the sample code for this article.

 

在
开发过程中,重要的是有自动的生成过程。同样重要的是要有自动创建发布的手段。遗憾的是,在很多组织中(尤其是较小的组织),并没有这些。通常,您会发现发布内容只是在最后一分钟才被拼合在一起。但是,如果花时间建立自动生成和发布计划,就会节省无数的时间,您就可以将这些时间更好地用在完成任务上,而不是用来生成和发布项目。

 

在本文中,我将介绍如何使用 Microsoft® Build Engine (MSBuild) 和 Windows® Installer XML (WiX) 工具集在组织中实现自动和可重复的生成和发布过程。本文讨论了 WiX v2(注意,当 WiX v3 发布时,不会直接转换某些语法示例)。虽然 WiX 的确简化了创建发布的过程,但无论您是否使用 WiX 创建发布,本文描述的技术都可供您参考。也可以对这些技术进行一些修改后,将它们应用于不使用 Microsoft .NET Framework 2.0 所开发的应用程序(本文还包含指向英文网页的链接)。

我将假定您熟悉 MSBuild(如果需要重新了解它,请参阅我在 2006 年 6 月的《MSDN®杂志》上的文章 深入了解 MSBuild:通过 Microsoft Build Engine 的自定义任务以自己的方式编译应用程序)。我将为那些不熟悉该工具的人提供 WiX 工具集的概述。请参见侧栏“MSBuild 和 WiX 资源”中关于更多相关文章和工具的参考。在本文中,为了进行演示,我将使用我的 Sedodream MSBuild 项目。您可以从 www.codeplex.com/Sedodream 获得最新源代码。


WiX 简介

创建应用程序时,通常最终结果是要在生产计算机上安装并运行它。WiX 工具集可以帮助您完成此任务。在这一节中,我将描述 WiX,并介绍如何使用 WiX 创建安装程序。

WiX 描述了在目标计算机上的安装是什么样子的。从 Visual Studio® 到 Microsoft Office,在很多 Microsoft 应用程序中都使用了 Windows Installer。您可能会吃惊地发现:WiX 实际上是个开放源代码的项目,并且承载在 sourceforge.net 上。您可以从 wix.sourceforge.net 下载最新的二进制文件和源文件。下载并安装 WiX 后,您会发现计算机上安装了如图 1 中总结的大量可执行文件。我将重点介绍 Candle.exe 和 Light.exe 工具的使用。

WiX 使用声明性语言,而不是过程性语言,这意味着您要描述您的安装将是什么样子的,而不用描述为了实现它需要执行哪些步骤。这可能与您的习惯不同,但它非常容易掌握。通常,要安装在目标计算机上的描述文件将填充 WiX 源文件。在这里,我将重点介绍这些组件。

在 WiX 源文件中,有三个与您希望安装的文件有关的主要元素:文件、组件和功能。文件元素是对单个文件的引用。文件必须包含在组件元素中,组件元素是最小的安装单位。就是说,如果您有一个包含 100 个文件的组件,并且您要安装该组件,则会安装它包含的所有文件。相反,如果不安装该组件,则不安装任何文件。建议不要创建包含大量文件的组件。

组件始终包含在功能元素中,并且可以包含在多个功能中。功能是一组组件,也可能是一组子功能。如果安装程序具有允许用户选择要安装哪些项的图形界面,则用户实际是在选择功能。


创建 WiX 文件

可以在您选择的任何文本或 XML 编辑器中创作 WiX 源文件。还可以使用 Visual Studio,利用 IntelliSense® 创作 WiX 源文件。如果在使用 Visual Studio 编辑 WiX 源文件时未启用 IntelliSense,则您唯一要做的是将 wix.xsd 文件复制到 Visual Studio 架构目录中。通常,此目录位于 %Program Files%/Microsoft Visual Studio 8/Xml/Schemas。创建 WiX 源文件时,还需要生成很多 GUID。Visual Studio 有一个可用于此目的工具,还有一些 Visual Studio 宏也可用于分配快捷方式,以便直接插入新的 GUID。

现在,让我们开始创建新的 WiX 源文件。第一个元素始终是 Wix 元素。Wix 元素的子元素包括 Product、Fragment、Module 和 PatchCreation。预期输出的类型将决定您将使用其中的哪个子元素。在这里,我希望最终结果是我的项目的安装程序数据库 (MSI) ,因此我将使用 Product 元素。下面是示例项目的 WiX v2 源文件 Sedodream.wxs 的开头部分:

<?xml version='1.0' encoding='UTF-8'?>
<Wix xmlns='http://schemas.microsoft.com/wix/2003/01/wi'>   
   <Product Name='Sedodream MSBuild Project' 
        Id='C9D25926-FCE0-4EB6-8FF5-4686EE5AB089'
        Language='1033' Codepage='1252' Version='1.0.0' 
        Manufacturer='SedoTech'
        UpgradeCode='9C5E4073-EFDE-419B-935D-CE2632BC560E'>

      <Package Id='????????-????-????-????-????????????' 
         Keywords='Installer'
         Description='Sedodream MSBuild Project Installer'
         InstallerVersion='100' Languages='1031' 
         Compressed='yes' SummaryCodepage='1252' />
...

 

可以看到,Product 元素有很多属性,其中最重要的是 Id 和 Name。Id 用于唯一标识产品。对于新的主要发布必须更改 Id。还应当记录下它的值,以便随后引用。Name 属性的值是将显示在“添加/删除程序”面板中的值,如果安装程序有 UI,它将显示在简介页上。

Product 元素的第一个子项 Package 元素也显示在这里。注意,Id 属性包含一系列 GUID 格式的 ? 字符。通过使用此语法,代码指定应当在生成时生成 Id。创建安装程序时,可以让所创建的每个安装程序的 Package Id 各不相同,甚至让每次生成的安装程序不同。这是您可以让系统自动生成的唯一 GUID;其他所有 GUID 都需要保持相同,并应记录下来,供将来引用。

几乎每个安装程序都会将至少一个文件放在目标计算机上,而且此安装程序没有差异。由于 WiX 使用声明性方式,所以我将声明目录结构是什么,并且将在目标计算机上重新创建它。我使用一系列 Directory 元素完成该操作:

<Media Id='1' Cabinet='Sedodream.cab' EmbedCab='yes'/>

<Directory Id='TARGETDIR' Name='SourceDir'>
   <Directory Id='ProgramFilesFolder' Name='PFiles'>
      <Directory Id='MSBuildDir' Name='MSBuild'>
         <Directory Id='INSTALLDIR' Name='Sedodrea' 
          LongName='Sedodream'>

 

此示例中的 Media 元素用于指定将生成一个压缩文件,并且要将它放在安装程序数据库中。这是必需元素,如果安装文件跨越多个介质,则可以有多个该元素。例如,如果在 CD 上分发应用程序,但它需要跨越多张盘。大多数情况下,不必担心这个问题。

下面的 Media 是 Directory 元素系列。第一个 Directory 元素将是仅封装其他条目的虚拟元素。TARGETDIR 目录元素下的条目的 Id 值是 ProgramFilesFolder。您可能已猜到,这是众所周知的位置,并且 Windows Installer 将在启动时设置它的值。还有其他可以使用的系统文件夹;若要查看完整列表,请访问 msdn.microsoft.com/library/en-us/msi/setup/system_folder_properties.asp。这些系统属性始终解析为其完整路径。

接下来的元素提供自定义目录的名称。如果可能,该目录将使用 LongName 属性进行命名;否则使用 Name 属性。如果这些目录都不存在,Windows Installer 将在安装时创建合适的目录。在前面的代码段中声明的内容的完整树以 C:/Program Files/MSBuild/Sedodream 结束。

声明目录结构之后,即可开始创建 Component 声明。前面提到过,可以安装的最小单位是组件,组件可以由很多不同项组成,这些项包括文件、快捷方式、注册表项和证书。请设计您的组件,使它们相互独立。就是说,安装或卸载组件时,不应当对其他组件有任何负面影响。实际上,这意味着这些项应当仅包含在单个组件中。如果不是这种情况,则可能要重新考虑如何组织组件。组件只包含单个文件是很平常的。

每个组件声明必须有一个 Id 和一个 GUID 属性。Id 是用于引用组件的名称,GUID 是 Windows Installer 要使用的唯一标识符。非常重要的一点是,要确保所有组件的 GUID 都不重复。

图 2 显示了示例 WiX 源文件中的组件。可以看到它包含四个 File 元素和两个 XmlFile 元素。File 元素只是对需要放在目标设备中的文件的引用。File 元素可以有很多不同属性,但您使用的最多的是 Id、Name、LongName、Source 和 DiskId,如图 3 所示。

XmlFile 元素实际上用于修改目标计算机上的现有 XML 文件。它将新元素插入 Microsoft.Build.xsd 文件中,该文件允许对包含在示例项目中的自定义 MSBuild 任务启用 IntelliSense。

Feature 是在安装程序显示 UI 的情况下用户要选择安装的项。下面是示例项目中的 Feature 声明:

<Feature Id='Complete' 
   Title='Sedodrem Core' Description='MSBuild libraries'
   Display='expand' Level='1' ConfigurableDirectory='INSTALLDIR'>
   <ComponentRef Id='TaskBinFiles'/>
   <ComponentRef Id='NUnitFiles'/>

   <Feature Id='Samples' Title='Samples' 
      Description='Contains samples of Sedodream usage'
      Display='expand' Level='100' ConfigurableDirectory='INSTALLDIR'>
      <ComponentRef Id='Samples'/>
   </Feature>
</Feature>

 

像其他很多 WiX 元素一样,Feature 元素有很多可能的属性,图 4 描述了一些较常用的属性。

在此 WiX 片段中,主要的功能“Complete”包含对两个组件和一个子功能的引用。子功能引用通过子 Feature 元素实现。WiX 源文件可以包含任何数目的这些所需项。组件引用使用 ComponentRef 元素。使用 ComponentRef 元素时,所使用的 ID 值必须与在尝试引用的 Component 元素中所使用的该值相同。


WiX 和 MSBuild

在最新发布的 WiX v2 中,您将发现 wix.targets 文件,该文件包含的定义将帮助您从 MSBuild 创建 WiX 发布。WiX 团队已创建了一组被 wix.targets 文件引用的 MSBuild 任务。这些任务全部包含在 WiXTasks.dll 程序集中,如图 5 所示。

您生成安装程序的方式非常类似于现在生成托管项目的方式。例如,创建 C# 项目时,项目文件将包含生成项目所需的所有属性和项,但生成项目的所有步骤则包含在 Microsoft.CSharp.targets 文件中。此文件将通过 MSBuild Import 元素包含到项目文件中。对于 WiX,项目文件将定义所有需要的属性和项,然后包含 wix.targets 文件以定义如何生成最终包的逻辑。图 6 提供了使用 wix.targets 成功生成安装程序所需的最低声明的列表。

若要创建安装程序,需要创建 MSBuild 文件,以定义必需的属性和项。在这里,我将该文件命名为 SedodreamMSI.wproj。图 7 中显示了此文件。

可以看到,此 MSBuild 文件中的内容不是很多,只定义了少量属性和一个单项。该文件假定 WiX 源文件 Sedodream.wxs 已创建,并且驻留在 BaseInputPath 目录中。WiX 文件还对文件的存放位置进行了一些假定。现在,我们假定所有文件都在期望的位置。

为了生成安装程序,我需要对项目文件调用 MSBuild。我打开 Visual Studio 2005 命令提示符,导航到文件所在位置,并执行以下命令:

msbuild Sedodream.wproj

 

输出显示在图 8 中。

由于我没有定义要执行的目标,因此会执行 DefaultTargets,并且它被定义为 Sedodream.wproj 文件中的 Build。从输出,可以看到在 bin/Release 文件夹中创建了名为 ReleaseSedodream.msi 的文件。在这里,可以执行安装程序,以确保它按期望工作。在继续实现我们的主要目标之前,我将在下一节讨论高级 MSBuild 主题:批处理。之所以需要这样做,是因为在生成产品及其发布的整个过程中要用到批处理。

图 8 从 MSBuild 实现的 WiX 安装程序创建输出
图 8  从 MSBuild 实现的 WiX 安装程序创建输出 (单击该图像获得较大视图)

MSBuild 批处理

使用 MSBuild 时,您将发现找不到循环构造。您可以使用批处理来代替循环,批处理使用项的元数据将项划分成不同的类别,这些类别称为批或桶,它们是由一个或多个项组成的。一旦划分了项,就可以迭代遍历每个批。这种方式在托管项目的整个生成过程中使用。批处理有两个大的类别:任务批处理和目标批处理。在任务批处理中,当您执行任务时,MSBuild 引擎将确定需要创建什么存储桶,并通过这些存储桶执行任务。若要使用任务批处理,则要向任务提供项的元数据。

若要演示批处理,请想象您面对以下情形。您必须将一组文件从一个位置复制到一个或多个其他位置,并且您有两个选项:可以使用单个 Copy 任务将文件复制到每个位置,也可以使用批处理在一个复制元素中实现该操作。第一个方案尽管简单,但不是个好的解决方案,因为它难以维护。为了说明第二个方案,请参考下面这个非常简单的 MSBuild 文件 batching01.proj:

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" 
         DefaultTargets="CopyFiles">
  <ItemGroup>
    <SourceFiles Include="*.txt"/>
    <Dest Include="One;Two;Three;Four;Five"/>
  </ItemGroup>

  <Target Name="CopyFiles">
    <Copy SourceFiles ="@(SourceFiles)" 
         DestinationFolder="%(Dest.FullPath)"/>
    <Message Text="Fullpath: %(Dest.FullPath)"/>
  </Target>
</Project>

 

在此文件中,我声明了一个 SourceFiles 项,包括以 .txt 结尾的任何文件,然后我还声明了一个 Dest 项,包括五个位置。在目标 CopyFiles 中,我使用 Copy 任务将 SourceFiles 复制到每个目标位置。由于我将 FullPath 元数据传递到 Copy 任务中,因此 MSBuild 引擎将创建一个由所有具有不同 FullPath 值的 Dest 项组成的批,然后对每个批调用 Copy 任务。这就是任务批处理。为了执行它,我可以从包含 batching01.proj 文件的目录调用以下命令:

msbuild.exe batching01.proj

 

现在,我将用目标批处理演示相同结果。对于目标批处理,批是基于目标的 Inputs 和 Outputs 创建的,因此我必须为这些值传递项的元数据:

<Project xmlns="http://schemas.
  microsoft.com/developer/msbuild/2003" 
         DefaultTargets="CopyFiles">
  <ItemGroup>
    <SourceFiles Include="*.txt"/>
    <Dest Include="One;Two;Three;Four;
                   Five"/>
  </ItemGroup>

  <!-- These targets demonstrate target 
       batching -->
  <Target Name="CopyFiles" 
          Inputs="@(SourceFiles)" 
          Outputs="%(Dest.FullPath)">
    <Copy SourceFiles="@(SourceFiles)" 
          DestinationFolder=
          "%(Dest.FullPath)"/>
  </Target>
</Project>

 

目标批处理是以目标 Inputs 和 Outputs 驱动的。由于 Outputs 包含 Dest.FullPath 声明,因此每存储桶将调用目标一次。这类似于任务批处理,不同的是整个目标的内容将每批重复一次,如图 9 所示。

图 9 目标批处理的 MSBuild 输出
图 9  目标批处理的 MSBuild 输出 (单击该图像获得较大视图)

从此输出中,可以看到对于 Dest 项,每批调用一次目标 CopyFiles。在自动执行生成/打包过程的示例中,我将使用批处理为配置平台与风格的每个组合创建安装程序。


自动执行生成和打包

对于每个组织来说,有时由于所使用的技术,有时由于组织要求,使得生成和打包所涉及的步骤略微不同。我将一组我认为可以很好地适合很多组织的步骤放在一起,虽然可能需要根据特定情形对它进行一些微调。自动的生成和打包过程的目标是双重的:创建可重复的公用生成过程,以及创建自动的可重复打包过程。有三个核心步骤:更新、生成和打包,但这又细分成很多更小的步骤,如图 10 所示。

图 10 生成过程
图 10  生成过程 (单击该图像获得较大视图)

一个非常重要的步骤是标记源文件。之所以要这样做,是因为如果没有此步骤就不能重复该过程。创建一个将发送用于生产的生成版本时,需要能够重新创建完全相同的生成版本,以便维护所部署的代码。根据用作源代码管理提供程序的工具,此步骤将有所不同。针对各种不同的源代码管理提供程序,可以下载很多不同的 MSBuild 任务来实现此目的。有关详细信息,请参见侧栏“MSBuild 和 WiX 资源”。


分析 MSBuild 脚本

在这一节中,我将介绍可以用于生成和打包示例应用程序的 MSBuild 脚本。总体结构非常类似于托管项目的生成方式。就是说,您将定义一个项目文件,在其中包含应当处理的各项,然后导入另一个用于定义过程流的项目文件 Sedodream.Package.targets。请将这些过程视为句子。打包项目文件用于定义名词,导入的文件用于定义谓词。我将通过对 Solution 文件使用 MSBuild 任务来生成项目。若要生成安装程序,将使用前面的项目文件 SedodreamMSI.wproj。

如果您有一个过程具有多个分离元素,那么需要将它们以某种方式聚合在一起。可以通过定义一组公用的属性、项和目标来完成该操作(参见图 11)。

在此过程中,将生成解决方案,并且会将 WiX 源文件复制到相同目录中。您应当定义您的 WiX 源文件,并记住此路径。不管 WiX 文件包含于源代码管理中的什么位置,都将从生成产品的相同目录中生成这些文件。其他必需文件也将由 CopyFilesForPackaging 目标复制到此目录。

在此过程中,PackageRoot 属性还将定义在哪里生成项目以及 WiX 将在哪里生成安装程序。在示例中,目录处于与解决方案文件相同的级别,但它可以是生成计算机上的任何位置(虽然我建议此位置不要使用网络共享)。

图 12 显示了 Sedodream.Package.targets 文件中声明的主要目标。所有这些目标(以及目标文件中的其他很多目标)都有其在属性中定义的依赖项,因此您可以在此过程中完全更改事件的顺序。例如,如果需要在 Build 目标执行项目文件之前注入步骤,则可以在 Sedodream.Package.targets 文件的 Import 语句之后直接插入以下片段:

<BuildDependsOn>
    CustomBeforeBuild;
    $(BuildDependsOn);
</BuildDependsOn>

 

由于已重新定义了 BuildDependsOn 属性,因此可以有效地将 CustomBeforeBuild 步骤注入到当前过程中,而不用修改现有目标文件。若要了解对这个问题的更详细讨论,应参阅我在 2006 年 6 月的文章(前面已引用)。

在创建安装程序之前,让我们快速看一下生成解决方案的 CoreBuild 目标(参见图 13)。它将演示如何针对每个已定义的配置,使用目标批处理来生成解决方案。

具体来说,首先要注意的是 Inputs 和 Outputs;它们是 AllConfigurations 项的元数据值,该项将使目标按每个平台/风格配对分别执行一次。然后,调用 MSBuild 任务以生成具有所有相应属性的解决方案文件。之所以提供 OutputPath,是因为我要将输出重定向到另一个目录。


综述

至此,我已讨论了如何使用 WiX 创建安装程序、某些高级 MSBuild 概念以及 MSBuild 打包脚本的结构。对于为示例项目创建安装程序,剩下的唯一问题是创建 MSBuild 项目文件,以定义必需属性。这非常的简单。最后,我将介绍如何根据具体需要自定义该过程。

图 14 显示了 MSBuild 文件 Sedodream.Package.dproj,它将驱动示例项目的该过程。前面讨论过,此项目文件的主要目的是描述需要生成什么。导入的文件 Sedodream.Package.targets 知道如何创建最后产品的所有细节,还可以通过向生成过程注入步骤来自定义该过程。

在此项目文件中,可以看到已定义的必需的 SolutionFilePath 属性。这是将用于生成产品的解决方案文件的路径。而且,可以看到 WixSourceFiles 项的声明,其中包括将用于编译安装程序的文件集合。它们是将发送给 Candle.exe 工具进行编译的文件。

同时还提供了 OtherFiles 项的声明,其中包含那些应当复制到将用于创建安装程序的位置的文件。这些文件将复制到用于创建安装程序的 BaseInputPath 中。通过使用 Destination 元数据值,可以将它们复制到 BaseInputPath 下面的任何位置。在此示例中,我会将用于生成安装程序的自定义位图复制到位图文件夹中。当调用 Light.exe 工具来创建 MSI 文件时,它将在选取位于本地目录中的任何默认文件之前,先选取在此目录中的文件。

注意 DropLocation 属性的声明。通过声明此属性,在 PackageRoot 中的所有文件都将复制到 DropLocation 中的目录内。当需要快速进行某些修改时,将二进制文件和安装程序文件放在同一个位置将大有帮助。

若要看到为示例项目执行的生成文件和包,请打开 Visual Studio 2005 命令提示符,并导航到项目部署文件夹。然后执行以下命令:

msbuild SedodreamPackage.dproj

 

命令提示符下会生成很多输出,您将在 Package 目录下发现两个目录:Debug 和 Release。这两个目录都将包含所生成的安装程序及其关联文件。


自定义过程

为产品设置此过程时,将需要创建类似于 SedodreamPackage.dproj 文件的文件。可以将自定义内容直接放在该文件内。应当不需要更改 Sedodream.Package.targets 文件本身。如果要注入步骤,可以执行先前描述的过程。如果要重新定义目标,可以通过在 import 语句之后重新声明目标,直接重写它们。例如,Sedodream.Package.targets 文件定义了目标 GetLatest,但它为空。在此目标中,应当放入必要的任务,以便从存储库获得最新的源文件。下面是它在项目中的大致模样:

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" 
       DefaultTargets="Package">
   <!-- Your properties and items defined here -->

   <!-- Import the deployment target to do all the work for us -->
   <Import Project="Sedodream.Package.targets"/>

   <!-- Place overriding customizations after this point -->
   
   <Target Name="GetLatest">
      <Message Text="Getting latest sources"/>
      <!-- Insert tasks to get latest from source control -->
   </Target>
</Project>

 

图 15 提供了在 Sedodream.Package.targets 文件中定义的重要目标的列表。可以自定义这些目标,以适合具体需要。

剩下的主要问题是如何自动执行此过程,现在这一问题非常简单。您唯一要做的是对部署文件自动执行 MSBuild.exe,在这里,该文件是 SedodreamPackage.dproj。如何完成该操作将取决于当前如何生成公用生成文件。如果正在使用 Visual Studio Team Foundation Server,则可以使用 Team Build 来执行此操作。可以使用 TFSBuild.proj 文件中的 MSBuild 任务。如果使用其他某些技术,则很可能它们对 MSBuild 有本机支持。如果不借用任何一种上述技术,可以使用 Windows Scheduler 定时执行此操作。


总结

在本文中,我介绍了如何基于 WiX 工具集创建自动的生成文件和打包过程。一旦完成了此集成,就可以按可靠和可重复的方式生成和打包产品。这对即将发送到部署位置的应用程序是非常重要的。

如果 WiX 不是您的安装程序技术,仍然可以在进行某些修改后使用其理念和文件。所有我知道的安装程序技术都支持某种形式的命令行执行方式。您可以借用此方式,以使用这些其他技术来创建您的安装程序。另外随 MSBuild 任务和目标一同提供了某些第三方安装程序技术,它们可帮助您重新创建此过程。



Sayed Ibrahim Hashimi拥有佛罗里达大学的计算机工学位。他是佛罗里达州 Jacksonville 的开发人员和体系结构设计师。他的专长是财务、教育和收藏行业。他在 Latitude Software 公司从事 .NET 技术方面的工作。

Subscribe  摘自  March 2007 期刊  MSDN Magazine.
Figure 1 WiX 组件

名称说明
Candle.exe将 WiX 源文件转换为中间表示形式。这实际上是另一个 XML 文件,但永远不应手动更改这些生成的文件。
Dark.exe将 MSI 文件转换为适当的 WiX 源文件(可以视为“反编译”安装程序)。
Light.exe从 WiX 源文件的中间表示形式生成 Windows Installer。
Lit.exe生成 WiX 库,该库可用于生成其他安装程序包。
Tallow.exe用于创建 WiX 源 XML,以复制它在安装目录或文件中的文件和文件夹。
WixCop.exe检查 WiX 源文件中存在潜在问题的区域,类似于 FxCop。

Figure 2 定义组件
<Component Id='TaskBinFiles' Guid='38736E2E-BEB7-48A9-A2B3-138A57A69D45'>
   <File Id='MSBCommonDLL' Name='CommoDLL' 
      LongName='Sedodream.MSBuild.Common.dll'
      Source='Sedodream.MSBuild.Common.dll' Vital='yes' DiskId='1'/>
   <File Id='SedodreamLoggersDLL' Name='Logger' 
      LongName='Sedodream.MSBuild.Loggers.dll'
      Source='Sedodream.MSBuild.Loggers.dll' Vital='yes' DiskId='1'/>
   <File Id='SedodreamTasksDLL' Name='Tasks' 
      LongName='Sedodream.MSBuild.Tasks.dll'
      Source='Sedodream.MSBuild.Tasks.dll' Vital='yes' DiskId='1'/>
   
   <File Id='SedodreamTASKS' Name='SeTasks' LongName='Sedodream.tasks'
      Source='Sedodream.tasks' Vital='yes' DiskId='1'/>
   <!-- Merge in the Xsd for the custom tasks we have created to have 
      IntelliSense -->
   <XmlFile Id='SedoSche' 
      File='C:/Program Files/Microsoft Visual Studio 8/
               Xml/Schemas/1033/Microsoft.Build.xsd' 
      Action='createElement' ElementPath='//xs:schema' 
      Name='xs:include' Permanent='no' Sequence='0'/>
   <XmlFile Id='SedoSch2' 
      File='C:/Program Files/Microsoft Visual Studio 8/
               Xml/Schemas/1033/Microsoft.Build.xsd'
      Action='setValue' 
      ElementPath='//xs:schema/xs:include[/[]not(@schemaLocation)[/]]'
      Name='schemaLocation' Value='MSBuild/Sedodream.MSBuild.xsd' 
      Permanent='no' Sequence='1' />
</Component>


Figure 3 文件属性

属性名称说明
Id可用于引用文件的标识符。
Name8.3 格式的文件短名称。
LongName文件的长名称;这是将用在目标计算机上的名称(如果该计算机支持长文件名)。否则使用 Name 值。
Source在要创建安装程序数据库的计算机上的文件的相对路径。
DiskId将包含此文件的介质项的标识符。

Figure 4 功能属性

属性名称说明
Id标识符,用于引用功能。
ConfigurableDirectory允许您从属性设置安装位置,可以由用户通过 UI 确定该位置。
Description对功能的说明,它将通过 UI 向用户显示。
Level指定功能的安装级别。值为零意味着将不安装该功能。小于或等于 INSTALLLEVEL 的非零值意味着安装该功能。
Title功能的标题,如果使用 UI,其值将显示在功能树中。

Figure 5 WiX 的 MSBuild 任务

名称说明
Candle通过调用 WiX 编译器创建 WiX 源文件的中间表示形式。
Lit从中间表示形式创建 WiX 库。
Light通过调用 WiX 连接器,从中间表示形式创建最终的安装程序。

Figure 6 生成安装程序所需的声明

名称说明
ToolPath包含 WiX 安装目录的路径。这是 wix.targets 文件的驻留位置。
OutputName将创建的输出的路径和名称。该名称不应包含任何扩展名,因为中间表示形式和最后的包文件都将使用它。此属性类似于 Candle.exe 和 Light.exe 的 OutputFile 参数。
OutputType表示最后的文件应当是什么。可能的值是包、模块、库和对象。它将确定要调用 Light 还是 Lit 任务,以及所创建的最终文件的扩展名。由于我们要创建安装程序,因此我们将为此属性提供值 package。
Compile包含要编译的 WiX 源文件的 MSBuild 项。
BaseInputPath作为源文件的输入路径。尽管该技术不是必需的,但在大多数情况下应使用它。否则,您必须确保从正确的目录调用 msbuild.exe。

Figure 7 MSBuild 文件 SedodreamsMSI.wproj
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="Build">
  <!--===============================================================
    These must be declared BEFORE the statement 
    that imports the wix.targets file
   ==================================================================-->
  <PropertyGroup>
    <!-- The location pointing where WiX is installed -->
    <ToolPath>C:/Data/Development/WiX/</ToolPath>
    <!-- Required Property by WiX -->
    <OutputName Condition="$(OutputName)==''" >
      $(Configuration)Sedodream</OutputName>
    <!-- Required property by WiX -->
    <OutputType 
      Condition="$(OutputType)==''" >package</OutputType>
    <!-- Input path to source files  -->
    <BaseInputPath  Condition="$(BaseInputPath)==''">
      $(PackageRoot)$(Configuration)</BaseInputPath>
  </PropertyGroup>

  <ItemGroup>
    <!-- Required WiX item.
      Files in this item are sent to the Candle tool.
    -->
    <Compile Include="$(BaseInputPath)/Sedodream.wxs"/>
  </ItemGroup>

  <Import Project="$(ToolPath)wix.targets"/>
</Project>


Figure 11 用于连接元素的属性和项

名称说明
SolutionFilePath用于定义将用来生成产品的解决方案文件的位置的属性。
PackageRoot用于定义在哪里生成项目并作为 WiX 的临时生成位置的属性。
WiXSourceFiles包含要发送到 Candle 工具进行编译的所有 WiX 文件的项。
AllConfigurations包含生成此过程要针对的所有配置的项。每个配置都应当包含 FlavorToBuild 和 PlatformToBuild 的元数据值。示例声明是:
<AllConfigurations Include="Release|Any CPU"> <FlavorToBuild>Release</FlavorToBuild> <PlatformToBuild>Any CPU</PlatformToBuild> </AllConfigurations>
OtherFiles可选项,其中包含应当复制到包目录中的其他文件。这些项应当包含 Destination 元数据。此元数据定义了目标相对于包的根目录的相对路径。这是示例声明:
<OtherFiles Include= "../Sedodream.MSBuild.Tasks/SampleTargets/**/*"> <Destination>Samples/</Destination> </OtherFiles>

Figure 12 在示例项目中生成目标

名称说明
Package只调用 DefaultTargets 列表中的目标以执行整个过程。
Build生成解决方案文件。在调用 MSBuild 任务时,通过重写 OutputPath 属性,将把此生成过程的输出发送到正确的目录。
CopyFilesForPackaging将所有未生成但需要用到的文件复制到生成目录。
DeployPackage接近过程结束时调用。在示例中此目标为空,但在实现时可以决定重写该目标,以便将包发送到 QA 环节或部署团队。
Clean清理其他目标造成的混乱。

Figure 13 CoreBuild 目标
<Target Name="CoreBuild"
   Inputs="%(AllConfigurations.PlatformToBuild);
           %(AllConfigurations.FlavorToBuild)"
   Outputs="%(AllConfigurations.PlatformToBuild);
            %(AllConfigurations.FlavorToBuild)" >
   <Message Text="Building for Flavor/Platform:
       %(AllConfigurations.FlavorToBuild)/
       %(AllConfigurations.PlatformToBuild)"/>
   <MSBuild Projects="@(SolutionFile)"
      Targets="Build"
      Properties="OutputPath=$(PackageRoot)
         %(AllConfigurations.FlavorToBuild);
      Configuration=%(AllConfigurations.FlavorToBuild);
      Platform=%(AllConfigurations.PlatformToBuild)">
      <Output ItemName="OutputFiles" TaskParameter="TargetOutputs"/>
   </MSBuild>
</Target>


Figure 14 Sedodream.Package.dproj MSBuild 项目
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" 
         DefaultTargets="Package">

   <!-- Required properties by the deployment.targets file -->
   <PropertyGroup>
      <SolutionFilePath>../Sedodream.MSBuild.sln</SolutionFilePath>
      <PackageRoot>../Package/</PackageRoot>
   </PropertyGroup>

   <ItemGroup>
      <WiXSourceFiles Include="Sedodream.wxs"/>
      <OtherFiles Include="../Sedodream.MSBuild.Tasks/
         SampleTargets/**/*">
         <Destination>Samples/</Destination>
      </OtherFiles>
      <!-- Copy the license to the BaseSearchPath so it can 
         be included into the installer -->
      <OtherFiles Include="License.rtf">
         <Destination></Destination>
      </OtherFiles>
      <!-- Copy the custom installer bitmaps -->
      <OtherFiles Include="bitmaps/dlgbmp.bmp">
         <Destination>bitmaps/</Destination>
      </OtherFiles>
   </ItemGroup>

   <!-- Define all the configurations that you want to build here -->
   <ItemGroup>
      <AllConfigurations Include="Debug|x86">
         <FlavorToBuild>Debug</FlavorToBuild>
         <PlatformToBuild>Any CPU</PlatformToBuild>
      </AllConfigurations>
      <AllConfigurations Include="Release|Any CPU">
         <FlavorToBuild>Release</FlavorToBuild>
         <PlatformToBuild>Any CPU</PlatformToBuild>
      </AllConfigurations>
   </ItemGroup>

   <PropertyGroup>
      <DropLocation>C:/Data/Drops/MSBuild-Wix/</DropLocation>
   </PropertyGroup>
   
   <!-- Import the deployment target to do all the work for us -->
   <Import Project="Sedodream.Package.targets"/>
</Project>


Figure 15 在 Sedodream.Package.targets 中的自定义目标

目标名称说明
Build驱动产品的生成。
BeforeBuild在生成产品之前执行。
AfterBuild已完成生成之后执行。
GetLatest负责从存储库获得最新源文件,默认情况下为空。
LabelSources执行它可标注存储库中的源文件,默认情况下为空。
Package负责将产品打包。
BeforePackage打包过程开始之前执行。
AfterPackage打包过程完成之后执行。
DeployPackage重写它可将包发送到部署团队。
CopyFilesToDrop将在过程结束时使输出文件复制到其中的位置(如果在属性 DropLocation 中定义了丢弃位置)。
Clean负责清理生成的文件。
PackageBreak发生错误时执行。
BeforePackageBreak调用错误处理程序之前执行。
AfterPackageBreak完成错误处理程序之后执行。
InstallMSI用于在执行计算机上安装产品。可以在开发期间使用它来调试打包过程。
UninstallMSI用于在执行计算机上卸载产品。可以在开发期间与 InstallMSI 一同使用,以调试打包过程。
 
Visual Studio C++项目自动化版本号管理:从原理到实战 在软件工程中,版本号是标识软件迭代状态的核心元数据,遵循语义化版本(SemVer)等规范,能清晰传达API兼容性与功能变化。其原理在于将主版本、次版本、修订号及构建号等信息结构化嵌入二进制文件资源段,实现构建产物的唯一标识与生命周期管理。这项技术的工程价值在于保障团队协作、持续集成与问题排查的可追溯性,是DevOps实践的关键环节。应用场景广泛覆盖从动态链接库(DLL)、静态库模块管理到安装部署流程。针对Visual Studio C++开发环境,通过属性表(Property Sheets)与预生成事件构建 阅读详情

相关推荐

Visual Studio开发环境搭建与核心功能解析

集成开发环境(IDE)是现代软件开发的核心工具,通过集成代码编辑、编译调试等功能大幅提升开发效率。Visual Studio作为微软旗舰级IDE,采用模块化架构设计,支持通过工作负载定制安装组件。其智能编码系统IntelliCode基于AI技术提供上下文感知的代码补全,配合强大的调试工具链可快速定位性能瓶颈。在云原生和跨平台开发场景中,Visual Studio提供Azure服务集成和.NET跨平台支持,结合扩展生态可满足从Web开发到游戏编程的多样化需求。对于企业级开发,ReSharper等扩展能有效提升

weixin_30635053的博客 292

Windows Installer XML,wxs文件概述

在讲windows installer xml 描述文件之前, 首先讲讲Windows Installer(以前是Microsoft installer 所以有简写MSI,后来改名Windows Installer, 但仍然使用使用大家熟悉的msi后缀)。 Windows Installer改变了软件安装的方式,以前的大多数的安装软件所做的仅仅是拷贝文件和写注册表,而通常不会去关心系统是否已经装有什么。Windows Installer 把软件分成“Product”(比如像Office这样的软件),.

whl0071的专栏 1131

light.exe 屏幕面前没有人就调低屏幕亮度

通过笔记本电脑的摄像头检测屏幕面前是否有人,如果没有人就降低屏幕亮度,当人回来时,又会恢复屏幕亮度,同时也会检测是不是坐的太近

利用Wix打包安装包

利用Wix打包安装包背景具体步骤1、安装 WiX Toolset 工具集2、安装 WiX Toolset 系列 Visual Studio 插件3、创建Wix工程4、添加工程文件5、修改Product元素6、修改Package元素7、修改MajorUpgrade元素8、修改Media属性9、设置安装引导界面10、添加WPF项目文件11、添加桌面快捷方式12、添加菜单快捷方式13、设置安装引导界面语言环境14、最后设置源码 背景 将指定WPF项目利用Wix打包为安装包! 具体步骤 以打包名为 MRISoftw

m0_51961114的博客 4871

2.1 量子精密测量:SERF原子陀螺仪中的核自旋-电子自旋强耦合机制

本文深入解析了SERF原子陀螺仪中核自旋-电子自旋强耦合机制的核心原理与应用。通过量子精密测量技术,SERF陀螺仪实现了超高精度的惯性导航,其独特的磁场自补偿和拉莫尔进动特性使其在弱磁场环境下表现出色。文章还探讨了关键技术突破和前沿进展,展示了量子精密测量在惯性导航领域的巨大潜力。

kotlin6android的博客 210

5分钟搞懂异构图注意力网络(HAN):从元路径到节点聚合的全流程解析

本文深入解析异构图注意力网络(HAN)的核心原理与实战应用,从元路径设计到多级注意力实现的全流程。HAN通过节点级和语义级双重注意力机制,有效处理包含多种节点和边类型的异构图数据,在电商推荐、学术网络分析等场景展现卓越性能。文章提供DGL实现代码和调参技巧,帮助开发者快速掌握这一强大工具。

weixin_29056701的博客 311

Wix toolset打包工具介绍

wix toolset windows安装包打包工具

PRML_MAN的博客 2828

Vue3 + MQTT.js 实现 SCADA 实时数据通信:从不稳定到稳定的完整实践

本文介绍了SCADA组态引擎开发中MQTT协议的应用及优化实践。项目采用MQTT协议实现工业设备数据采集与可视化,但面临连接不稳定、数据格式多样化和数据绑定复杂三大问题。针对MQTT连接问题,分析发现超时设置过短(原8秒)、缺少客户端超时配置和心跳机制是主要原因。优化方案包括:协议转换(mqtt://→ws://)、双重超时保护(15秒手动+30秒客户端)、60秒心跳间隔等,显著提升了连接稳定性。通过完整代码实现展示了连接配置、认证处理和错误处理机制,为物联网数据传输提供了可靠解决方案。

weixin_36794508的博客 1398

Windows Installer XML (WiX) 使用记录 (一)

被 Wix (Windows) 折腾了很长的一段时间。为了确保以后不要被同样的问题折腾,决定把知识的都记录下来。   最简单的安装包。 1. 创建一个 bland solution,命名为 WixSample 2. 添加一个 C# 的 Console Application,命名为 HelloWorld,然后就是HelloWorld 的标准写法了。(代码) 3. 假设这个Console

CGabriel的专栏 2117

三甲医院网络架构与安全建设实战

华为OceanStor] [华为OceanStor][PACS存储双活] [HIS数据库同步]患者隐私 数据脱敏 天清Web防火墙动态脱敏策略。主数据中心 --[OTV专线]-- 备数据中心。实现医疗业务网/卫生专网/互联网三网隔离。核心业务配置(华为CE6865)自动备份配置(每天02:00)安全事件响应(30分钟SLA)漏洞扫描(每周日00:00)实现医疗物联网统一接入管控。五、等保2.0合规关键配置。堡垒机登录(双因素认证)四、医疗物联网安全方案。

有小小的远大抱负 1910

Winform美化框架AntdUI中Table的使用方法

使用核心注意点: 数据模型中属性名需要完全与new Column里的列名一致否则添加数据源则无法读取到详细内容。

wevix的博客 3676

使用WIX打包.net7.0的应用程序安装包

由于项目需要,需要.net7开发的winform应用程序进行打包生成msi安装包,因为是.net7的项目,所以选择了wix进行安装包的打包处理,但是网上搜到的相关的技术文档都比较老,而且多数使用的wix3.0版本对.net framework的winform应用程序的打包。经过阅读官网,终于实现了打包的功能,于是将自己实现方式做了一个整理和分享。参考链接:链接:wix官网firegiant官网以上就是我打包一个使用.net7.0开发的应用程序的全过程。

weixin_44531910的博客 2799

AD20/Altium designer——如何进行DRC检查、冲突的错误如何改正

对于一个画完的PCB,我们常常需要进行DRC检查,确保板子的电器连接及制作工艺在设定规则的范围内,本篇将介绍如何对PCB进行后期DRC检查处理,确保电路板出现不必要错误。 1.DRC检查入口 2.DRC设置 3.错误分析 对于错误的内容,依据个人实际情况不同,其出现的原因都是因为与设计规则中的设定标准冲突,这里按遇到的错误进行修改讲解,其他错误同理。 4.常见错误分析及解决方法 (1) 解决方法:将未连接网络连接(...

嵌入式创客工坊的博客 5万+

WiX Toolset:构建Windows安装包的强大工具

WiX Toolset 是一个开源的Windows安装包构建工具,它允许开发者通过XML源代码生成Windows安装包(MSI)和可执行的安装包。WiX Toolset 提供了一个命令行环境,开发者可以将其集成到他们的构建流程中,从而自动化生成安装包。WiX GitHub项目托管了WiX Toolset的源代码Git仓库,提供了丰富的文档和资源,帮助开发者快速上手并深入使用。 ## 项目技术分析...

gitblog_01158的博客 818

Altium Designer实战:手把手教你设计智能车竞赛疯狂电路组赛道(附封装库)

本文详细介绍了使用Altium Designer软件进行智能车竞赛“疯狂电路”组赛道设计的全流程实战指南。从理解比赛规则、搭建设计环境、创建专用元器件封装库,到电路布局、路径规划及设计输出验证,手把手教你高效完成符合计分标准的赛道图纸设计,并附上实用的封装库管理方法。

alice7model的博客 1320

手把手教你学Simulink——机器人控制场景实例:基于Simulink的双足人形机器人步态生成与ZMP稳定控制仿真

ZMP是地面上合力矩在水平方向为零的点。对于平面行走: [ x_{\text{zmp}} = x_c - \frac{z_c}{g} \ddot{x}_c ]📌稳定性判据ZMP必须始终位于支撑脚的凸包(即脚掌范围内)本文完成了基于Simulink的双足人形机器人ZMP稳定控制仿真,实现了:✅ 基于LIPM生成稳定CoM轨迹✅ 构建ZMP参考与验证机制✅ 设计简化踝关节PD控制器✅ 验证ZMP始终位于支撑脚内 → 动态平衡✅ 为人形机器人步态规划奠定理论基础。

MHD0815的博客 138

WiX Toolset开源解决方案:企业级Windows安装包自动化构建框架

WiX Toolset是基于XML的Windows Installer开源工具集,专为技术架构师和DevOps团队设计,解决企业级软件分发中的标准化、自动化和可维护性挑战。通过声明式XML语法和模块化设计,WiX实现了从源代码到安装包的全流程自动化构建,支持MSI、MSP、MSM等多种Windows Installer格式,为企业软件部署提供标准化技术栈。 ## 价值主张:从复杂部署到标准化分发

gitblog_01183的博客 1091

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

企业级Windows安装包构建:WiX Toolset完整解决方案深度解析

在当今企业软件分发生态中,78%的Windows应用开发者面临安装包配置复杂、版本控制混乱和自动化集成困难等挑战。WiX Toolset作为专业的Windows Installer XML开源工具集,提供了从源代码到安装包的完整企业级解决方案。基于XML的声明式语法不仅实现了安装逻辑的精准控制,更能无缝融入现代DevOps流程,成为企业级软件分发的技术基石。WiX Toolset的核心价值在于将复

gitblog_00248的博客 826

华为的C语言编程规范.pdf

华为技术有限公司内部技术规范。本规范制定了编写C语言程序的基本原则、规则和建议。从代码的清晰、简洁、可测试、安全、程序效率、可移植各个方面对C语言编程作出了具体指导。

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

WiX Toolset技术解密:Windows安装包构建系统的架构演进与实践重构

在Windows应用分发领域,传统安装包制作面临三大技术挑战:配置复杂度高、版本控制困难、自动化集成缺失。WiX Toolset通过XML声明式语法和模块化构建系统,将Windows Installer的底层复杂性转化为可版本化、可测试的代码驱动流程。这一开源解决方案不仅解决了企业级软件分发的标准化问题,更实现了从源代码到安装包的完整DevOps流水线。 ## 技术实现原理:编译-链接-验证的三

gitblog_00487的博客 364

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

WiX Toolset:告别安装包制作烦恼的终极免费解决方案

还在为Windows安装包制作头疼吗?每次发布新版本都要手动配置一堆文件,测试各种兼容性问题,最后还要担心安装失败?今天我要给你介绍一个神器——WiX Toolset,这可能是你见过最强大、最灵活的开源Windows安装包制作工具! **WiX Toolset**(Windows Installer XML Toolset)是一个完全免费的开源工具集,它用XML语言重新定义了Windows安装包

gitblog_00027的博客 759

MSDN杂志上Wix相关的文章

使用 MSBuild 和 Windows Installer XML 执行自动发布: http://msdn.microsoft.com/zh-cn/magazine/cc163456.aspx 用于创建可靠的最佳实践 Build,第 1 部分:http://msdn.microsoft.com/zh-cn/magazine/dd419659.aspx 用于创建可靠的最佳实践 Build,第 ...

weixin_34417635的博客 76

[转]WiX v3.7——支持MSBuild、自更新及引用计数

最近发布的Windows Installer XML (WiX) 工具包v3.7包含了对MSBuild的完整支持,并使开发者能够通过Bootstrapper应用程序来实现程序的自更新(self-update)。此版本还包含了引用计数(reference-counting)功能,借助该功能,Burn引擎能够对引用的MSI和MSP包进行自动计数。 WiX使开发人员能够从XML源代码创建Windows...

weixin_34290000的博客 167

[翻译] WiX 教程 - 简介

原文地址:http://wix.tramontana.co.hu/tutorial/ 上一节 << 返回目录 >> 下一节 WiX 教程 Windows Installer XML (WiX) 是一套帮助您从XML源文件中创建Window安装包的工具集。该工具集可在命令行环境中创建MSI和MSM安装包,这样开发人员不仅可以将其和以前的 M...

weixin_30684743的博客 242

记录 WixV3 打包WPF应用的过程,制作一个简单的Msi格式安装包

Wix Toolset 制作安装包。快速理解配置对应关系

qq_21216109的博客 3549

Wix 软件打包(一)

简单的wix安装打包   对winform或者wpf打包visual studio有自己的插件VS Installer,但是使用时遇到一些问题,如桌面快捷方式指向的文件不是运行程序,安装运行环境时每个环境都需要询问无法跳过等,在网上看到Wix(Windows Intstall XML)打包的方式。Wix是一种使用xml脚本配置部署的打包方式 1、首先在官网(https://wixtoolset.org/)下载WiX Toolset build tools 进行安装 点击Install安装,我这里是已经安装完

qq_35159235的博客 3963

Windows Installer XML (WiX) 部署程序包 批量添加文件

什么是 WiX? Windows Installer XML (WiX) 平台是一组工具与规范,使您能够创建 Microsoft Windows Installer 数据库文件(MSI 和 MSM)。WiX 工具承袭了根据源代码创建可执行文件所使用的传统编译和链接模型。WiX 的源代码是使用 XML 文件编写的。WiX 源代码经过预处理、编译与链接,以创建 Windows Installer 数...

yurhzzu的博客 1031
上一篇: 继续学习WIX
下一篇: wix-tallow使用
workdog
博客等级 码龄20年 40粉丝 13原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值