MSIX 打包 Win 程序技术文档

一个简单项目的演示的Demo

MsixAppDemo.zip

1. 对比 MSIX 和 EXE 上架 Microsoft Store 的优缺点。

1.1. 使用 MSIX 打包 Win 程序上架 Microsoft Store 的优点

(1) 可触达的用户更多。 Windows 操作系统中新版本的 Microsoft Store 能够直接下载安装 exe 程序,但是很多 Windows 10 、 Windows 11 的用户都是不会手动更新 Microsoft Store ,这些用户都无法通过 Windows 10 、 Windows 11 自带的 Microsoft Store 下载安装开发者提交的 exe 程序。 MSIX 打包 Win 程序能够支持 Windows 10 1809 及以后版本操作系统自带的 Microsoft Store 下载安装。

MSIX 特性支持情况
https://docs.microsoft.com/zh-cn/windows/msix/supported-platforms

(2) 支持检查安装状态。目前所有版本的 Microsoft Store 都不支持检查从 Microsoft Store 安装的 exe 程序是否已经安装,但是MSIX 打包 Win 程序能够支持检查是否已经安装、是否可以更新。

1.2. 使用 MSIX 打包 Win 程序上架 Microsoft Store 的缺点。

以下缺点都在 MSIX 满足上架 Microsoft Store 需要满足的受限功能的场景下,进行介绍。

受限功能:https://docs.microsoft.com/zh-cn/windows/uwp/packaging/app-capability-declarations#restricted-capabilities

(1) MSIX 部署原生不支持创建快捷方式。部署的可执行程序不支持直接被调用,需要借助其他程序在沙箱中运行对应的程序。

示例:
explorer.exe shell:appsFolder\MsixAppDemo_y21qvjamj4ssj!MsixAppDemo

需要注意的是,直接借助explorer.exe 启动 MSIX 程序,是无法像常规程序的快捷方式那样传递参数的。

(2) MSXI 的程序图标不支持透明,如果你的图标中有透明的部分,透明部分只可以根据开发者的设置,选择使用系统主题色填充,或者指定的颜色填充。

(3) MSXI 不支持 .NET 编写的文件资源管理器缩略图COM组件、预览COM组件,不过它支持 原生C++ 编写的文件资源管理器缩略图COM组件、预览COM组件。

(4) 在 MSIX 满足 上架 Microsoft Store 的场景下, MSIX 不支持自定义安装、卸载程序。如果 MSIX 不上架 Microsoft Store ,使用受限功能的的场景下是支持定义安装、卸载程序。

(5) 在 MSIX 满足 上架 Microsoft Store 的场景下, 程序都被放在类似沙盒的环境中,通用库的以外的文件夹、文件、注册表都存放在虚拟的文件夹中。其中,通用库包括:公用视频、公用图片、公用文档、公用下载、公用音乐、公用视频、公用图片、公用文档、公用下载、公用音乐、我的视频、我的图片、我的文档、我的下载、我的音乐、我的视频、我的图片、我的文档、我的下载、我的音乐。%appdata% 文件夹在有些版本的 Windows 系统上会放在虚拟的文件夹,在有些版本的 Windows 系统上会直接放在真实文件夹。

2. 创建 MSIX 包的方法

创建 MSIX 包的方法大致上有3种,其中前2种的无法充分发挥 MSIX 能力,但是很适合新手入门 MSIX 打包,第3种方法能够充分发挥 MSIX 能力,并且支持打包服务器创建 MSIX 包。

2.1. 使用 MSIX Packaging Tool 将已有安装程序创建 MSIX 包

微软文档:
https://docs.microsoft.com/zh-cn/windows/msix/packaging-tool/create-an-msix-overview

这是最简单的创建 MSIX 包的方法,不过也是最无法发挥 MSIX 能力的方法。

最简单的原因:

