news 2026/10/1 17:13:58

ImageGlass Windows 打包全指南:MSIX、MSI 与便携版 ZIP 的构建与签名实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
ImageGlass Windows 打包全指南:MSIX、MSI 与便携版 ZIP 的构建与签名实战
  • 桌面应用
  • 图像处理

【免费下载链接】ImageGlass

🏞 A fast, open-source, modern image viewer for 90+ formats – including WEBP, GIF, SVG, AVIF, JXL, HEIC and more – built for smooth browsing across Windows, macOS, and Linux.

项目地址:https://gitcode.com/gh_mirrors/im/ImageGlass
点击查看免费下载

导读

本指南基于 ImageGlass 仓库 source/__assets/win/README.md 及其配套打包脚本,系统讲解 Windows 端三套交付物的构建原理与实操:MSIX(Microsoft Store 与 GitHub sideload 两个风味)、MSI 安装器(WiX 5 双作用域)与便携版 ZIP。读完你将掌握三套 PowerShell 打包脚本的参数语义、签名与版本号策略、本地测试 Store 包的方法,以及 MSI 双作用域安装/升级中那些只有踩过坑才写得出的细节——每一条都能在仓库源码中找到对应实现。

三套交付物总览

ImageGlass 的 Windows 端由同一个ImageGlass.Win32自包含 AOT 构建派生出三种产物,全部产出到__artifacts/bundle/:

产物用途架构打包脚本
MSIX(msstore 风味,未签名)Microsoft Store 提交x64 / arm64script-pack-win-msix.ps1
MSIX(signed 风味,Authenticode 签名)GitHub Releases sideloadx64 / arm64同上(加-Sign)
MSI 安装器GitHub Releases 传统安装仅 x64script-pack-win-msi.ps1
便携版 ZIPGitHub Releases 免安装x64 / arm64script-pack-win-zip.ps1

MSIX 的两种风味差异集中在签名、身份(Identity)与美工三处:

风味签名Identity / Publisher美工资源去向
msstore否(Store 提交时重签)Store 保留名 + PublisherAssets-msstoreMicrosoft Store
signed是(每个.exe/.dll+ 包本身)普通名 + 证书 SubjectAssets-signedGitHub Release

微软商店在提交时自行重签包,所以msstore 构建必须保持未签名——如果你自己签了再上传,反而会被拒绝或出问题;而 GitHub 风味必须签名,因为未签名的 MSIX 无法安装。

环境准备(Prerequisites)

