乐于分享
好东西不私藏

把自己写的软件上架微软商店,是一种怎样的体验

把自己写的软件上架微软商店,是一种怎样的体验

写在前面

本文记录了把 Tydora(一款基于 Tauri v2 的桌面 Markdown 编辑器)发布到微软商店(Microsoft Store)的完整过程,包含 MSIX 打包、Partner Center 配置、GitHub Actions 自动发布的全流程,以及途中踩过的所有坑。

背景:为什么是 MSIX

Tydora 的默认分发方式是 NSIS(.exe)+ MSI(.msi)+ macOS/Linux 包,通过 GitHub Releases 发布。但微软商店只接受 MSIX 格式,且 Tauri CLI 2.11.2 在 Windows 平台的 --bundles 不支持 msix 目标(只有 msi/nsis)。

所以我们的思路是:

1️⃣ 用 tauri build --no-bundle 产出原始 release exe(前端已内嵌到二进制)

2️⃣ 自己写一份 AppxManifest.xml

3️⃣ 用 Windows SDK 的 MakeAppx.exe 把 exe + 清单 + 图标打包成 .msix

4️⃣ 上传到 Partner Center,由微软统一签名后上架

整个过程不依赖 Tauri 的 MSIX bundler,可控且能精确对齐商店身份。

全流程概览

整个上架过程分为 6 个主要步骤,环环相扣:

Step 1 → 注册账号:在 Partner Center 注册开发者账号,保留产品名,获取商店身份信息

Step 2 → 配置环境:在 .env 中填入 MSSTORE_* 参数(Name / Publisher / DisplayName)

Step 3 → MSIX 打包:执行 npm run tauri build:msix,生成商店模式的未签名 MSIX 包

Step 4 → 手动上传:将 MSIX 上传至 Partner Center,填写隐私策略、支持信息、渠道等。首个版本必须手动上传

Step 5 → 等待审核:微软认证审核,周期通常 1-3 个工作日,审核通过即正式上架

Step 6 → 自动化发布:配置 GitHub Actions Secrets,后续版本通过 CI 自动发布

第 1 步:Partner Center 注册与保留产品名

1.1 注册开发者账号

打开 storedeveloper.microsoft.com 用微软账号登录,选「个人开发者」。

💡 小提示:个人号几分钟过审,免费;公司号需 DUNS 或营业执照,2-5 工作日。

1.2 新建产品并保留名称

进入 Partner Center → Apps and games → New product

🚨 重点注意:产品类型必须选「MSIX 或 PWA 应用」(MSIX or PWA app)。如果选了「EXE 或 MSI app」,上传 MSIX 时会被直接拒绝,且产品类型创建后无法更改,只能删掉重建。

输入产品名「Tydora」并保留(名字保留后 3 个月内必须发布,否则失效)。

1.3 拿到商店身份

保留成功后进入应用页面 → 顶部 Product identity(产品标识)页,你会看到 4 个关键信息:

