如何将用户从未打包的 Web 应用过渡到 Microsoft Store 打包的应用

如果将应用程序作为 Web 下载 (EXE /MSI) 发布,并在 Microsoft Store 中作为打包的应用程序 (MSIX) 分发,那么,你可能希望阻止用户同时安装这两个版本,或者想要将用户从未打包的 Web 版本迁移到 Microsoft Store 版本。 本指南将提供有关如何将用户从未打包的版本无缝过渡到打包版本的说明。

下面将介绍两种方案:

  1. 用户已安装基于 Web 的未打包版本,并且你希望将其替换为 Microsoft Store 的打包版本。
  2. 用户已安装这两个版本,你希望优先使用 Microsoft Store 打包版本并卸载基于 Web 的未打包版本。

方案 1:将未打包的 Web 应用程序自动更新到 Microsoft Store 打包的应用程序

如果你打算将用户从未打包的 Web 应用程序自动迁移到打包的 Microsoft Store 版本,建议执行以下步骤:

  1. 使 Microsoft Store 打包的应用程序能够使用现有的任务栏和“开始”菜单固定功能,并确保在 Microsoft Store 打包的应用程序替换未打包的 Web 应用程序时用户保留其快捷方式。
  2. 从未打包的 Web 版本以无提示方式下载并安装 Microsoft Store 版本。
  3. 向用户指示应用程序将重启以应用更新
  4. 下载并安装后,启动 Microsoft Store 打包版本并关闭未打包的 Web 版本。
  5. 在 Microsoft Store 打包应用程序中,将数据迁移到新的应用数据文件夹。
  6. 最后,以编程方式卸载未打包的 Web 版本。

方案 2:如果用户安装了这两个版本,请卸载基于 Web 的未打包应用程序。

你可以允许用户并行使用这两个版本的应用程序,但你必须管理应用程序之间的冲突,并负责在这 2 个版本之间同步数据。

如果你希望用户仅使用 1 个版本并优先使用 Microsoft Store 版本,请参阅以下一些建议:

  1. 使 Microsoft Store 打包的应用程序能够使用现有的任务栏和“开始”菜单固定功能,并确保在 Microsoft Store 打包的应用程序替换未打包的 Web 应用程序时用户保留其快捷方式。
  2. Microsoft Store 应用程序应检测未打包的版本是否存在并在启动时卸载它
  3. 当用户正在启动未打包的应用程序时,应自动启动打包的版本
  4. 如果需要,可能要迁移数据
  5. 最后,以编程方式卸载未打包的 Web 版本。

技术建议

如何从未打包的 Web 应用程序安装 Microsoft Store 打包的应用程序

要启动下载和安装,你必须知道应用程序的 Microsoft Store ID。 此 12 个字符的 ID 可以从合作伙伴中心获取,具体而言是在“产品标识”部分下面,即使尚未提交应用程序也不例外。

