You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用CPack+WiX制作MSI:超2GB安装包拆分多CAB及DiskId分配方法

解决CPack+WiX拆分MSI CAB文件的DiskId分配问题

要给CMake中安装的文件指定DiskId从而拆分到不同CAB包,CPack WiX本身没有直接的内置变量,但可以通过自定义属性+XSLT补丁或者路径匹配XSLT补丁两种方式实现,下面是具体步骤:

方法一:通过INSTALL属性传递自定义标识(推荐,灵活可控)

这种方法可以在CMake层面为不同文件/目录标记对应的DiskId,再通过WiX补丁自动映射。

1. 修改CMakeLists.txt,添加自定义属性

给需要拆分的文件/目录添加CPACK_WIX_DISK_ID自定义属性,对应你在WiX补丁中定义的Media Id:

cmake_minimum_required(VERSION 3.14)
project(content 1.0.0 LANGUAGES CXX)

# 给video目录下的视频文件标记DiskId=2
install(DIRECTORY video DESTINATION "." FILES_MATCHING REGEX ".+\.(avi|mp4)"
        PROPERTIES CPACK_WIX_DISK_ID "2")

# 给images目录下的图片文件标记DiskId=3
install(DIRECTORY images DESTINATION "." FILES_MATCHING PATTERN "*.png"
        PROPERTIES CPACK_WIX_DISK_ID "3")

set(CPACK_WIX_PATCH_FILE "${CMAKE_CURRENT_SOURCE_DIR}/misc/wix_patch.xml")
include(CPack)

2. 更新wix_patch.xml,添加XSLT补丁处理属性

用XSLT读取CMake传递的自定义属性,为WiX的<File>元素添加DiskId,同时移除自定义属性避免WiX报错:

<CPackWiXPatch>
  <CPackWiXFragment Id="#PRODUCT">
    <Media Id='2' Cabinet='package1.cab' EmbedCab='no'/>
    <Media Id='3' Cabinet='package2.cab' EmbedCab='no'/>
  </CPackWiXFragment>

  <!-- XSLT补丁:将自定义属性转换为DiskId -->
  <CPackWiXXslPatch>
    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:wix="http://schemas.microsoft.com/wix/2006/wi">
      <!-- 匹配带有自定义属性的File元素 -->
      <xsl:template match="wix:File[@CPACK_WIX_DISK_ID]">
        <xsl:copy>
          <!-- 复制原有所有属性 -->
          <xsl:apply-templates select="@*"/>
          <!-- 添加DiskId属性,值来自自定义标识 -->
          <xsl:attribute name="DiskId">
            <xsl:value-of select="@CPACK_WIX_DISK_ID"/>
          </xsl:attribute>
          <!-- 移除自定义属性,防止WiX解析报错 -->
          <xsl:attribute name="CPACK_WIX_DISK_ID" xmlns="" />
        </xsl:copy>
      </xsl:template>

      <!-- 保留其他所有元素和属性不变 -->
      <xsl:template match="@*|node()">
        <xsl:copy>
          <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
      </xsl:template>
    </xsl:stylesheet>
  </CPackWiXXslPatch>
</CPackWiXPatch>

方法二:直接通过文件路径匹配(无需修改CMake)

如果你的文件路径规则明确,可以跳过CMake属性设置,直接在WiX补丁中用XPath匹配文件路径,分配对应的DiskId:

<CPackWiXPatch>
  <CPackWiXFragment Id="#PRODUCT">
    <Media Id='2' Cabinet='package1.cab' EmbedCab='no'/>
    <Media Id='3' Cabinet='package2.cab' EmbedCab='no'/>
  </CPackWiXFragment>

  <CPackWiXXslPatch>
    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:wix="http://schemas.microsoft.com/wix/2006/wi">
      <!-- 匹配video目录下的avi/mp4文件,分配DiskId=2 -->
      <xsl:template match="wix:File[contains(@Source, 'video/') and (ends-with(@Source, '.avi') or ends-with(@Source, '.mp4'))]">
        <xsl:copy>
          <xsl:apply-templates select="@*"/>
          <xsl:attribute name="DiskId">2</xsl:attribute>
        </xsl:copy>
      </xsl:template>

      <!-- 匹配images目录下的png文件,分配DiskId=3 -->
      <xsl:template match="wix:File[contains(@Source, 'images/') and ends-with(@Source, '.png')]">
        <xsl:copy>
          <xsl:apply-templates select="@*"/>
          <xsl:attribute name="DiskId">3</xsl:attribute>
        </xsl:copy>
      </xsl:template>

      <!-- 保留其他元素不变 -->
      <xsl:template match="@*|node()">
        <xsl:copy>
          <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
      </xsl:template>
    </xsl:stylesheet>
  </CPackWiXXslPatch>
</CPackWiXPatch>

关键注意事项

  • 确保Media元素的Id和你设置的DiskId完全对应,比如Media Id='2'对应DiskId="2"
  • 必须设置EmbedCab='no',拆分的CAB文件不能嵌入到MSI中,会作为独立的外部文件生成
  • 如果存在单个文件超过2GB的情况,这种拆分方法也无法解决,需要将该文件单独分配一个Media节点
  • 可以通过CPACK_WIX_DEBUG=1参数生成MSI时保留中间WiX文件,检查<File>元素的DiskId是否正确设置

内容的提问来源于stack exchange,提问作者Chris

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.07 23:03:01