字段示例值用途
Package/Identity/Name<你的Name>(形如 1234567890.TydoraMSIX 清单的 Identity Name
Package/Identity/Publisher<你的Publisher>(形如 CN=XXXX-XXXX-...MSIX 清单的 Identity Publisher
PublisherDisplayName<你的发布者显示名>MSIX 清单的 Properties/PublisherDisplayName
Store product ID<你的ProductID>(形如 9WZDNCRFXXXX后续 CI 自动发布用

🚨 划重点:这三个值(Name / Publisher / PublisherDisplayName)必须和你 MSIX 清单里的值一字不差,否则 Partner Center 校验会打回。下面会讲怎么把这三个值注入清单。

第 2 步:MSIX 清单与打包脚本

2.1 AppxManifest.xml 模板

Tauri 不生成 MSIX 清单,我们自己写一份,放在 src-tauri/msix/AppxManifest.xml

关键点:

xml
<IdentityName="{{PACKAGE_IDENTITY_NAME}}"Publisher="{{PUBLISHER}}"Version="{{VERSION}}"ProcessorArchitecture="x64" /><Properties><DisplayName>Tydora</DisplayName><PublisherDisplayName>{{PUBLISHER_DISPLAY_NAME}}</PublisherDisplayName><Logo>Assets\StoreLogo.png</Logo><Description>A modern Markdown editor built with Tauri</Description></Properties>

三个 {{...}} 占位符由打包脚本在运行时替换为实际值。

🚨 踩过的大坑<Properties> 下子元素顺序有 XML Schema 严格约束——必须是DisplayName → PublisherDisplayName → Logo → Description。如果 Description 写在 Logo 前面,MakeAppx 会报 C00CEE3B(app manifest XML must be valid),但错误信息是乱码(???),很难定位。错误提示 Line 28, Column 15,但真正的问题在 line 25 的元素顺序!

2.2 文件关联(可选)

如果想让应用接管 .md 文件,在 <Applications> 节点加 <Extensions>

xml
<Extensions><uap:ExtensionCategory="windows.fileTypeAssociation"><uap:FileTypeAssociationName="markdown"><uap:SupportedFileTypes><uap:FileType>.md</uap:FileType><uap:FileType>.markdown</uap:FileType><uap:FileType>.mdx</uap:FileType></uap:SupportedFileTypes></uap:FileTypeAssociation></uap:Extension></Extensions>

2.3 能力声明(Capabilities)

xml
<Capabilities><rescap:CapabilityName="runFullTrust" /><CapabilityName="internetClient" /></Capabilities>

runFullTrust 是受限能力,提交时 Partner Center 会强制要求你解释用途(见第 4.3 节)。

2.4 打包脚本 build-msix.ps1

核心流程:

1️⃣ 判定模式:检查 MSSTORE_PACKAGE_IDENTITY_NAME 和 MSSTORE_PUBLISHER 是否同时存在

  • 都存在 → 商店模式(不签名,产物给 Partner Center)
  • 任一缺失 → 本地测试模式(自签名,本地可安装)

2️⃣ 读取 release exesrc-tauri/target/release/tydora.exe

3️⃣ 暂存目录src-tauri/target/msix-staging/,把 exe + 图标 + 生成的 manifest 放进去

4️⃣ MakeAppx pack:打包成 src-tauri/target/msix/Tydora_<version>_x64.msix

5️⃣ 签名(仅本地模式或显式 -Sign):用 Sign-AppxPackage 创建自签名证书并签名

📌 版本号来源:从 VERSION 文件读取,转换为四段式 0.1.4.0(MSIX 要求 Major.Minor.Build.Revision 四段,空位补 0)。

2.5 接入 npm 脚本

scripts/run-tauri.mjs 拦截 build:msix 子命令,转发给 PowerShell 脚本:

javascript
if (subCommand === 'build:msix') {const args = process.argv.slice(3).join(' ');spawnSync('pwsh', ['-File''scripts/build-msix.ps1', ...args], { stdio'inherit' });}

在 package.json 里注册:

json
"scripts":{"tauri":"node scripts/run-tauri.mjs"}

这样就能用 npm run tauri build:msix 打包了。

第 3 步:本地打包

3.1 配置 .env

在项目根目录的 .env 里填入商店身份:

dotenv
# 已有的 Tauri 签名密钥(用于 NSIS/MSI 自动更新签名,不是 MSIX 签名)TAURI_SIGNING_PRIVATE_KEY=...TAURI_SIGNING_PRIVATE_KEY_PASSWORD=...# 微软商店身份(用于 MSIX 打包)MSSTORE_PACKAGE_IDENTITY_NAME=<你的Name>MSSTORE_PUBLISHER=<你的Publisher>MSSTORE_PUBLISHER_DISPLAY_NAME=<你的发布者显示名>

🚨 踩过的第二个坑.env 里的值不要加引号。Tydora 的 loadEnv 解析器只按第一个 = 分割并 trim 两边,不会剥引号。如果写成 MSSTORE_PACKAGE_IDENTITY_NAME="你的Name",引号会被当作值的一部分传给 MakeAppx,导致 Partner Center 校验 Identity Name 时报错——「无效的软件包标识名称: Tydora (应为: 你的Name)」,实际上是因为之前 .env 里写成了 = " 你的Name"(带前导空格和引号)。

3.2 执行打包

powershell
# 完整打包(含 Rust 编译,首次约 5-10 分钟)npm run tauri build:msix# 跳过编译,仅重新打包(已有 release exe 时,秒级)npm run tauri build:msix ---SkipBuild# 跳过签名(商店模式默认就是不签名,加这个只是不显示签名警告)npm run tauri build:msix ---SkipBuild-NoSign

3.3 验证产物

打包成功后,控制台会输出:

Identity Name:        <你的Name>Publisher:            <你的Publisher>PublisherDisplayName: <你的发布者显示名>Mode:                 Store (unsigned)Package creation succeeded.✓ MSIX 已生成: D:\code\Tydora\src-tauri\target\msix\Tydora_0.1.4.0_x64.msix  大小: 6.09 MB

💡 重要说明:本地双击这个未签名 MSIX 会显示「发布者: 未知」并报错 0x800B010A(证书不受信任),这是正常的——商店模式的包本来就不该本地安装,它只应该上传到 Partner Center,由微软用自己的证书统一签名后分发给最终用户。

如果想本地测试安装效果,可以用本地模式(注释掉 .env 里的 MSSTORE_* 三行)打一个自签名的包,本地安装不会报错。

第 4 步:上传到 Partner Center

4.1 开始新提交

进入 Tydora 应用页面 → Start a submission(开始新提交)。需要填的几个块:

内容
Packages上传 .msix 文件
Pricing and availability免费 / 付费区域
Properties应用分类、隐私策略 URL 等
Age ratings年龄分级问卷
Store listing商店页面文案、截图、图标

4.2 上传 Packages

在 Packages 块,把本地生成的 Tydora_0.1.4.0_x64.msix 拖进去。

上传完成后,Partner Center 会自动解析 Identity 信息并校验:

Package identifier:  <你的Name>_0.1.4.0_x64__<PFN后缀>Version:             0.1.4.0Architecture:        x64

如果和你在 Partner Center 注册的 Name/Publisher 一致,就显示绿色 ✓;否则会报类似:

无效的软件包标识名称: Tydora (应为: <你的Name>)无效的软件包发布者名称: CN=Tydora (应为: <你的Publisher>)

这时候你需要:

1️⃣ 检查 .env 里的 MSSTORE_PACKAGE_IDENTITY_NAME / MSSTORE_PUBLISHER 是否正确

2️⃣ 用 -SkipBuild 重新打包(秒级)

3️⃣ 在 Packages 页先删除旧包,再上传新包

4.3 填写受限功能说明(runFullTrust)

因为 MSIX 清单里声明了 rescap:Capability Name="runFullTrust",Partner Center 会要求你在「Properties」或「Notes for certification」里解释用途。

这是我填写的内容,供参考

Tydora 是一款桌面 Markdown 编辑器,采用 Tauri v2 框架构建,核心工作模式为「本地优先」——所有笔记、文件、配置都保存在用户自己的设备上,不经过任何云端服务器。

runFullTrust 是 Tauri 桌面应用的标准要求,具体用于:

  1. 文件系统读写:用户通过「仓库」机制选择本地文件夹作为笔记存储位置,应用需要直接读取和写入该文件夹下的 .md 文件、图片附件等。
  2. 文件系统监听:当用户使用其他编辑器同时编辑笔记时,应用需要监听本地文件变更(通过 Rust notify crate),以实时同步链接索引。
  3. 系统文件对话框:使用 Tauri plugin-dialog 提供原生打开/保存对话框。
  4. 调用外部程序:用系统默认应用打开文件、在文件管理器中定位文件等。
  5. 发布网站功能:调用本地 Node.js CLI 将 Markdown 构建为静态网站。

应用不收集任何用户个人数据,所有操作均在本地完成;不包含任何第三方分析 SDK 或广告 SDK。runFullTrust 仅用于上述桌面应用场景,不会绕过系统安全机制。

4.4 隐私策略 URL

微软商店强制要求所有应用提供隐私策略 URL。我在文档站专门加了一个页面:「隐私策略 · Tydora」。

隐私策略内容要点(按微软商店审查清单):

✅ 明确「不收集个人数据」的本地优先声明

✅ 哪些「仅本地」的非个人数据会写入 localStorage

✅ 自动更新机制说明(仅 HTTPS 版本检查,无用户数据)

✅ MSIX 微软商店版本的 Microsoft 端遥测说明

✅ 不使用第三方分析/广告 SDK

✅ 数据安全、保留与删除方式

✅ 儿童隐私、用户权利、策略更新

4.5 商店页面素材

Store listing 块需要准备:

  • 至少 1 张截图(推荐 1920×1080,PNG)
  • 应用图标(512×512)
  • 简短描述(≤ 200 字符)
  • 详细描述(≤ 10000 字符)
  • 发行说明(可选)

4.6 提交审核

填完所有块后,点 Submit for certification。状态变化:

Pending → Pre-processing → Certification → Publishing → In the Store

整个认证流程通常 1-3 个工作日。如果被拒,Partner Center 会告诉你具体原因(比如隐私策略 URL 不可访问、runFullTrust 说明不充分等),修改后重新提交即可。

第 5 步:首个版本过审后的 CI 自动发布

微软硬性规定:首个版本必须手动上传,之后版本才能通过 API 自动发布。

5.1 关联 Microsoft Entra ID

在 Partner Center:

1️⃣ 右上角齿轮 → Account settings → Organizations(或 Entra ID / Azure AD)

2️⃣ 关联你的 Microsoft Entra ID 租户(个人开发者可以用自己的微软账号对应的默认租户)

3️⃣ 在 Entra ID 里注册一个应用:Azure Portal → App registrations → New registration

4️⃣ 给这个应用赋予 Partner Center 的 Manager 角色

5.2 拿到 4 个 Secrets 值

Secret Name在哪里拿
AZURE_AD_TENANT_IDEntra ID → 应用注册 → 概览 → 目录(租户) ID
AZURE_AD_APPLICATION_CLIENT_ID同上 → 应用程序(客户端) ID
AZURE_AD_APPLICATION_SECRET同上 → 证书和密码 → 新建客户端密码 → 生成的值(仅显示一次!)
SELLER_IDPartner Center → 账户设置 → Legal Info → Seller ID

5.3 配置 GitHub 仓库

进入仓库 Settings → Secrets and variables → Actions

🔒 Secrets(敏感信息)

NameValue
AZURE_AD_TENANT_ID上面的租户 ID
AZURE_AD_APPLICATION_CLIENT_ID上面的客户端 ID
AZURE_AD_APPLICATION_SECRET上面的客户端密码
SELLER_ID上面的 Seller ID

📋 Variables(非敏感,控制开关)

NameValue
MSSTORE_PACKAGE_IDENTITY_NAME<你的Name>(如 1234567890.Tydora
MSSTORE_PUBLISHER<你的Publisher>(如 CN=XXXX-XXXX-...
MSSTORE_PUBLISHER_DISPLAY_NAME<你的发布者显示名>
MSSTORE_PRODUCT_IDPartner Center 概览页的 Store product ID(如 9WZDNCRFXXXX

💡 关键设计:在 .github/workflows/msstore.yml 的发布步骤用 if: vars.MSSTORE_PRODUCT_ID 门控。只要不配 MSSTORE_PRODUCT_ID 变量,工作流只会构建 MSIX 并附到 GitHub Release,不会尝试调用 msstore publish。这意味着你可以现在就推 tag 验证 MSIX 打包是否成功,等手动上架后再开启自动发布。

5.4 工作流文件

完整工作流 .github/workflows/msstore.yml,核心步骤:

yaml
on:push:tags: ['v*']workflow_dispatch:jobs:msix:runs-on:windows-lateststeps:-uses:actions/checkout@v4-name:SetupNode.jsuses:actions/setup-node@v4-name:InstallRuststableuses:dtolnay/rust-toolchain@stable-name:Rustcacheuses:swatinem/rust-cache@v2-name:Installfrontenddependenciesrun:npmci-name:BuildMSIXshell:pwshenv:MSSTORE_PACKAGE_IDENTITY_NAME:${{vars.MSSTORE_PACKAGE_IDENTITY_NAME}}MSSTORE_PUBLISHER:${{vars.MSSTORE_PUBLISHER}}run:./scripts/build-msix.ps1-name:UploadMSIXartifactuses:actions/upload-artifact@v4with:name:tydora-msixpath:src-tauri/target/msix/*.msix-name:AttachMSIXtoGitHubReleaseif:startsWith(github.ref,'refs/tags/v')uses:softprops/action-gh-release@v2with:files:src-tauri/target/msix/*.msix# 以下步骤仅当配置了 MSSTORE_PRODUCT_ID 时执行-name:SetupMicrosoftStoreCLIif:vars.MSSTORE_PRODUCT_IDuses:microsoft/microsoft-store-apppublisher@v1.1-name:Reconfigurestorecredentialsif:vars.MSSTORE_PRODUCT_IDrun:|          msstore reconfigure `            --tenantId ${{ secrets.AZURE_AD_TENANT_ID }} `            --sellerId ${{ secrets.SELLER_ID }} `            --clientId ${{ secrets.AZURE_AD_APPLICATION_CLIENT_ID }} `            --clientSecret ${{ secrets.AZURE_AD_APPLICATION_SECRET }}-name:PublishpackagetoStoreif:vars.MSSTORE_PRODUCT_IDrun:|          $msix = Get-ChildItem src-tauri/target/msix/*.msix | Select-Object -First 1          msstore publish $msix.FullName -id ${{ vars.MSSTORE_PRODUCT_ID }}

5.5 触发发布

bash
# 打 taggit tag v0.1.5git push origin v0.1.5

推送后 GitHub Actions 自动跑:构建 → 打包 → 上传 Release → (配了变量时)发布到商店。整个过程约 10-15 分钟。

踩过的坑汇总

坑 1:MakeAppx 报 C00CEE3B 且错误信息是乱码

现象

MakeAppx : error: Error info: error C00CEE3B: App manifest validation error:The app manifest XML must be valid: Line 28, Column 15, Reason: ???????????????????????????

根因<Properties> 下子元素顺序不符合 XML Schema。正确顺序必须是 DisplayName → PublisherDisplayName → Logo → Description,我原来把 Description 写在 Logo 前面了。

修复:调整 AppxManifest.xml 的元素顺序。

坑 2:中文变乱码导致闭合标签被吃掉

现象:MakeAppx 仍然报 C00CEE3B,但这次是真正的 XML 解析错误。

根因:PowerShell 5.1 的 Get-Content -Raw 在文件无 BOM 时,会按系统默认编码(中文 Windows = GBK/936)解码 UTF-8 字节。具体破坏点:

  • 「左瑞」的「宁」UTF-8 编码是 E5 AE 81
  • 紧跟其后的 < 是 3C
  • GBK 把 81 3C 当作一个合法双字节汉字 → 吃掉了<
  • 导致 </PublisherDisplayName> 变成 ?/PublisherDisplayName>,XML 闭合标签失效

修复:在 build-msix.ps1 里把 Get-Content -Raw 改成 [System.IO.File]::ReadAllText(path, [System.Text.Encoding]::UTF8),并给 AppxManifest.xml 加 UTF-8 BOM。

坑 3:Identity Name 不匹配

现象

无效的软件包标识名称: Tydora (应为: <你的Name>)无效的软件包发布者名称: CN=Tydora (应为: <你的Publisher>)

根因.env 里的值带了引号和前导空格:

dotenv
MSSTORE_PACKAGE_IDENTITY_NAME = " <你的Name>"

Tydora 的 loadEnv 解析器不剥引号,导致实际传给 MakeAppx 的 Name 是带引号带空格的字符串,触发 Partner Center 校验失败。

修复.env 里去掉引号和 = 两边的空格:

dotenv
MSSTORE_PACKAGE_IDENTITY_NAME=<你的Name>

坑 4:PublisherDisplayName 不匹配

现象

应用清单中的 PublisherDisplayName 元素是 Tydora,该值与发布者显示名称: <你的发布者显示名> 不匹配

根因:Partner Center 注册时填的发布者显示名(个人账号默认用真实姓名),但 manifest 模板里硬编码了「Tydora」。

修复:把 PublisherDisplayName 改成占位符 {{PUBLISHER_DISPLAY_NAME}},由脚本从 .env 注入。

坑 5:本地双击未签名 MSIX 报 0x800B010A

现象:商店模式打的包,本地双击显示「发布者: 未知」,安装按钮灰色,报错 0x800B010A

根因:不是坑,是预期行为。商店模式的包本来就不签名,只应该上传到 Partner Center,由微软统一签名后分发。

解决:如果只是想本地测试安装效果,用本地模式(注释掉 .env 里的 MSSTORE_* 三行)打一个自签名的包即可。

常用命令速查

powershell
# 完整打包(含 Rust 编译)npm run tauri build:msix# 跳过编译,仅重新打包npm run tauri build:msix ---SkipBuild# 跳过签名npm run tauri build:msix ---SkipBuild-NoSign# 强制在商店模式下也签名(用于本地安装商店身份的包做视觉验证)npm run tauri build:msix ---SkipBuild-Sign# 本地测试模式(不配 MSSTORE_* 变量)Remove-Item Env:MSSTORE_PACKAGE_IDENTITY_NAME, Env:MSSTORE_PUBLISHER, Env:MSSTORE_PUBLISHER_DISPLAY_NAME -ErrorAction SilentlyContinuenpm run tauri build:msix ---SkipBuild

参考资料

  • Publishing app updates to Microsoft Store with GitHub Actions

    https://learn.microsoft.com/en-us/windows/apps/publish/msstore-dev-cli/github-actions?tabs=msix

  • MakeAppx.exe 文档

    https://learn.microsoft.com/zh-cn/windows/win32/appxpkg/make-appx-package--makeappx-exe-

  • App package manifest schema reference

    https://learn.microsoft.com/en-us/uwp/schemas/appxpackage/uapmanifestschema/schema-root

  • Microsoft Store submission API

    https://learn.microsoft.com/en-us/windows/uwp/monetize/create-and-manage-submissions

写在最后

把应用上架微软商店,整个流程走下来最大的感受是——坑都在细节里。XML 元素顺序错了不行,编码没搞对不行,.env 多了个引号也不行。但一旦首个版本上架成功,后续的 CI 自动化发布就非常顺滑了。

希望这篇文章能帮到同样想把 Tauri 应用上架微软商店的你。如果觉得有用,欢迎点赞收藏 👍