你只要从Windows 10 1809 及以后版本操作系统中的 Microsoft Store 中下载安装 MSIX Packaging Tool 工具,执行这个工具,运行已有安装程序,就可以通过录制安装行为,创建 MSIX 包。如果你向减少录制多余的内容,可以先安装一个虚拟机,并在这虚拟机执行前面的步骤。

安装虚拟机的教程:
https://docs.microsoft.com/zh-cn/windows/msix/packaging-tool/quick-create-vm

2.2. 使用 Visual Studio 中创建 MSIX 包

微软文档:
https://docs.microsoft.com/zh-cn/windows/msix/desktop/vs-package-overview

在 Visual Studio 2017 15.5 及更高版本中,通过 “工具” > “获取工具和功能”,打开 “正在修改”弹窗。

在这里插入图片描述
勾选 “通用 Windows 平台开发”,勾选 “Windows 10 SDK(10.0.19041.0)”,点击 “下载时安装”,等待安装完成。

在这里插入图片描述可以 通过 “新建” > “项目” ,打开 “创建新项目” 弹窗,选择 “Windows 应用程序打包项目” 。
在这里插入图片描述在这里插入图片描述
在 “依赖项” 上右键,点击 “添加项目引用”,添加你的主程序对应的项目。
需要注意的是,这里只支持添加 .NET Framework 4.6.1 及以上版本的项目。

在这里插入图片描述在打包项目上右键,选择 “Publish” > “创建应用程序包§”,就可以进入创建 MSIX包流程。

在这里插入图片描述

2.3. 使用命令行创建 MSIX 包

微软文档:
https://docs.microsoft.com/zh-cn/windows/msix/package/manual-packaging-root

2.3.1 准备打包环境

确认 makeappx.exe 是否存在。根据 SDK 的安装路径,以下是makeappx.exe 在 Windows 10 电脑上的位置:

C:\Program Files (x86)\Windows Kits\10\bin<build number><architecture>\makeappx.exe
其中 = x86、x64、arm、arm64 或 chpe。

示例:
C:\Program Files (x86)\Windows Kits\10\bin\10.0.18362.0\x86\makeappx.exe
C:\Program Files (x86)\Windows Kits\10\bin\10.0.19041.0\x86\makeappx.exe

如果没有找到makeappx.exe,也可以通过安装 “2.2. 使用 Visual Studio 中创建 MSIX 包” 中介绍的环境。如果为了节约空间,也可以只安装 Windows 10 SDK(10.0.19041.0) 。

2.3.2 MSXI打包的主要内容

MSIX打包最主要的内容:被打包的文件夹的结构、被打包的文件夹中的AppxManifest.xml 文件。

MSIX安装的时候,会直接复制文件夹中所有内容,到分配给程序的虚拟目录;根据AppxManifest.xml 文件的声明,注册程序和组件。所以,AppxManifest.xml 文件能够声明的哪一些信息,就代表 MSIX 打包支持哪一些能力。

AppxManifest.xml 支持的能力文档:
https://docs.microsoft.com/zh-cn/uwp/schemas/appxpackage/uapmanifestschema/root-elements

使用 MSIX 打包与使用 EXE 打包最大区别是,MSIX 只能够向被打包的文件夹添加文件、文件夹,在AppxManifest.xml 文件中声明程序信息。 EXE 打包能够向 Windows 系统任何文件夹添加文件、文件夹,调用可执行程序,执行安装部署流程,调用 Windows 的组件注册程序、驱动注册程序等等。

2.3.3 一个简单项目的演示

2.3.3.1 需要被打包的文件夹

详细演示见后面的Demo。MsixAppDemo.zip

MsixAppDemo.zip

需要打包的文件夹为ProjectSource,主程序为 MsixAppDemo.exe ,虚拟注册表为Registry.dat
在这里插入图片描述
程序的图标资源位于文件夹 Images
在这里插入图片描述
产品程序、组件、右键多语言资源位于文件夹 Strings
在这里插入图片描述

AppxManifest.xml 支持的能力文档:
https://docs.microsoft.com/zh-cn/uwp/schemas/appxpackage/uapmanifestschema/root-elements

