发布.NET应用到Microsoft Store

Get NightCorer by Ulysses

 本文是一篇将WPF/WinForm项目上架到微软商店的全面流水账。之前在网上看的他人的教程,大多是针对UWP项目而不是转制项目。希望这篇文章对各位有参考价值。

首先感谢MS寄来的小礼物,千里送Python:

为什么要上架Microsoft Store

0202年了不会还有人用巨硬商店吧?不会吧不会吧?

MS商店预装在所有普通用户(不含服务器)的Win10上,对于个人开发的Windows软件来说是一个不错的渠道。

MS商店也不收费用,审核通过就可以上架,上架后全世界的用户都能下载,安全方面也容易被用户所信任。(订正:之前注册过的开发者账户不交钱,我赶上了好时候,现在新注册的开发者账户需要交116块钱的入场费,入场后发布应用不需再交钱。自行斟酌)

比起对普通用户说“请到github的release页面下载我的xxx” “请先翻墙” 这样费口舌的解释(发链接还容易被各种平台当做广告删除),不如一句“请在Win10商店搜索XXX”来的更方便和自信。

 

为什么不搞UWP

0202年了不会还有人用UWP吧?不会吧不……

是想搞的,但是老的WPF项目迁移到UWP确实麻烦,重写又没有这么多时间。

既然MS已经允许我们在MS Store发布普通Win32应用,还给了MSIX Packaging Tool这样的工具,那么自然要把老项目物尽其用。

 

改造.NET项目以符合UWP权限规范

平时我们的.NET项目经常是随意在各种目录下读取/创建文件,这在UWP权限规范下是不行的。最主要的一点是,程序的安装/运行目录本身是没有写权限的。因此,如果你的程序运行时会在程序目录下面释放什么东西,那就需要改造这部分代码,把它放到其他有权限的目录。

为了避免权限问题,同时还可以用上一些UWP的API功能,非常建议在项目里安装Microsoft.Windows.SDK.Contracts这个nuget库。安装时不要盲目追求最新的库版本,它的版本是与Win10版本对应的,选择低一点的版本能让你的项目适配更老的Win10版本。

导入库后,可以把你需要释放的文件放到你的程序专属目录:

Windows.Storage.StorageFolder storageFolder = Windows.Storage.ApplicationData.Current.LocalFolder;

或者通过这种方式获取用户的库目录(如图片、音乐等常用库):

var musicLib = await StorageLibrary.GetLibraryAsync(KnownLibraryId.Music);

参考文档:

https://docs.microsoft.com/en-us/windows/uwp/files/quickstart-reading-and-writing-files

改造完成后,release编译项目,并确保删除编译输出目录下面所有不必要的文件(XML、PDB等)。

 

注册应用

到MS合作伙伴中心: https://partner.microsoft.com/zh-cn/dashboard/

登录后,找到 开发人员计划——Windows和Xbox——开始使用。如果你之前没有注册过开发者账户,则需要填写信息注册,并缴入场费116RMB。

注册完成后,在发布第一个应用之前,还需要填写税务信息。这个表格有点长,而且是纯英文的,但是没有什么难填的项目。

填完之后,就可以在 Windows和Xbox——概述 面板中创建项目了。

创建好项目之后,首先来到 Windows和Xbox——产品——你创建的项目——产品管理——产品标识 这里。

这里的产品标识将会在后面用到,先记下来准备好。

 

打包应用

在MS商店搜索并下载MSIX Packaging Tool。打开,创建新安装包,从本机创建(当然你有足够的存储和计算性能,可以从虚拟机创建的话更好),“选择想要打包的安装程序”找到你的主程序,“签名首选项”先不要签名。“程序包信息”这一栏是非常关键的,必须要与上一步中“产品标识”中的内容一致,如下图。“安装位置”可以随便选择,并不是最终用户的安装位置。

后面还有个选择权限的步骤,尽管通过这个工具制作的转制应用已经默认是runFullTrust权限等级(类似于Win32应用),但是出于过审考虑,还是建议如实选择用到的权限,比如联网的程序必选“Internet”,播放器必选“音乐”(这里指的是访问用户的音乐库文件夹)和“背景媒体播放”等。

如果一切顺利,会生成一个MSIX程序包。格式应该类似于:

81920MonarchSolutions.NightCorer_1.0.0.0_x64__deadbeef01337.msix