三套脚本统一要求PowerShell 7+(文件头均有#Requires -Version 7.0),以及以下工具链:

  • Windows 10/11 SDK:提供makeappx.exe、makepri.exe、signtool.exe。脚本中的Find-SdkTool会自动在Windows Kits\10\bin下按版本号排序选最新的一个,无需手工配置 PATH。
  • .NET 10 SDK:用于dotnet publish产出 Release、AOT、自包含的构建。
  • 代码签名证书(仅 signed 风味):安装在CurrentUser\My或LocalMachine\My并带私钥,或以 PFX 文件形式提供。
  • WiX Toolset 5.0.2(仅 MSI):.NET 全局工具 + UI 扩展,安装命令如下,且版本被钉死:
dotnet tool install --global wix --version 5.0.2 wix extension add -g WixToolset.UI.wixext/5.0.2

版本为何钉死?WiX 6/7 是同一工具在 FireGiant Open Source Maintenance Fee 许可下的续作,而裸dotnet tool install --global wix会装最新的。script-pack-win-msi.ps1中的Assert-WixVersion会读取wix --version并严格比对5.0.2,不一致直接抛错并提示重装。加-BootstrapWix参数可让脚本代装钉死版本。

  • 签名的 Pro 许可证(仅 msstore 风味):从官网管理后台签发一份channel: msstore、versionScope: 10、initVersion: <当前版本>、无邮箱、无过期日的许可证,把下载的<licenseId>.iglicense.json放进 git 忽略的__artifacts\store-license\,或显式传-StoreLicenseFile。没有它 msstore 构建会立即失败(Resolve-StoreLicense在发布前就检查,避免几分钟后才失败)。

MSIX 打包实战

基础命令

# Microsoft Store(未签名) pwsh __assets/win/script-pack-win-msix.ps1 -Platform x64 pwsh __assets/win/script-pack-win-msix.ps1 -Platform arm64 # GitHub Release(签名;按证书 Subject 子串选择) pwsh __assets/win/script-pack-win-msix.ps1 -Platform x64 -Sign pwsh __assets/win/script-pack-win-msix.ps1 -Platform arm64 -Sign # 一个 .msixbundle 同时装下 x64 + arm64 pwsh __assets/win/script-pack-win-msix.ps1 -Bundle -Sign # 签名,给 GitHub pwsh __assets/win/script-pack-win-msix.ps1 -Bundle # 未签名,给 Store # 用 PFX 而非证书库签名 pwsh __assets/win/script-pack-win-msix.ps1 -Platform x64 -Sign -CertFile C:\ig.pfx -CertPassword <pw>

也可在 VS Code 里通过任务运行:pack-win-x64-msix、pack-win-arm64-msix(GitHub 签名单架构包)、pack-win-msstore-msixbundle(Store 未签名双架构包)、pack-win-all(全部产物)。

输出命名规则为ImageGlass_<version>_win-x64.msix(或_win-arm64.msix,signed)与ImageGlass_<version>_win-msstore.msixbundle(msstore)。

完整参数表

参数说明默认值
-Platformx64 或 arm64;-Bundle时忽略x64
-Bundle打包 x64+arm64 单一.msixbundle关
-Signsigned 风味;无证书时仍产出未签名包并警告关
-CertSubject从证书库按 Subject 子串选证书Duong Dieu Phap
-CertFile/-CertPasswordPFX 路径与密码空
-TimestampUrlRFC-3161 时间戳服务器http://timestamp.sectigo.com
-PackageVersion覆盖包版本见下节
-MsStoreIdentityNameStore 保留身份名9662DuongDieuPhap.ImageGlass
-MsStorePublisherStore 分配的 Publisher DNCN=29F1B9EC-...
-SideloadIdentityNamesigned 风味身份名DuongDieuPhap.ImageGlass
-PublisherDisplayName显示名Duong Dieu Phap
-UnvirtualizedResources退出资源虚拟化(见下文)关
-StoreLicenseFilemsstore 内嵌许可证路径__artifacts\store-license\*.iglicense.json
-SkipPublish复用__artifacts/publish/win-<arch>加快迭代关

.msix 与 .msixbundle 的区别

.msixbundle把 x64 与 arm64 两个.msix打包在一起,Windows 安装时自动选择匹配当前设备的架构,因此发布一个文件即可。bundle 内部的单架构包只做了载荷签名(其中的.exe/.dll带信任链),不对包本身签名——只有.msixbundle这一个文件被签名。

版本号策略:两个风味各自约束

只有 Store 对版本号有限制,所以两个风味采用不同方案(源码见 script-pack-win-msix.ps1 的版本段):

  • sideload(GitHub):原样携带<IgVersion>,例如10.0.6.906,与 MSI 和“关于”框一致。
  • msstore:Store 保留了第 4 段(revision),要求其为0,所以打包为<Major>.<Minor>.<IgBundleBuild>.0,例如10.0.6+ 构建号906→10.0.906.0,丢弃 patch 段。两个值都来自 Directory.Build.props,可用-PackageVersion覆盖。

脚本中的Assert-PackageVersion还会预校验:MSIX 版本必须恰好 3~4 段、每段 0~65535、major 段非 0,防止畸形版本在 publish 几分钟后才被makeappx以晦涩错误打回。

升级陷阱:Windows 拒绝安装比已装版本旧的包,所以每个风味的版本必须逐版抬升。sideload 包最高到10.0.6.906时,会以10.0.<build>.0形式发布,而新方案排在其下面,因此首个采用新方案的发布需要更高的 major/minor(或传一个高于10.0.906.0的-PackageVersion),否则老安装无法升级。

文件类型关联:两个风味截然不同

Windows 对包声明的每个类型都会取清单中的图标,优先级高于经典DefaultIcon注册,因此两个风味策略相反(见New-FileTypeAssociationXml):

  • signed / sideload(-UnvirtualizedResources):清单不声明任何关联,让应用自己的 HKCU 注册与_ext_icons目录负责图标。同时清单中写入<desktop6:RegistryWriteVirtualization>disabled</desktop6:RegistryWriteVirtualization>与rescap:Capability Name="unvirtualizedResources",退出资源虚拟化后经典注册才能真正落到 HKCU。
  • msstore:该注册在虚拟化环境中不可见,因此为每种格式声明一条关联并带各自uap:Logo。扩展名列表直接读取Const.IMAGE_FORMATS(Const.cs,当前约 70 种格式,含.webp、.avif、.heic、.jxl等),保证清单与应用永不脱节。关联的DisplayName(如ImageGlass WEBP File)需与Win32DefaultAppApi.GetFriendlyTypeName保持一致。

Export-ExtIconPngs会把_ext_icons里每个.ico的 256px PNG 帧按targetsize-16/32/48/96/256五种尺寸导出为 MRT 可解析的 PNG 变体;找不到精确尺寸帧时才用 GDI+ 缩放兜底。

打包流水线(脚本内部六个步骤)

New-MsixPackage的完整流程:publish(dotnet publishRelease/AOT/自包含 + 拷贝__assets/__app共享资源)→stage(ImageGlass\子目录 + 剔除*.pdb;msstore 额外写入_store\许可证;未开启-UnvirtualizedResources时删掉_ext_icons死重)→生成清单(模板替换 + UTF-8 BOM 写出)→签名载荷(signed 风味对所有.exe/.dll)→构建资源索引(makepri createconfig+makepri new,让无限定名的 Logo 正确解析到各 DPI 变体)→打包(makeappx pack;-Bundle时先各架构各打一个.msix再makeappx bundle)。最后对最终产物.msix/.msixbundle签名并用signtool verify /pa校验,然后清理 staging 中间目录。

本地测试 msstore 包:两条正路

msstore 产物故意不签名,而 Windows 拒绝安装未签名包,所以不能双击安装测试。不要试图把Publisher改成你自己的证书 Subject 来绕过——Windows 的包族名中的 publisher id 由该 DN 派生,Win32AppIdentity.IsMsStorePackage(Win32AppIdentity.cs)正是据此判断,这样测试出来的包会自报“不是 Store 安装”,Pro 不会解锁,测的是错的东西。

方案一:用 Store 的 Publisher Subject 自签(安装体验最接近真实):

$subject = 'CN=29F1B9EC-D220-4DC3-BEDB-01A9CCA51904' # 必须等于清单 Publisher $cert = New-SelfSignedCertificate -Type CodeSigningCert -Subject $subject ` -CertStoreLocation Cert:\CurrentUser\My -FriendlyName 'ImageGlass msstore local test' ` -TextExtension @('2.5.29.19={text}') # 信任它以便安装(需管理员),然后签一份 COPY,绝不签你要上传的产物 Export-Certificate -Cert $cert -FilePath "$env:TEMP\ig-store-test.cer" | Out-Null Import-Certificate -FilePath "$env:TEMP\ig-store-test.cer" -CertStoreLocation Cert:\LocalMachine\TrustedPeople Copy-Item __artifacts\bundle\ImageGlass_*_win-msstore.msixbundle __artifacts\bundle\local-test.msixbundle signtool sign /fd SHA256 /sha1 $cert.Thumbprint __artifacts\bundle\local-test.msixbundle Add-AppxPackage __artifacts\bundle\local-test.msixbundle

方案二:注册松散布局(无需证书,但需要开发者模式):

makeappx unbundle /p __artifacts\bundle\ImageGlass_<version>_win-msstore.msixbundle /d "$env:TEMP\igb" makeappx unpack /p "$env:TEMP\igb\ImageGlass-x64.msix" /d "$env:TEMP\igx" Add-AppxPackage -Register "$env:TEMP\igx\AppxManifest.xml"

无论哪种方式,测试后检查:帮助菜单显示Manage Pro license、Pro 功能解锁、licensed-to 行显示内嵌许可证的customerName。结束后用Remove-AppxPackage卸载、删除签名副本,并从Cert:\CurrentUser\My与Cert:\LocalMachine\TrustedPeople移除测试证书。

美工资源的生成

两套 Logo 集由 script-generate-msix-assets.ps1 从单张源图渲染:Assets-signed由 logo_c_512.png 生成,Assets-msstore由 logo_p_512.png 生成(Store 身份本身即授予 Pro,故用 Pro 版 Logo)。脚本按-ReferenceDir中的文件名逐一镜像:Logo 名决定基准尺寸(Square150x150→150,Wide310x150→310×150,StoreLogo→50),targetsize-N覆盖为 N×N,scale-N按基准缩放,Wide 系列居中绘制在透明宽画布上。两套文件名完全一致,因此同一份清单与resources.pri解析逻辑对两个风味都成立。换 Logo 后需重跑脚本,并注意-ReferenceDir必须指向另一套资源目录(脚本会先清空-OutDir)。MSI 向导图banner.bmp/dialog.bmp则由 script-generate-msi-art.ps1 从同一 Logo 生成。

MSI 安装器

构建命令

# 签名 x64 安装器(载荷二进制 + .msi 本身) pwsh __assets/win/script-pack-win-msi.ps1 -Sign # 先装钉死版 wix 工具 + UI 扩展,再打包 pwsh __assets/win/script-pack-win-msi.ps1 -Sign -BootstrapWix # 本地快速迭代:复用 publish 目录、跳过 ICE 校验、用廉价压缩 pwsh __assets/win/script-pack-win-msi.ps1 -SkipPublish -SkipValidation -CompressionLevel mszip

输出为__artifacts/bundle/ImageGlass_<version>_win-x64.msi,当前仅 x64(-Platform只接受x64)。VS Code 任务:pack-win-x64-msi(包含在pack-win-all中)。

安装向导与双作用域

向导共四屏:Terms and Privacy(指向官网条款的超链接 + 勾选“I agree”才能进入下一步)、Installation type(按用户/按机器安装,目录默认值随之变化,带 Browse 按钮)、进度、完成(可选“Launch ImageGlass”)。

项目刻意不用WixUI_Advanced:它早于 per-user/per-machine 切换类包,只设置WixAppFolder/ALLUSERS而从不设置MSIINSTALLPERUSER,且其 per-machine 默认会落到 per-user 目录。两个自定义对话框都在 msi/UI.wxs 中。

作用域与提权对照:

选择目标目录提权
Only me%LocalAppData%\Programs\ImageGlass无
All users%ProgramFiles%\ImageGlass安装开始时弹 UAC

静默安装示例:

msiexec /i ImageGlass_<version>_win-x64.msi /qn ALLUSERS=2 MSIINSTALLPERUSER=1 # 按用户 msiexec /i ImageGlass_<version>_win-x64.msi /qn ALLUSERS=1 # 按机器 msiexec /i ImageGlass_<version>_win-x64.msi /qn /l*v "%TEMP%\ig.log" # 带日志 msiexec /x {PRODUCT-CODE} /qn # 卸载

按机器安装配合/qn时调用方必须已提权,因为静默安装无法弹出 UAC 提示(MSI 日志:MSI_LUA: Installation UI level is silent, no credential elevation is possible)。

为什么对话框必须发布ALLUSERS

这是一个极易踩的坑:对话框必须发布ALLUSERS,仅发布MSIINSTALLPERUSER不够。客户端在启动瞬间就会折叠ALLUSERS=2(日志:MSI (c): Deleting ALLUSERS property...),此时 Property 表里MSIINSTALLPERUSER=1就把上下文定格为按用户,之后清空MSIINSTALLPERUSER是无效操作,只有ALLUSERS=1会被服务端采纳。而命令行两种写法都能生效,因为属性在折叠发生前就已就位。历史教训:10.0.5.825发布时未发布ALLUSERS,导致其“All users”安装实际把文件装进%ProgramFiles%、ARP 行写入 HKLM,却按用户注册产品并把HKMU作用域标记写进 HKCU。

FindRelatedProducts排在该作用域预置之后,原因相同:它会静默跳过与当前生效上下文不同的相关产品(current install is per-user. Related install ... is per-machine. Skipping...),否则旧版本会残留注册。它仍先于LaunchConditions运行,故降级防护不受影响。

可在命令行设置的公共属性:ALLUSERS、MSIINSTALLPERUSER、INSTALLFOLDER、IGDESKTOPSHORTCUT、IGSTARTMENUSHORTCUT、IGREMOVEFILEASSOC、IGAGREETOTERMS。注意IGINSTALLSCOPE只驱动 UI 中的单选按钮,不改变安装上下文。

升级既有安装:作用域锁死与降级防护

Windows Installer 无法在按用户与按机器之间搬移产品——在另一作用域运行主升级时,无法移除它要替换的产品,结果要么回滚、要么装下两份拷贝。因此包从自身HKMU标记所在的注册表根判断当前作用域:

签名由谁留下
<IgSetupRegKey>\InstallLocation所有携带本 authoring 的构建
<IgSetupRegKey>\StartMenuShortcut、\DesktopShortcut更早的构建(除非用户都拒绝了快捷方式)
Software\Duong Dieu Phap\{<Ig9UpgradeCode>}\AI_INSTALLPERUSERImageGlass 9(Advanced Installer 同样写法)

<IgSetupRegKey>\InstallLocation还记录精确目录(含自定义路径),升级因此落在旧安装之上而非默认位置。这里不能用ComponentSearch:其Type="directory"要求组件键路径是目录,而 exe 组件的键路径是文件,永远解析不到。

作用域不符时的行为分三种:

  • 向导中:作用域被预选且单选组禁用,物理上不可能产生错配;ImageGlass 9 只预选不锁死,因为其移除是尽力而为,强制提权安装反而错误。
  • 静默时:错配直接拒绝并提示应使用的命令行,刻意不做自动纠正——安装器在启动时就定格上下文,自定义操作翻转ALLUSERS只改变是否要求提权,却不会改变已应用到ProgramFiles64Folder的目录重定向,会把按机器安装塞进%LocalAppData%。这是“实测而非臆测”的决策。
  • 同版本更旧构建被拒:ProductVersion第 4 段携带构建号,而 MSI 忽略该段,10.0.5.825与10.0.5.906会被判为相等并落入升级区间,MajorUpgrade自带的降级防护不会触发,旧安装器会顶掉新版。因此安装时在InstallLocation旁记录InstalledBuild,IgBlockOlderBuild拒绝任何更低的构建并复用降级文案;比该记录更早的安装(IGPREVBUILD为空)则不拒绝。
  • 10.0.5.825的“All users”安装被整体拒绝:它实际按用户注册(见上文ALLUSERS问题),没有作用域能升级它。识别特征是 HKLM ARP 行 + HKCU 标记;卸载它即解除封锁。卸载需提权,否则 MSI 无法删除%ProgramFiles%里的文件。
  • 两种机制都不覆盖早到未留任何标记的安装(用户当时拒绝了两个快捷方式),这种情形仍按老方式失败。

MSI 的工程细节与踩坑记录

  • “Launch ImageGlass”动作是 type 50 而非 type 18:FileRef自定义操作在组件未随会话安装时会报 2753(“The File is not marked for installation”),而 MSI 会跳过键路径已以更高版本存在的组件。它改为从IgLaunchTarget执行,并在ExecuteAction之后才设置为[#FileImageGlassExe]——CostFinalize时对话框选择的目录尚未生效,此时取值还是 per-user 默认路径,启动会静默失败。
  • Magick.Native-*.dll手工声明为ImageGlass.exe的伴随文件:Costing 在CostFinalize(1000) 进行而RemoveExistingProducts在 1401,MSI 按旧产品的文件决定组件,会跳过键路径已以更高版本存在的组件(Action: Null),随后移除删除该文件,结果两边都没装,只能 Repair 找回。Magick.NET 14.14 给该 DLL 打的版本号是10.2.0.0,14.17.1 却是7.1.2.31,版本倒退了,升级时被静默丢弃。作为伴随文件,其版本读取自只升不降的ImageGlass.exe。任何版本会下降的载荷文件都需要同样处理。
  • 包绝不能标记为 “UAC compliant”:Word Count 汇总位第 3 位(“不需要提升权限”)会把 MSI 变成 per-user-only 包,MSI 日志MSIINSTALLPERUSER property is not valid for UAC compliant package,ALLUSERS被删除,按机器选项静默失效而安装仍报成功。WiX 对Scope="perUserOrMachine"正确地保持该位清零,打包器(Assert-DualScopeSummary)也断言它保持清零。提权由解析后的上下文决定,per-user 永不弹窗。
  • 版本:ProductVersion原样使用<IgVersion>全部四段,因此“应用与功能”显示真实构建号。MSI 只比较前三段,故 authoring 设置MajorUpgrade/@AllowSameVersionUpgrades,否则仅第 4 段不同的两个构建会被视为同一产品、永远不触发升级。ProductCode是版本的确定性 UUIDv5(New-DeterministicGuid),已发布构建的msiexec /x {GUID}因此始终可用。可用-ProductVersion覆盖。
  • 卸载清理文件类型注册:卸载时先运行ImageGlass.exe --ig-remove-default-viewer再删文件;传IGREMOVEFILEASSOC=0跳过。主升级期间自动跳过,否则每次更新都会剥掉关联。两个已知缺口:per-machine 卸载时动作以 SYSTEM 身份运行,无法清除交互用户的UserChoice(Explorer 下次选择时自愈);插件新增的扩展名不覆盖。
  • 安装会移除 ImageGlass 9(UpgradeCode{877DB994-AB03-4025-B99D-41CE565E810B},见 Variables.wxi)。移除是尽力而为:per-user 的 v10 无权卸载 per-machine 的 v9,失败不得回滚整个安装。注意 v9 的 per-user 安装用了同一个%LocalAppData%\Programs\ImageGlass目录且把igconfig.json存在里面,那些设置不会保留;v10 读%LocalAppData%\ImageGlass。
  • 绝不可移植:.igportable标记必须永不进入载荷——带标记的已安装副本无法启动(目录不可写,应用报错退出而非回退)。打包器发现标记或_store目录就拒绝构建。
  • _ext_icons保留(同便携 ZIP):这是非打包安装,应用经典 HKCU/HKLM 注册提供文件图标。
  • 签名顺序强制:载荷.exe/.dll必须在 harvest之前签名,因为wix build会记录尺寸与MsiFileHash行并压缩进内嵌 cabinet,之后无法再签。.msi在校验之后最后签名,此后不得再触碰。
  • ICE 校验是独立步骤(v5 的wix build不做任何 ICE 且无开关)。对双作用域包有三个不可避免的抑制:ICE57(快捷方式组件以HKMU键路径携带 per-user 数据,此处唯一正确的根)、ICE61(AllowSameVersionUpgrades)、ICE105(延迟无模拟卸载动作)。打包器用Test-Ice105Invariants手工重断言 ICE105 的其他检查:无 HKLM 注册表行、无服务、无 ODBC 或程序集行、无系统目录。
  • 更快的迭代:-SkipPublish复用__artifacts/publish/win-x64。绝不用于正式发布:版本已编译进二进制。

便携版 ZIP

构建命令

# 按架构签名便携 ZIP(载荷二进制签名;ZIP 本身无法签名) pwsh __assets/win/script-pack-win-zip.ps1 -Platform x64 -Sign pwsh __assets/win/script-pack-win-zip.ps1 -Platform arm64 -Sign # 非便携归档:设置写入 %LocalAppData%\ImageGlass,同 MSIX 构建 pwsh __assets/win/script-pack-win-zip.ps1 -Platform x64 -NoPortable

VS Code 任务:pack-win-x64-zip、pack-win-arm64-zip。输出__artifacts/bundle/ImageGlass_<version>_win-<arch>.zip。

便携模式原理

ZIP默认便携:打包器在ImageGlass.exe旁写入一个空.igportable标记文件。应用启动时在自己的目录里寻找该标记(文件名来自Const.PORTABLE_MARKER_FILE=.igportable,见 Const.cs),找到后把写入的一切(igconfig.json、_cache、_logs、_plugins、_lang……)都留在本目录而非%LocalAppData%\ImageGlass。整个文件夹因此可移动、重命名或塞进 U 盘而不丢设置。检测逻辑在 ConfigMode.cs,由BHelper.ConfigPath转换为配置目录。

三条硬约束(均有源码佐证):

  • 便携目录必须可写:标记存在但应用无法在该目录建文件(例如解压进了Program Files),应用报出真实错误并退出,绝不回退到%LocalAppData%——否则会静默地隐藏便携设置于第二个配置之下。ConfigMode.Resolve在检测到标记后用带DeleteOnClose的探针文件实测可写性。
  • 标记绝不进入 MSIX:打包 MSIX 的载荷目录只读,带标记的每次启动都会失败。标记只由 ZIP 打包器写入,__assets/__app里没有它。
  • 打包时的护栏:MSI 打包器同样拒绝携带.igportable或_store的载荷。

打包细节

script-pack-win-zip.ps1的流程与 MSIX 打包器前半段一致:publish → 按归档名建立单层顶层目录(解压不会散落文件到当前目录)→ 剔除*.pdb→ 清理任何残留_store(防-SkipPublish泄漏 Store 许可证到公开归档)→ 写便携标记 → 签名载荷二进制 → 打包。打包用[System.IO.Compression.ZipFile]::CreateFromDirectory而非Compress-Archive:文件量大时快得多,且通配符式Compress-Archive会跳过点前缀的标记文件。includeBaseDirectory:$true保证一切落在归档同名目录下。

Pro 授权与 Store 身份

Store 版的Pro 授权就是包身份本身。Win32AppIdentity.IsMsStorePackage要求运行时包的身份名等于-MsStoreIdentityName,且 publisher id 等于-MsStorePublisher的哈希;Win32StoreEntitlementProvider把该身份当作已购 Pro 的证明。publisher id 是 publisher DN 以 UTF-16LE 编码后 SHA-256 的前 8 字节再做 base32,它是包全名的尾段,最稳妥的重新推导方式是直接读一个已装包的尾段。改动任一参数都会悄悄关掉所有 Store 用户的 Pro——所以改MSSTORE_IDENTITY_NAME/MSSTORE_PUBLISHER_ID(在 Win32AppIdentity.cs)必须与打包参数同 commit。

同一身份还让构建跳过许可证版本作用域检查:LicenseScope.IsScopeExempt(LicenseScope.cs)在IsStoreEntitled时直接豁免,这正是 Store 客户在 Windows 上对未来所有版本都拥有 Pro 的原因,尽管内嵌文件只作用于 major 10。该作用域仍约束 macOS/Linux 上的导出副本。

这一整套只在 Store 条目保持付费应用 + 限时试用时才成立:Windows 在试用期结束后拒绝启动应用,于是“进程在运行”等价于“客户已授权”。若将来列表变为免费或无限试用,就必须替换为实时的 Store 许可证查询。

另一个关联要点是内嵌许可证仅用于导出。msstore 载荷把签名许可证放在ImageGlass\_store\——应用许可证扫描永不查看的子目录,因此它本身不授予任何权限(授权靠 Store 身份);它的存在纯粹是让 Store 客户能导出副本给自己的 macOS/Linux 机器,所以作用域限定在他们购买的主版本线。signed 风味见到_store目录就拒绝构建,-SkipPublish的陈旧复用因此不可能把许可证泄漏进 GitHub 包。任何人仍可解包取出该许可证,这是被接受的设计——泄漏的应对是下一次提交换用全新licenseId。

快速对照:何时用哪条命令

场景命令
Store 提交(x64)pwsh __assets/win/script-pack-win-msix.ps1 -Platform x64
GitHub Release(签名,双架构一个文件)pwsh __assets/win/script-pack-win-msix.ps1 -Bundle -Sign
传统安装器pwsh __assets/win/script-pack-win-msi.ps1 -Sign
免安装便携版pwsh __assets/win/script-pack-win-zip.ps1 -Platform x64 -Sign
本地快速迭代各脚本加-SkipPublish(MSI 再加-SkipValidation -CompressionLevel mszip)
一把梭VS Code 任务pack-win-all

所有产物汇入__artifacts/bundle/,供上传 Store 或 GitHub Release。打包脚本本身(script-pack-win-msix.ps1、script-pack-win-msi.ps1、script-pack-win-zip.ps1)包含完整的参数文档与示例,是排查打包问题时的第一手参考。

  • 桌面应用
  • 图像处理

【免费下载链接】ImageGlass

🏞 A fast, open-source, modern image viewer for 90+ formats – including WEBP, GIF, SVG, AVIF, JXL, HEIC and more – built for smooth browsing across Windows, macOS, and Linux.

项目地址:https://gitcode.com/gh_mirrors/im/ImageGlass
点击查看免费下载

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/1 17:13:18

SEMA动态Adapter池:按需生长解决持续学习容量困境

1. 为什么预训练模型需要“按需长大”CVPR 2025 的 SEMA&#xff0c;核心一句话&#xff1a;给冻结的预训练模型配一个可增长的 Adapter 池&#xff0c;训练时根据当前任务和已有 Adapter 的匹配度&#xff0c;决定是复用老 Adapter&#xff0c;还是长一个新的 Adapter 出来。你…

作者头像 李华
网站建设 2026/10/1 17:13:04

软件安全性相关内容主要分布在“网络与信息安全知识”模块,同时也与“系统开发和运行知识”中的软件质量、容错机制、安全测试等内容存在交叉

软件设计师考试属于全国计算机技术与软件专业技术资格&#xff08;水平&#xff09;考试中的中级资格&#xff0c;由人力资源和社会保障部、工业和信息化部共同组织实施。2026年软件设计师考试大纲和教材未改版&#xff0c;仍沿用《软件设计师考试大纲》&#xff08;2018年审定…

作者头像 李华
网站建设 2026/10/1 17:12:31

截面数据空间计量模型:SAR、SEM、SDM估计与Python/R实战

简介&#xff1a;这份资源面向空间计量经济学初学者与实证研究者&#xff0c;系统整理了截面数据下的主流空间回归估计方法&#xff0c;帮助解决空间依赖建模与模型选择问题。内容覆盖空间滞后模型SLM、空间误差模型SEM、空间杜宾模型SDM及其误差形式&#xff0c;并延伸至自变量…

作者头像 李华
网站建设 2026/10/1 17:12:27

微信小程序WXSS模板样式:从rpx适配到高频场景实战写法

刚入坑微信小程序那段时间&#xff0c;我犯过一个挺典型的错误&#xff1a;以为WXSS就是把CSS换个名字&#xff0c;顶多写样式时把px换成rpx。直到接手一个社区团购项目&#xff0c;写了一堆自以为很漂亮的模板样式&#xff0c;结果在真机上一查&#xff0c;大量样式要么不生效…

作者头像 李华
网站建设 2026/10/1 17:11:00

小白程序员想入行大模型?高薪AI大模型应用开发工程师指南

本文探讨了AI大模型应用开发工程师的兴起及其高薪原因。文章指出&#xff0c;企业更需要的是能将AI模型落地到实际业务中的人才&#xff0c;而非单纯研究模型。文章强调了项目经验的重要性&#xff0c;并建议程序员通过参与真实企业项目、持续学习和应用新技术来提升竞争力&…

作者头像 李华