最简单的 AppxManifest.xml 文件的内容

<?xml version="1.0" encoding="utf-8"?>
<Package
  xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
  xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest"
  xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
  xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"
  IgnorableNamespaces="uap mp rescap">
  <Identity
    Name="MsixAppDemo"
    Publisher="CN="Msix App Demo Co.,Ltd", OU=RD, O="Wondershare Technology Co.,Ltd", C=CN"
Version="1.0.0.1" ProcessorArchitecture="x86" />
  <Properties>
    <DisplayName>ms-resource:Resources/PackageDisplayName</DisplayName>
    <PublisherDisplayName>ms-resource:Resources/PublisherDisplayName</PublisherDisplayName>
    <Logo>Images\StoreLogo.png</Logo>
  </Properties>
  <Dependencies>
    <TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.17763.0" MaxVersionTested="10.0.19041.0" />
    <TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.17763.0" MaxVersionTested="10.0.19041.0" />
  </Dependencies>
  <Resources>
    <Resource uap:Scale="200" />
<Resource Language="en-US" />
  </Resources>
  <Applications>
    <Application Id="MsixAppDemo" Executable="MsixAppDemo.exe" EntryPoint="Windows.FullTrustApplication">
      <uap:VisualElements DisplayName="ms-resource:Resources/ApplicationDisplayName"
           Description="ms-resource:Resources/ApplicationDescription"
           BackgroundColor="#101010"
           Square150x150Logo="Images\Square150x150Logo.png" Square44x44Logo="Images\Square44x44Logo.png">
        <uap:DefaultTile ShortName="ms-resource:Resources/TileShortName"
          Wide310x150Logo="Images\Wide310x150Logo.png"
          Square71x71Logo="Images\Square71x71Logo.png">
          <uap:ShowNameOnTiles>
            <uap:ShowOn Tile="square150x150Logo"/>
          </uap:ShowNameOnTiles>
        </uap:DefaultTile>
      </uap:VisualElements>
    </Application>
  </Applications>
  <Capabilities>
    <rescap:Capability Name="runFullTrust" />
  </Capabilities>
</Package>

元素 Identity 的属性 Name

属性 Name 的内容会做为你 msixbundle 的唯一标记的前缀,需要注意的是,唯一标记的后缀,无法由开发者在签名前固定,因为后缀受版本号和签名证书影响。除版本号和架构以外的后缀,在签名证书修改的情况下,一定会改变;否则,一定不会改变。

在这里插入图片描述
演示项目的唯一标记示例:
MsixAppDemo_1.0.0.1_x86__y21qvjamj4ssj
MsixAppDemo_y21qvjamj4ssj

如果没有修改 WindowsApp 安装位置,包内的资源都会复制到文件夹
C:\Program Files\WindowsApps\MsixAppDemo_1.0.0.1_x86__y21qvjamj4ssj

如果修改 WindowsApp 安装位置为D盘,包内的资源都会复制到文件夹
D:\WindowsApps\MsixAppDemo_1.0.0.1_x86__y21qvjamj4ssj

如果使用了控制台可用的别名,支持别名的虚拟程序同时存在于以下2个文件夹:
%AppData%…\Local\Microsoft\WindowsApps
%AppData%…\Local\Microsoft\WindowsApps\MsixAppDemo_y21qvjamj4ssj

程序运行期间对非通用库的文件夹读写、注册表,实际上都是在读写以下文件夹中的文件夹、文件、虚拟注册表
%AppData%…\Local\Packages\MsixAppDemo_y21qvjamj4ssj

元素 Identity 的属性 Publisher

属性 Publisher 的内容必须与你将要使用的签名证书相对应,否则会签名失败。如果要验证点击 msixbundle 文件就进入安装,那边就必须要对 msixbundle 进行签名
在这里插入图片描述

元素 Application

元素Applications中可以包含多个元素Application,但是每个元素Application的属性 Id 必须不同,属性 Id 作为程序入口的唯一标记,可以支持使用文件资源管理器启动 MSIX 中的程序。

