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

推荐订阅源

P
Proofpoint News Feed
C
CERT Recently Published Vulnerability Notes
O
OpenAI News
V
Vulnerabilities – Threatpost
C
Cybersecurity and Infrastructure Security Agency CISA
S
Schneier on Security
Latest news
Latest news
F
Full Disclosure
T
Tenable Blog
T
Troy Hunt's Blog
The Last Watchdog
The Last Watchdog
S
Secure Thoughts
L
LangChain Blog
有赞技术团队
有赞技术团队
Project Zero
Project Zero
Cloudbric
Cloudbric
爱范儿
爱范儿
GbyAI
GbyAI
C
CXSECURITY Database RSS Feed - CXSecurity.com
T
The Exploit Database - CXSecurity.com
S
Security @ Cisco Blogs
Hugging Face - Blog
Hugging Face - Blog
Recorded Future
Recorded Future
大猫的无限游戏
大猫的无限游戏
Last Week in AI
Last Week in AI
C
Cisco Blogs
WordPress大学
WordPress大学
Apple Machine Learning Research
Apple Machine Learning Research
小众软件
小众软件
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
V2EX - 技术
V2EX - 技术
Engineering at Meta
Engineering at Meta
Spread Privacy
Spread Privacy
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
Hacker News: Ask HN
Hacker News: Ask HN
Recent Commits to openclaw:main
Recent Commits to openclaw:main
Schneier on Security
Schneier on Security
T
Threat Research - Cisco Blogs
M
MIT News - Artificial intelligence
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
K
Kaspersky official blog
The Hacker News
The Hacker News
V
V2EX
F
Fortinet All Blogs
L
LINUX DO - 最新话题
Cisco Talos Blog
Cisco Talos Blog
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
N
News | PayPal Newsroom
博客园 - 三生石上(FineUI控件)
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org

博客园_首页

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主分片和副本分片概念详解 【002】HTTPS 粗解:证书、TLS 握手与对后端配置的影响 Hermes Agent 一周暴涨五万 Star,但我劝你别急着追 明明连接的是Redis的DB0,为什么能查到DB3的数据? 【从0到1构建一个ClaudeAgent】协作-Agent团队 熟悉电子元器件之后,电子小白下一步该怎么走? MAF快速入门(23)通过C#类定义Skills .NET 高级开发 | 手写一个对象映射框架 FastAPI数据库ORM怎么选?我肝了三个Demo后,终于不再纠结了 mysqldump 参数拾遗:在遗忘与铭记之间 C# .NET 周刊|2026年3月5期 Claude code入门 - 陈彦斌 一文学习入门 ThingsBoard 开源物联网平台 GitHub 热门项目 | 2026年04月16日 如何为GIT设置全局勾子,为每次提交追加信息 Number.isFinite和isFinite与isNaN()和Number.isNaN的区别 PortSwigger SQL注入LAB2 推荐一个测试人必备的Skills,从功能到性能全搞定(附详细实操和安装下载方式) 筑基期:掌握Odoo基础核心知识点02(Odoo XML 开发方式详解) GLM模型这么火,咱们用vllm也咧一个呗! 深入理解 AbortController:从底层原理到跨语言设计哲学 字符串学习笔记 多租户系统框架的基础模块设计和分析设计 Apache SeaTunnel Zeta 为什么能做到“又快又稳”? AI开发-python-LangGraph框架(3-26-LangGraph基本概念及第一个简单样例) Vue 3 组件通信,别只会用 Props 和 Emits 了,这几个狠活儿你得看看 ElasticSearch7.X版本配置密码 用Manim实现动态交点计算--从一个动点问题说起 团结引擎+Addressable+Instant Game打包抖音小游戏 function call 实战:让 LLM 自动判断 pod 异常、调用日志工具并完成故障分析 bubseek —— 让 Agent 的足迹,变成团队的洞察 通过 C# 读取并导出 PDF 书签 如何用 GitHub Actions 实现 Steam 自动化发布 【从0到1构建一个ClaudeAgent】并发-后台任务 .NET 高级开发 | 定制 ASP.NET Core 框架 电子小白:什么是运算放大器(运放) zero2Agent:面向大厂面试的 Agent 工程教程,从概念到生产的完整学习路线 堆上的ORW HC32F460 USB CDC通信异常:非对齐访问异常排查 20260413-Hyperbridge 攻击事件:发生在默克尔山上的验证绕过 那些喊着AI 要淘汰你的人,正在靠你的焦虑赚大钱! 深度学习进阶(八)Swin Transformer 最小二乘问题详解19:带先验约束的增量式SFM优化与实现 SnapTranslate 3.0 正式发布:全局划词翻译 + 完整英语学习闭环,一站式搞定查词、记词、复习 工作的意义、工作的困难认知再思考 .NET + AI 进阶实战:基于类的技能开发 - 打造可治理的 Agent 能力模块 【从0到1构建一个ClaudeAgent】规划与协调-技能 上周热点回顾(4.6-4.12) 电子小白的工具三件套:面包板、杜邦线、万能板 单表五亿数据的查询优化 | Mysql、StarRocks 2. WorkBuddy:从“我是谁”到“帮我干活” C# 如何减少代码运行时间:7 个实战技巧 基于HelixToolkit.SharpDX 渲染3D模型 - 笺上知微 从零开始的双臂具身VLA起源及现阶段发展综述 - SkyXZ 记对 xonsh shell 的使用, 脚本编写, 迁移及调优 - pluvium27 受够了Vibe Coding的失控?换个起点,让AI事半功倍 从开始配置漏洞环境到漏洞复现流程 - 難しい 关于10年工作经验的程序员对OpenClaw的实战经验分享以及看法 - 虚无境 Any metadata 的内存布局 C# .NET 周刊|2026年3月2期 - InCerry 我帮你测过了,测试圈排名第二的 Skill 依然很牛逼 Skill Discovery | 无监督技能发现的经典工作总结 - MoonOut 上下文工程是什么?过时了么?一文讲明白! - 一枫说码 开了 TUN 模式还是直连?90% 的人都踩过这个坑 AScript扩展多种脚本语言 - rockey627 AI 学习笔记:Agent 的记忆机制 你能被装进一个文件里吗?——7 万人把同事"蒸馏"成了 AI - 我没有三颗心脏 Claude Code 通关手册(七):给 AI 装上技能包——Skills 完全指南 - 暮色之狐 在浏览器中快速编辑代码:VSCode Web 集成实践 - Newbe36524 蒸馏自己 skill?基于 Deepseek 的蒸馏器,丐版蒸馏方式,简单便捷 - To_Carpe_Diem Spring AI Aliababa和AgentScope,哪个更好? - 苏三说技术
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 打包到商店提交”,更稳妥的推进方式是先把关键配置、依赖边界和落地路径逐步跑通,再补齐优化细节。

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

原文与版权说明

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