随后,可以使用以下代码以无提示方式下载并安装 Microsoft Store 应用程序。 此代码将:

  1. 向当前 Microsoft Store 用户(如果存在)分配权利;否则,权利将与设备关联。
  2. 启动产品的下载和安装,而不生成任何通知 Toast。
  3. 你可以使用事件 API 监视下载和安装进度。
    private async Task<bool> DownloadStoreVersionAsync()
    {
        var productId = "<Product Id from Partner Center>";
        var applicationName = "<name of your application>";

        var appInstallManager = new AppInstallManager();
        var entitlement = await appInstallManager.GetFreeUserEntitlementAsync(productId, string.Empty, string.Empty);
        if (entitlement.Status is GetEntitlementStatus.NoStoreAccount)
        {
            entitlement = await appInstallManager.GetFreeDeviceEntitlementAsync(productId, string.Empty, string.Empty);
        }
        if (entitlement.Status is not GetEntitlementStatus.Succeeded)
        {
            return false;
        }

        var options = new AppInstallOptions()
        {
            LaunchAfterInstall = true,
            CompletedInstallToastNotificationMode = AppInstallationToastNotificationMode.NoToast
        };
        var items = await appInstallManager.StartProductInstallAsync(productId, string.Empty, applicationName, string.Empty, options);
        var firstItem = items.FirstOrDefault();
        if(firstItem is null)
        {
            return false;
        }
        firstItem.StatusChanged += StoreInstallation_StatusChanged;
        firstItem.Completed += StoreInstallation_Completed;
        return true;
    }

    private void StoreInstallation_Completed(AppInstallItem sender, object args)
    {
        // Launch the new Store version when ready and close this application
        // The Store version will then be responsible of migrating the data and uninstall the unpackaged version
    }

    private void StoreInstallation_StatusChanged(AppInstallItem sender, object args)
    {
        var status = sender.GetCurrentStatus();
        switch(status.InstallState)
        {
            case AppInstallState.Installing:
                {
                    // Show installing status
                }
                break;
            case AppInstallState.Downloading:
                {
                    // Show download progress using status.PercentComplete
                }
                break;
            ...
        }

如何从未打包的 Web 应用启动 Microsoft Store 应用程序

要启动 Microsoft Store 应用程序,必须知道其 AMUID,该 AMUID 由以感叹号 (!) 分隔的程序包系列名称(在合作伙伴中心的“产品标识”部分中)和应用程序 ID(来自 appxmanifest)组成。

        Process.Start(
            "explorer.exe",
            "shell:AppsFolder\\Microsoft.WindowsCalculator_8wekyb3d8bbwe!App"
        );

如何检测是否已安装 Microsoft Store 打包版本并启动它

你可以使用 GetPackagesByPackageFamily win32 API 并传入打包应用的包系列名称来确定是否已安装打包版本的应用程序。 如果计数值大于零,则表示已安装此应用程序。

如何从打包的应用程序中卸载未打包的 Web 应用程序

若要检索卸载程序的绝对路径,你可以访问注册表。

卸载程序信息位于注册表中的以下位置:

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\<your product code GUID\>

UninstallString 值中检索完整命令并执行此命令。 建议以无提示方式执行卸载,或通知用户你将迁移数据并卸载其他应用程序。

如何迁移数据

未打包的应用程序可能会将其本地数据存储在:

%localAppData%/<YourPublisherName\>/<YourAppName\>

打包的应用程序为数据存储保留了空间,在卸载应用程序后会自动删除该空间。 强烈建议(尽管不作要求)首次启动时将数据迁移到此空间。 可以通过调用 Windows.Storage.ApplicationData.Current.LocalFolder.Path 来检索此文件夹的绝对路径。

如何迁移购置和应用内购买的内容

应用内购买

为了保证具有最佳用户体验,用户必须无缝访问他们在未打包版本的应用程序中购买的内容。 为了实现这一目标,自 2021 年 6 月起,Microsoft Store 允许发布者使用自己的商务平台或第三方商务平台以及 Microsoft 商务平台,从而针对发布者提高了灵活性。

我们强烈鼓励发布者除了与 Microsoft Commerce 平台集成外,还继续验证在其未打包版本应用程序中执行应用内购买的权利,使用户只需单击几下 Windows 即可轻松购买内容。

允许未打包应用程序的付费用户迁移到打包版本

如果用户在网站上购买了你的产品,则他们不必再次付费即可从 Microsoft Store 下载打包的版本。 为了确保无缝过渡,我们建议采用以下方法:

  1. 提供产品的免费/演示版本,允许用户通过应用内购买解锁完整版本。 对于已在网站上付费的用户,他们可以通过登录来验证许可证或在应用程序的用户界面中输入许可证密钥来访问完整版本。
  2. 将应用程序设置为付费产品/服务,但通过自己的渠道将优惠券代码分发给现有用户。 这些代码将允许他们下载 Microsoft Store 版本,而无需额外付费。 可以在生成促销代码中找到详细信息。

如何迁移现有的固定任务栏和“开始”菜单快捷方式

用户可能已将桌面应用程序固定到任务栏或“开始”菜单。 你可以通过在应用程序清单中包含“windows.desktopAppMigration”扩展,将这些快捷方式定向到新的打包应用。

示例

xmlns:rescap3="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities/3"
...
<Extensions>
<rescap3:Extension Category="windows.desktopAppMigration">
<rescap3:DesktopAppMigration>
<rescap3:DesktopApp AumId="[your_app_aumid]" />
<rescap3:DesktopApp ShortcutPath="%USERPROFILE%\Desktop\[my_app].lnk" />
<rescap3:DesktopApp ShortcutPath="%APPDATA%\Microsoft\Windows\Start Menu\Programs\[my_app].lnk" />
<rescap3:DesktopApp ShortcutPath="%PROGRAMDATA%\Microsoft\Windows\Start Menu\Programs\[my_app_folder]\[my_app].lnk"/>
</rescap3:DesktopAppMigration>
</rescap3:Extension>
</Extensions>

安装应用程序后,任务栏或开始菜单中的固定程序以及磁贴(适用于 Windows 10)将自动启动 Microsoft Store 应用程序。

如何迁移文件扩展名和协议关联

如果应用程序支持文件扩展名或协议关联,并且用户已将应用选为特定文件扩展名和协议的默认应用程序,则可以选择将这些关联迁移到 Microsoft Store 打包的应用程序。 可以通过使用以下代码片段更新应用清单来实现此迁移。

xmlns:rescap3="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities/3"
...
<Extensions>
<uap:Extension Category="windows.fileTypeAssociation">
<uap3:FileTypeAssociation Name=".foo">
<rescap3:MigrationProgIds>
<rescap3:MigrationProgId>Foo.Bar.1</rescap3:MigrationProgId>
</rescap3:MigrationProgIds>
…
</uap3:FileTypeAssociation>
</uap:Extension>
</Extensions>

只需列出要迁移到的编程标识符,系统就会在安装后自动将其迁移到应用程序。