惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Y
Y Combinator Blog
有赞技术团队
有赞技术团队
J
Java Code Geeks
H
Hackread – Cybersecurity News, Data Breaches, AI and More
美团技术团队
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Hugging Face - Blog
Hugging Face - Blog
人人都是产品经理
人人都是产品经理
酷 壳 – CoolShell
酷 壳 – CoolShell
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
C
Check Point Blog
博客园 - 【当耐特】
The GitHub Blog
The GitHub Blog
Recent Announcements
Recent Announcements
The Cloudflare Blog
Microsoft Azure Blog
Microsoft Azure Blog
腾讯CDC
Vercel News
Vercel News
IT之家
IT之家
MyScale Blog
MyScale Blog
博客园_首页
Martin Fowler
Martin Fowler
WordPress大学
WordPress大学
罗磊的独立博客

博客园_首页

Plist 二进制格式 Milvus 和 PGVector,哪个更好? OpenClaw 已过时?在 VS Code 中运行 Hermes Agent! 第30篇文章:一个大三计科生的自白 Manim如何在数学公式中完美显示中文? Docker 部署 RocketMQ 5 并发编程核心概念辨析 C#事务处理最佳实践:别再让“主表存了、明细丢了”的破事发生 CLI 是什么?为什么大厂突然集体卷命令行? 【从0到1构建一个ClaudeAgent】协作-自主Agent UIImageView 设置图片不生效的原因排查 最小二乘问题详解20:无先验约束下的增量式SFM自由网平差 痞子衡嵌入式:大话双核i.MXRT1180之XIP应用里借助MU实现可靠Flash IAP的方法 AI Chat 封装, SemanticKerne.AiProvider.Unified 已发布 Windows下右键编辑js文件无法打开记事本——在注册表中使用环境变量 在后台服务中使用 Scoped 服务,为什么总是报错? H200 安装驱动并使用sglang启动模型 wireshark 抓包Trap上报告警内容 我用 AI 辅助开发了一系列小工具(2):图片压缩工具 [A Primer On MC and CC] 2.1 Memory Consistency 1 - 指令重排序和 SC 模型 Oracle数据库SCN推进技术详解与实践指南 玩转控件:封装个带图片的Label控件 Claude Code 4.7 真正该升级的不是模型,而是你的工作流 前端小白一句话,AI 帮我做了个颜值拉满的桌面媒体播放器。当代码不再是门槛,一句话编程就是现实。 5. WorkBuddy: 小龙虾的灵魂三件套,让你的小龙虾不只是工具 SQLite 分片方案实战:三种分片策略的深度对比 告别简陋 UI!一款基于 Fluent Design 和基于 WinUI 的开源免费、现代化的 Avalonia UI 控件库 关于二进制排列组合枚举的总结 AI开发-python-LangGraph框架(3-27-LangGraph从零实现大模型智能决策工作流) ElasticSearch主分片和副本分片概念详解
Electron 应用如何上架微软商店:从 MSIX 打包到商店提交
Newbe36524 · 2026-06-18 · via 博客园_首页

Electron 应用如何上架微软商店:从 MSIX 打包到商店提交

其实 Electron 说到底,不过是个普普通通的 Win32 桌面应用罢了,可微软商店它只认 MSIX。这篇文章,就借着我们 HagiCode Desktop 实打实跑通的那套构建配置,把「注册开发者账号 → 打 MSIX 包 → 提交商店」这条链路从头到尾拆给你看,顺便聊聊我们踩过的那些坑——毕竟坑踩过了,也就成了故事。

背景

手里有这么一个 Electron 应用,要在 Windows 上分发给最终用户。除了一直以来都在用的 NSIS 安装包、便携版之外,我们还盼着它能出现在微软商店里。说起来原因其实挺现实的:

  1. 可信分发渠道:商店里的应用都是签过名、审过的,用户安装的时候不会再被 SmartScreen 拦,也不必去面对那一句冷冰冰的「未知发行者」。
  2. 自动更新与商业化:更新的事,商店替你接管了;订阅、永久许可证,也都能直接对接上。
  3. 覆盖 Windows 10/11 自带的入口:winget、商店搜索、开始菜单推荐……这些入口,对拉新是实打实有用的。

只是 Electron 它终究不是 UWP。要想上架微软商店,其实核心也就一件事——把 Electron 的产物重新打包成微软商店认得下的 MSIX 包,再老老实实把注册、提交流程走完。听起来轻巧,可真上手了,坑还不少。为了把这些坑填平,我们花了不少功夫把整条链路摸了个通透,下面就把每一步掰开来揉碎了讲。

关于 HagiCode