通常情况下,你的.NET程序其实是AnyCPU的甚至是x86的,可是打出来的包有可能被判定为x64,这时候就需要手动调整一下。或者你发现一个文件漏打进去,或者要替换一个文件,也需要手动调整。即使这些你都不需要,你也需要再次测试一下这个MSIX安装之后到底能不能运行——千万不要觉得你release编译出来已经跑过了就万事大吉了,我当初也是这么想的,直到我提交了两次都收到了拒信:(原因就出在权限问题上)

这时就需要选择MSIX工具的第三功能:编辑安装包。

如果你的程序包信息填的不对,在上传MSIX时验证不通过;或者权限选的不对;或者要换一个文件;都可以从这里修改。

如果你要把x64改成x86,需要点击“清单文件”下面那个按钮来编辑清单文件。但是至少在我的电脑上,点击这个按钮无法直接打开清单文件。这时候你需要手动找一下这个.manifest文件,通常位于AppData\Local\Packages\Microsoft.MsixPackagingTool_8wekyb3d8bbwe\LocalState\DiagOutputDir\下面的某个文件夹里。如果你有Everything等搜索工具,可以直接按修改日期+.manifest定位到。用文本编辑器打开,然后把ProcessorArchitecture改成x86,保存就可以了。

如果你要验证MSIX安装后能不能跑(强烈建议自己自测,避免不过审),则需要先生成一个证书来给MSIX签名。

首先 Windows设置——更新和安全——开发者选项 里,要选择开发人员模式。

然后,参考这篇文档来生成pfx证书:

https://docs.microsoft.com/en-us/windows/msix/package/create-certificate-package-signing

打开PowerShell,输入命令(自己替换【CN=...】和【NightCorer_Cert】,前者要与应用标识中的发布者名称一致,后者随意填):

New-SelfSignedCertificate -Type Custom -Subject "CN=DEADBEEF-76BE-4167-B3B2-BEEFBEEFDEAD" -KeyUsage DigitalSignature -FriendlyName "NightCorer_Cert" -CertStoreLocation "Cert:\CurrentUser\My" -TextExtension @("2.5.29.37={text}1.3.6.1.5.5.7.3.3", "2.5.29.19={text}")

随后打开证书设置(打开开始菜单,直接输入cert或者证书),选择“管理个人证书”。在 证书(当前用户)——个人——证书 下面找到你刚创建的证书(通过友好名称FriendlyName来定位),双击打开——详细信息——复制到文件——导出私钥——随意设置密码。双击刚刚导出的pfx文件,导入证书,存储位置选择本地计算机,将所有证书都放入下列存储——浏览——受信任的根证书颁发机构。

随后回到MSIX工具编辑安装包的界面,签名首选项选择“使用证书(pfx)签名”,选择刚才的pfx文件并输入密码。然后保存安装包到另一个位置(不要覆盖未签名的安装包)。随后双击安装刚刚签名的安装包:

如果显示的是“受信任的应用”,说明你的签名没问题;否则,上面的步骤肯定哪里出了问题,常见的两种是:没有把带私钥的pfx导入到本地计算机/受信任的根证书颁发机构(而是选择了别的位置);或者创建证书的命令Subject写的不对。

随后安装运行,如果运行一切正常,那么就可以准备提交了(切记提交时还是要提交没有签名的版本!)

 

提交应用

到刚才的MS合作伙伴中心,找到你创建好的应用程序页面,开始提交。

需要填写以下内容,应该没有什么特别难写的。要制作一些尺寸合适的logo图片,还要为你面向的每种语言准备一份介绍。

最主要的就是“程序包”这一步,要上传刚才未签名的MSIX。如果上传后直接验证失败,很大概率是你MSIX包里的程序标识和商店里显示的不一致。

一切都准备好后,就可以提交等待审核了。

审核通常需要2~3天的时间,而且据我观察,大部分时候是欧洲人/美国人来审核,因此非常建议好好写英语说明。

如果一切顺利,你会收到一封分级邮件和一封过审邮件:

——

本文中提到的我的第一个应用,NightCorer,已经在MS商店审核通过并上架!商店链接

 

生成Badge

本文开头的那个Get it from Microsoft Store的标志(badge)可以从这个网址生成:

https://developer.microsoft.com/en-us/store/badges/

StoreID就是你的应用的商店链接的末尾那一串乱码。

需要注意的是,生成的badge的图片的URL地址是有点问题的(内链),可能需要自己补全;而生成的Product banner是需要页面支持JS的,如果你的blog不支持……那就截个图用吧。

 

 

评论 (1) -

添加评论

Loading