示例:
explorer.exe shell:appsFolder\MsixAppDemo_y21qvjamj4ssj!MsixAppDemo

其中,
MsixAppDemo_y21qvjamj4ssj为项目的唯一标记。
MsixAppDemo为元素 Application 的属性 Id 。

在这里插入图片描述

2.3.3.2 准备签名证书

演示项目中,已经提供了1个测试签名证书,证书文件中包含2个证书。
在这里插入图片描述

点击 Msix App Demo Cert CA.pfx,进入安装
在这里插入图片描述

选择存储位置为“当前用户”,多次点击“下一步”。

在这里插入图片描述
输入密码 123456,勾选“标志此密钥可导出的密钥(M)”。
在这里插入图片描述
选择“根据证书类型,自动选择证书存储”,继续点击“下一步”,直至点击“完成”。
在这里插入图片描述

2.3.3.3 开始打包

点击演示项目中的 make_fat_msixbundle.bat ,即可开始自动打包。
在这里插入图片描述

make_fat_msixbundle.bat 文件的内容

@echo off
set CurrentDir=%~dp0
cd %CurrentDir%

set AppName=MsixAppDemo
::Msix包中要合并的多语言资源,要合并的多倍贴图资源。
set dq=/dq en-US_de-DE_fr-FR_scale-200

set ProcessorArchitecture=x86
set TargetSystemVersion=10.0

::被打包的文件夹的位置。
set ProjectSource=%CurrentDir%ProjectSource
::完成打包后,包输出文件夹。
set ProjectOutput=%CurrentDir%Output

::签名证书颁发者名称的一部分。
set CertificateLssuer=Msix App Demo Cert
::签名证书名称的一部分。
set CertificateName=Msix
::时间戳服务器。
set CertificateTime=http://timestamp.digicert.com

set Makepri="C:\Program Files (x86)\Windows Kits\10\bin\10.0.19041.0\x86\makepri.exe"
set Makeappx="C:\Program Files (x86)\Windows Kits\10\bin\10.0.19041.0\x86\makeappx.exe"
set Signtool="C:\Program Files (x86)\Windows Kits\10\bin\10.0.19041.0\x86\signtool.exe"


set IntermediateSplit=%CurrentDir%Temp\IntermediateSplit
set IntermediateBundle=%CurrentDir%Temp\IntermediateBundle

if exist Temp (RMDIR /S /Q Temp)
IF not exist %IntermediateSplit%. (mkdir %IntermediateSplit%)
IF not exist %IntermediateBundle%. (mkdir %IntermediateBundle%)
IF not exist %Output%. (mkdir %Output%)

set appNameXml=%IntermediateSplit%\%AppName%.xml

echo making config
%Makepri% createconfig /cf %appNameXml% %dq% /pv %TargetSystemVersion% /o
IF ERRORLEVEL 1 GOTO GenericError

set resourcesPri=%IntermediateSplit%\resources.pri

echo making pri
%Makepri% new /pr %ProjectSource% /cf %appNameXml% /of %resourcesPri% /mf appx /o
IF ERRORLEVEL 1 GOTO GenericError

set appxManifestXml=%ProjectSource%\AppxManifest.xml
set resourcesMapTxt=%IntermediateSplit%\resources.map.txt
set mainMsix=%IntermediateBundle%\%AppName%_%ProcessorArchitecture%.msix

echo making msixe
%Makeappx% pack /m %appxManifestXml% /f %resourcesMapTxt% /p %mainMsix% /o
IF ERRORLEVEL 1 GOTO GenericError

echo signing msixe
%Signtool% sign /i "%CertificateLssuer%" /fd sha256 /s my /a /n "%CertificateName%" /tr "%CertificateTime%" /td sha256 /d "%AppName%" "%mainMsix%"
IF ERRORLEVEL 1 GOTO SigningError