这篇文章里讲的方案,来自我们在 HagiCode 项目里的实践。HagiCode Desktop 是一个基于 Electron 的桌面端,要同时通过官网、GitHub Release、还有微软商店这三个渠道分发给用户。商店这条渠道是怎么打通的,就是这篇文章要讲的事。文末有更多关于 HagiCode 的信息,要是你感兴趣,不妨拉到底看看。

分析:上架之前必须想清楚的四个问题

上架微软商店这件事,技术链条上有四个关键判断。想清楚了,后面也就不必反复返工了——毕竟谁也不想返工呢。

1. 微软商店只接受 MSIX / AppX,不接受传统 NSIS/EXE

微软商店对桌面应用(Desktop Bridge)的支持,是建立在 MSIX 格式之上的。传统的 NSIS 安装包没法直接提交,得先用 MakeAppx 重新打包成 MSIX。好在 Electron Forge 提供了一个 @electron-forge/maker-msix maker,它能在打包阶段就直接产出 MSIX,省下了从已安装目录反推打包那一通折腾。

我们项目里就挂着这么一个 maker:

{
  name: '@electron-forge/maker-msix',
  platforms: ['win32'],
  config: {
    appManifest: msixManifestPath,
    packageAssets: msixAssetsPath,
    logLevel: 'warn',
    ...(windowsKitPath ? { windowsKitPath } : {}),
    ...(windowsKitVersion ? { windowsKitVersion } : {}),
    ...msixSigningConfig,
  },
},

关键输入其实就两个:appManifest(也就是 AppxManifest.xml,定义包身份和能力)和 packageAssets(商店图标资产)。这两个要是错了,后面做得再漂亮,也是白搭。

2. 包身份必须提前在合作伙伴中心预留

MSIX 包里那个 Identity 字段(NamePublisher),可不是随便填填就行的,必须和合作伙伴中心里预留的应用身份分毫不差,差一个字符都会被打回来。我们预留的身份,记在 forge.store-config.json 里:

{
  "packageIdentity": {
    "displayName": "Hagicode",
    "publisherDisplayName": "newbe36524",
    "publisher": "CN=8B6C8A94-AAE5-4C8B-9202-A29EA42B042F",
    "identityName": "newbe36524.Hagicode",
    "backgroundColor": "transparent",
    "languages": ["en-US", "zh-CN", "zh-TW", "ja-JP", "ko-KR", "de-DE", "fr-FR", "es-ES", "pt-BR", "ru-RU"]
  }
}

这里头那个 publisher 字符串,是从开发者账号注册之后微软签发的证书主题里来的,必须逐字符匹配。identityName 呢,则是你预留的包名前缀。这个字符串,一定要从合作伙伴中心原样复制下来,千万别自己手敲——这件事我们在后面「常见坑」里还会再唠一遍。

3. 桌面应用必须声明 runFullTrust 能力

Electron 应用要用到完整的文件系统访问、要拉起子进程、还要跑 Node 运行时,这些只能在「完全信任」模式下才能实现。所以 MSIX 清单里,必须老老实实声明 runFullTrust 能力,不然应用一启动,就被沙箱给拦下了,表现出来的,就是各种让人摸不着头脑的崩溃。我们的配置长这样:

{
  "msix": {
    "minVersion": "10.0.17763.0",
    "maxVersionTested": "10.0.19045.0",
    "capabilities": [
      "runFullTrust",
      "internetClient",
      "internetClientServer",
      "privateNetworkClientsServer"
    ]
  }
}

runFullTrust 是桌面应用上架的标配。minVersion 设到 17763(也就是 Windows 10 1809),是因为从这个版本起,MSIX 才稳定支持桌面 Win32 应用,设低了,用户装不上;设高了,又覆盖不到那些上了年纪的老机器。

4. 商店提交需要 Windows 环境 + Microsoft Store CLI

打包这件事,倒是可以在跨平台的 CI 上做,可商店提交(msstore publish)就不行了,必须在 Windows 环境里跑 Microsoft Store CLI,还得配好 Azure AD 应用凭证。这也就是为什么自动化流水线里那个 publish_store 作业,非得跑在 windows-latest runner 上不可。这一点是绕不开的硬约束,不像打包那样可以塞进 Linux 容器里去。

解决:完整上架的八步流程

把上面的分析串起来,要把一个 Electron 应用上架到微软商店,完整的步骤大概是这样的。

步骤 1:注册开发者账号

先到 Partner Center 注册一个开发者账号(个人或者公司都行),把那笔一次性的费用给交了。账号激活之后,你会拿到一个 Publisher 证书主题字符串,大概长这样:CN=XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX。这,就是后面 publisher 字段唯一的来源。

步骤 2:在商店里预留应用身份

在合作伙伴中心新建一个应用,填上你想保留的名称。系统会分配给你 identityName,再和你自己的 Publisher 一组合,完整的包身份就成型了。把这个身份,原样抄到本地配置里:

// forge.store-config.json
{
  "packageIdentity": {
    "displayName": "Hagicode",
    "publisherDisplayName": "newbe36524",
    "publisher": "CN=8B6C8A94-AAE5-4C8B-9202-A29EA42B042F",
    "identityName": "newbe36524.Hagicode"
  }
}

步骤 3:准备商店图标资产

微软商店要的是一组固定尺寸的 PNG:StoreLogo.pngSquare44x44Logo.pngSquare150x150Logo.pngWide310x150Logo.png 之类的。我们的 prepare-msix.js 脚本,在打包之前会先校验这些资产是不是都齐了:

// 校验商店必需的图标资产,缺一个都不行
const requiredAssets = ['StoreLogo.png', 'Square44x44Logo.png', 'Square150x150Logo.png', 'Wide310x150Logo.png'];
for (const assetName of requiredAssets) {
  const assetPath = path.join(paths.generatedAssetsPath, assetName);
  if (!fs.existsSync(assetPath)) {
    throw new Error(`Missing required MSIX asset after preparation: ${assetPath}`);
  }
}

为什么要这么做呢?因为缺一个尺寸,MakeAppx 打包的时候并不会告诉你具体哪里错了,等到商店审核的时候才被打回来——这时候,你已经等了好几天了。提前校验,是一个非常有效的防御。

步骤 4:生成 AppxManifest.xml

清单里要塞进包身份、能力、可视资产、入口可执行文件。我们用一个覆盖配置(forge.store-config.json)来驱动 prepare-msix.js 生成清单,确保身份和商店对得上。清单里关键的那几段,大概是这样:

<!-- 包身份:必须和合作伙伴中心一致 -->
<Identity Name="newbe36524.Hagicode"
          Publisher="CN=8B6C8A94-AAE5-4C8B-9202-A29EA42B042F"
          Version="1.2.3.0" />

<Applications>
  <Application Id="Hagicode" Executable="Hagicode.exe" EntryPoint="Windows.FullTrustApplication">
    <uap:VisualElements ... />
  </Application>
</Applications>

<!-- 能力声明:runFullTrust 是桌面应用的关键 -->
<Capabilities>
  <rescap:Capability Name="runFullTrust" />
  <Capability Name="internetClientServer" />
</Capabilities>

注意那一行 EntryPoint="Windows.FullTrustApplication",这可是桌面应用的关键标记,配合 runFullTrust 能力,才能以完整的权限跑起来。少了它,应用就只能乖乖待在沙箱里,憋屈得很。

步骤 5:用 maker-msix 打包

构建命令写在 package.json 里:

{
  "scripts": {
    "build:win:store": "npm run generate:store-bindings && node scripts/build-store-package.js"
  }
}

它最终调起 Electron Forge,把 forge.store-config.json 当作覆盖配置带进去,maker-msix 会去调用 Windows SDK 的 MakeAppx,吐出 .msix 文件来。这里头有个硬约束:打包必须在 Windows 上做(或者带 Windows SDK 的容器里),毕竟它依赖 MakeAppx,这点没法绕。

步骤 6:签名(商店提交时可以不签)

这一步,其实很容易被忽略——提交到商店的包,微软会用它自己的证书重新签一遍,所以「正式提交」之外的开发自测阶段,是可以不签名的。只是你要是想在本地装上测试一下,那就得用受信任证书签一下了,不然 Windows 是会拒绝安装的。我们的 resolveMsixSigningConfig 在没配签名材料的时候,会返回一个空对象,让流程接着往下走:

// 没配签名材料就不签,让商店统一重签
function resolveMsixSigningConfig() {
  if (!process.env.MSIX_CERT_FILE) return {};
  return {
    signMethod: 'signtool',
    certFilePath: process.env.MSIX_CERT_FILE,
    certPassword: process.env.MSIX_CERT_PASSWORD,
  };
}

把「自测签名」和「提交空签名」这两条路径分开,是一个非常关键的实践。

步骤 7:配置 Microsoft Store CLI 凭证

到 Azure 门户去创建一个 Azure AD 应用,给它授予访问 Partner Center 的权限,然后拿到下面这一组凭证:

  • AZURE_AD_APPLICATION_CLIENT_ID
  • AZURE_AD_APPLICATION_SECRET
  • AZURE_AD_TENANT_ID
  • SELLER_ID(合作伙伴中心的卖家 ID)
  • MICROSOFT_STORE_PRODUCT_ID(预留应用的产品 ID)

这一步稍微有点绕,不过 Azure 门户和 Partner Center 的文档里都写得很细,照着做就是了。

步骤 8:提交到商店

在 Windows 环境里,用 Microsoft Store CLI 提交:

# 配置凭证
msstore reconfigure --tenantId $env:AZURE_AD_TENANT_ID `
  --clientId $env:AZURE_AD_APPLICATION_CLIENT_ID `
  --clientSecret $env:AZURE_AD_APPLICATION_SECRET `
  --sellerId $env:SELLER_ID

# 提交 MSIX 包到预留的产品
msstore publish "$packagePath" -id $env:MICROSOFT_STORE_PRODUCT_ID

提交完之后,还要回到 Partner Center 把商店详情(描述、截图、定价、分级)填齐,最后点一下提交审核。审核一般要等 1–3 个工作日,第一次提审嘛,总会久一点。

实践:把配置和踩坑经验沉淀下来

走完一遍流程之后,下面这些实践,能让你少走一些弯路——毕竟弯路走多了,也就不觉得弯了,只是有些事能省则省。

配置文件要分开存

把「通用构建配置」和「商店特定配置」分开存放,是关键。我们的做法是:forge.config.js 跑日常构建(NSIS、portable、macOS dmg),forge.store-config.json 只在商店构建的时候,通过 extends 继承并覆盖:

{
  "extends": "forge.config.js",
  "buildVersion": "0.1.0.0",
  "packageIdentity": { /* 商店预留身份 */ },
  "msix": {
    "minVersion": "10.0.17763.0",
    "maxVersionTested": "10.0.19045.0",
    "capabilities": ["runFullTrust", "internetClient", "internetClientServer", "privateNetworkClientsServer"]
  }
}

这样一来,商店版和发行版,彼此都不会互相污染。HagiCode Desktop 同时维护着三套发行渠道,配置分离,是我们能稳定迭代下去的前提。

版本号必须是四段

MSIX 的版本号必须是 Major.Minor.Build.Revision 四段(比如 1.2.3.0),可 Electron 的 package.json 通常只写三段。那个 buildVersion 字段,就是用来补最后一段的——商店提交的时候,版本号必须递增,第四段非常方便,可以用来区分同一个语义版本下的多次提交。这一点,踩过的人都懂;没踩过的,迟早也会踩。

多语言声明

商店支持多语言的 listing,对应到清单里,就是那一条条 <Resource Language="..." />。我们声明了十种语言,商店会要求每种语言都填上一份描述(可以先用机器翻译过审,再慢慢本地化)。prepare-msix.js 里对应的渲染逻辑是这样:

// 把语言列表渲染成 MSIX 清单里的 Resource 标签
function renderResourceTags(languages) {
  return languages
    .map((language) => `    <Resource Language="${escapeXml(language)}" />`)
    .join('\n');
}

常见坑(重点看)

下面这些坑,HagiCode Desktop 几乎每一个都踩过:

  1. Publisher 不匹配:从 Partner Center 复制 publisher 字符串的时候,一不小心就丢了空格或者弄错了大小写,提交直接被拒。建议直接写成配置文件,别手敲。
  2. 缺少 runFullTrust:应用启动之后,没法访问文件系统,也拉不起子进程,表现成各种诡异的崩溃,排查起来,可费劲了。
  3. 图标尺寸不全:MakeAppx 不校验,可商店审核会打回。prepare-msix.js 提前校验,是个有效的防御。
  4. 版本号不递增:商店拒绝接收相同或者更低的版本号,CI 流水线得保证每次构建都 bump。
  5. 在非 Windows 环境里跑 maker-msix:会找不到 MakeAppx,必须用 windows-latest runner。
  6. 签名混乱:自测用自签证书,商店提交用空签名让微软重签,这两条路径要分开,别把自签证书塞进提交包里去。

自动化建议

第一次手动把全流程走完、每一步都摸清之后,强烈建议接上 GitHub Actions 做自动化。我们最后把版本解析、MSIX 构建、GitHub Release 发布、商店发布串成了一条流水线,每 4 小时检查一次新版本。这部分细节,在我们另一篇文章《Windows 应用自动上架 Microsoft Store 的自动化实践》里有完整的拆解。

如果你只是想先把应用挂上商店、商业化(订阅 / 永久授权)之后再接,也可以看看我们《Electron 桌面应用如何接入 Microsoft Store 订阅与永久许可证》那篇,那篇讲的,是商店上架之后的商业化能力对接。

参考资料

总结

围绕“Electron 应用如何上架微软商店:从 MSIX 打包到商店提交”,更稳妥的推进方式是先把关键配置、依赖边界和落地路径逐步跑通,再补齐优化细节。

当目标、步骤和验收点都明确之后,这类方案通常就能更顺畅地进入实际交付。

原文与版权说明

感谢您的阅读,如果您觉得本文有用,欢迎点赞、收藏和分享支持。
本内容采用人工智能辅助协作,最终内容由作者审核并确认。