set mainMsixbundleName=%AppName%__%ProcessorArchitecture%.msixbundle
set mainMsixbundleNamePath=%ProjectOutput%\%mainMsixbundleName%
set mainMsixbundle=%ProjectOutput%\%mainMsixbundleName%

echo making msix bundle
%Makeappx% bundle /d %IntermediateBundle% /o /p %mainMsixbundle%
IF ERRORLEVEL 1 GOTO GenericError

echo signing bundle
%Signtool% sign /i "%CertificateLssuer%" /fd sha256 /s my /a /n "%CertificateName%" /tr "%CertificateTime%" /td sha256 /d "%AppName%" "%mainMsixbundleNamePath%"
IF ERRORLEVEL 1 GOTO SigningError

GOTO End


:GenericError

ECHO.
ECHO ******
ECHO Error %ERRORLEVEL%; stopping batch file. 
ECHO ******
ECHO.
pause

:SigningError
ECHO.
ECHO ******
ECHO Error %ERRORLEVEL%; stopping batch file. 
ECHO Ensure that the Publisher attribute of the Identity element
ECHO in the AppX manifest matches the Issuer in the signing certificate.
ECHO ******
ECHO.
pause


:End

ECHO.
ECHO Done!
pause

常见问题

创建能够带参数的快捷方式

主要思路就是使用了控制台可用的别名,支持别名的虚拟程序同时存在于以下2个文件夹:
%AppData%…\Local\Microsoft\WindowsApps
%AppData%…\Local\Microsoft\WindowsApps\MsixAppDemo_y21qvjamj4ssj

在AppxManifest.xml 文件中,将以下元素添加到元素Application中的元素Extensions中,安装完成后,就会创建别名程序在相应的文件夹。

        <!--使得程序支持在包外通过快捷方式打开。-->
        <uap5:Extension Category="windows.appExecutionAlias">
          <uap5:AppExecutionAlias>
            <uap5:ExecutionAlias Alias="MsixAppDemo.exe" uap8:AllowOverride="true" />
          </uap5:AppExecutionAlias>
        </uap5:Extension>

创建一个快捷方式指向别名程序,就可以传参数了;不过,别名程序是没有图标的,需要给快捷方式添加图标。

如何观察 .msix .msixbundle 文件中的内容

可以直接使用 7-Zip 程序解压 .msix .msixbundle 文件,观察被打包的文件夹,在打包完成后,添加了什么内容。

如何仅在第1次启动程序时,执行一些自定义的部署行为

使用这个方法的意义:
在 MSIX 满足 上架 Microsoft Store 的场景下, MSIX 不支持自定义安装、卸载程序。如果需要执行一些部署行为,可以用这个方案替代。

利用卸载MSIX时,会删除所有虚拟文件夹的特性,在每个版本的程序的第1次启动时,执行一些自定义的部署行为,并将这个版本已经启动过的信息添加到特定的虚拟文件夹,之后启动这个版本时,检查前面写入的信息,如果发现已经启动过,就不再执行这些行为。在程序卸载时,这些信息会因为位于虚拟文件夹,而自动被删除。

示例可写入文件夹:
%AppData%…\Local\Packages\MsixAppDemo_y21qvjamj4ssj\LocalCache

注册文件资源管理器缩略图COM组件、预览COM组件

需要注意,MSXI 不支持 .NET 编写的文件资源管理器缩略图COM组件、预览COM组件,不过它支持 原生C++ 编写的文件资源管理器缩略图COM组件、预览COM组件。

在AppxManifest.xml 文件中,将以下元素添加到元素Application中的元素Extensions中,其中Id、AppId使用COM的Clsid,Path使用包含COM的dll文件相对于被打包的文件夹的路径。

        <!--注册COM。txt资源管理器预览插件PrevewHandler。-->
        <!--Id="AAABAF99-0C5D-4FA8-8CCD-1129EE6D25B9"-->
        <com:Extension Category="windows.comServer">
          <com:ComServer>
            <com:SurrogateServer AppId="815BAF99-0C5D-4FA8-8CCD-1129EE6D25B9">
              <com:Class Id="AAABAF99-0C5D-4FA8-8CCD-1129EE6D25B9" Path="App32\PEPreview4.dll" ThreadingModel="Both">
              </com:Class>
            </com:SurrogateServer>
          </com:ComServer>
        </com:Extension>
        <!--注册COM。txt资源管理器缩略图插件ThumbnailHandler。-->
        <!--Id="AAA3C4E9-D71A-4411-A9CD-1130412C5FC0"-->
        <com:Extension Category="windows.comServer">
          <com:ComServer>
            <com:SurrogateServer AppId="AAA3C4E9-D71A-4411-A9CD-1130412C5FC0">
              <com:Class Id="AAA3C4E9-D71A-4411-A9CD-1130412C5FC0" Path="App32\ThumbnailHandler.dll" ThreadingModel="Both">
              </com:Class>
            </com:SurrogateServer>
          </com:ComServer>
        </com:Extension>
        <uap:Extension Category="windows.fileTypeAssociation">
          <uap:FileTypeAssociation Name="txt">
            <uap:SupportedFileTypes>
              <uap:FileType>.txt</uap:FileType>
            </uap:SupportedFileTypes>
            <!--使得程序出现在文件的右键菜单的 打开方式 的列表中。-->
            <uap2:SupportedVerbs>
              <uap3:Verb Id="open" Parameters=""%1"">open</uap3:Verb>
            </uap2:SupportedVerbs>
            <!--注册txt资源管理器预览插件PrevewHandler。-->
            <desktop2:DesktopPreviewHandler Clsid="AAABAF99-0C5D-4FA8-8CCD-1129EE6D25B9"/>
            <!--txt资源管理器缩略图插件ThumbnailHandler。-->
            <desktop2:ThumbnailHandler Clsid="AAA3C4E9-D71A-4411-A9CD-1130412C5FC0" />
          </uap:FileTypeAssociation>
        </uap:Extension>
      </Extensions>

注册虚拟打印机

MSIX的虚拟打印机和EXE注册虚拟打印机完全不一样,本质上是添加一个处理.xps文件的exe程序,并在AppxManifest.xml 文件中声明这个程序。

详细演示见后面的Demo。MsixPrinterDemo.zip

    <Application Id="Printer"
      Executable="MsixPrinter.exe"
      EntryPoint="Windows.FullTrustApplication">
      <uap:VisualElements
        AppListEntry="none"
        DisplayName="MsixPrinterDemo"
        Description="MsixPrinterDemo"
        BackgroundColor="transparent"
        Square150x150Logo="Images\Square150x150Logo.png"
        Square44x44Logo="Images\Square44x44Logo.png">
        <uap:DefaultTile Wide310x150Logo="Images\Wide310x150Logo.png" />
      </uap:VisualElements>
      <Extensions>
        <desktop2:Extension
          Category="windows.appPrinter"
          Executable="MsixPrinter.exe"
          EntryPoint="Windows.FullTrustApplication">
          <desktop2:AppPrinter DisplayName ="MsixPrinterDemo"  Parameters=""%1"" />
        </desktop2:Extension>
        <uap:Extension Category="windows.fileTypeAssociation">
          <uap:FileTypeAssociation Name="pdf">
            <uap:SupportedFileTypes>
              <uap:FileType>.pdf</uap:FileType>
              <uap:FileType>.txt</uap:FileType>
              <uap:FileType>.doc</uap:FileType>
              <uap:FileType>.docx</uap:FileType>
            </uap:SupportedFileTypes>
          </uap:FileTypeAssociation>
        </uap:Extension>
      </Extensions>
    </Application>

使用 MSIX Hero 查看已安装 .msix .msixbundle 的注册表

文档:
https://msixhero.net/documentation/viewing-virtual-registry-keys-from-an-msix-app/
在这里插入图片描述

如何创建虚拟注册表文件

在这里插入图片描述在这里插入图片描述在这里插入图片描述

  • 20
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值