从 Windows 10 版本 1607 开始,你可以使用 Windows.Services.Store 命名空间中的 StoreContext 类的方法以编程方式检查Microsoft应用商店中当前应用的包更新,并下载并安装更新的包。 还可以查询在合作伙伴中心中标记为必填的包,并在安装强制更新之前禁用应用中的功能。
Windows 10 版本 1803 中引入的其他 StoreContext 方法使你能够无提示下载和安装程序包更新(无需向用户显示通知 UI),卸载 可选包,并获取有关应用下载和安装队列中的包的信息。
这些功能可帮助你自动使用户群与应用商店中最新版本的应用、可选包和相关服务保持最新。
使用用户权限下载并安装包更新
此代码示例演示如何使用 GetAppAndOptionalStorePackageUpdatesAsync 方法发现应用商店中的所有可用包更新,然后调用 RequestDownloadAndInstallStorePackageUpdatesAsync 方法下载并安装更新。 使用此方法下载和安装更新时,OS 会显示一个对话框,询问用户在下载更新之前的权限。
注释
这些方法支持你的应用所需的必需包和 可选包。 可选包可用于下载内容(DLC)附加组件,将大型应用程序拆分以管理大小限制,或发布独立于核心应用程序的其他内容。 若要获取将使用可选包(包括 DLC 加载项)的应用提交到应用商店的权限,请参阅 Windows 开发人员支持。
此代码示例假定:
- 代码在 Page 的上下文中运行。
-
Page 包含一个名为 的
downloadProgressBar
,用于提供下载操作的状态。 - 代码文件包含一个用于
Windows.Services.Store 、Windows.Threading.Tasks 和Windows.UI.Popups 命名空间的 语句。 - 该应用是一个单用户应用,仅在启动应用的用户的上下文中运行。 对于 多用户应用,请使用 GetForUser 方法获取 StoreContext 对象而不是 GetDefault 方法。
private StoreContext context = null;
public async Task DownloadAndInstallAllUpdatesAsync()
{
if (context == null)
{
context = StoreContext.GetDefault();
}
// Get the updates that are available.
IReadOnlyList<StorePackageUpdate> updates =
await context.GetAppAndOptionalStorePackageUpdatesAsync();
if (updates.Count > 0)
{
// Alert the user that updates are available and ask for their consent
// to start the updates.
MessageDialog dialog = new MessageDialog(
"Download and install updates now? This may cause the application to exit.", "Download and Install?");
dialog.Commands.Add(new UICommand("Yes"));
dialog.Commands.Add(new UICommand("No"));
IUICommand command = await dialog.ShowAsync();
if (command.Label.Equals("Yes", StringComparison.CurrentCultureIgnoreCase))
{
// Download and install the updates.
IAsyncOperationWithProgress<StorePackageUpdateResult, StorePackageUpdateStatus> downloadOperation =
context.RequestDownloadAndInstallStorePackageUpdatesAsync(updates);
// The Progress async method is called one time for each step in the download
// and installation process for each package in this request.
downloadOperation.Progress = async (asyncInfo, progress) =>
{
await this.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal,
() =>
{
downloadProgressBar.Value = progress.PackageDownloadProgress;
});
};
StorePackageUpdateResult result = await downloadOperation.AsTask();
}
}
}
注释
若要仅下载(但不安装)可用包更新,请使用 RequestDownloadStorePackageUpdatesAsync 方法。
显示下载和安装进度信息
调用 RequestDownloadStorePackageUpdatesAsync 或 RequestDownloadAndInstallStorePackageUpdatesAsync 方法时,可以为此请求中每个包的下载(或下载和安装)过程分配一次调用进度处理程序。 处理程序接收 StorePackageUpdateStatus 对象,该对象提供有关引发进度通知的更新包的信息。 前面的示例使用 StorePackageUpdateStatus 对象的 PackageDownloadProgress 字段显示下载和安装过程的进度。
请注意,在单个作中调用 RequestDownloadAndInstallStorePackageUpdatesAsync 下载和安装包更新时, PackageDownloadProgress 字段在包的下载过程中从 0.0 增加到 0.8,然后在安装过程中从 0.8 增加到 1.0。 因此,如果将自定义进度 UI 中显示的百分比直接映射到 PackageDownloadProgress 字段的值,则当包下载完毕并且 OS 显示安装对话框时,UI 将显示 80%。 如果希望自定义的进度 UI 在包下载完并准备安装时显示 100%,则可以在 PackageDownloadProgress 字段达到 0.8 时修改代码,将 100% 分配给进度 UI。
静默下载和安装软件包更新
从 Windows 10 版本 1803 开始,可以使用 TrySilentDownloadStorePackageUpdatesAsync 和 TrySilentDownloadAndInstallStorePackageUpdatesAsync 方法以无提示方式下载和安装包更新,而无需向用户显示通知 UI。 只有当用户在商店中启用了 自动更新应用 设置,并且用户未连接到按流量计费的网络时,此操作才会成功。 在调用这些方法之前,可以先检查 CanSilentlyDownloadStorePackageUpdates 属性,以确定当前是否满足这些条件。
此代码示例演示如何使用 GetAppAndOptionalStorePackageUpdatesAsync 方法发现所有可用的包更新,然后调用 TrySilentDownloadStorePackageUpdatesAsync 和 TrySilentDownloadAndInstallStorePackageUpdatesAsync 方法以无提示方式下载和安装更新。
此代码示例假定:
- 代码文件包含适用于
Windows.Services.Store 和System.Threading.Tasks 命名空间的 语句。 - 该应用是一个单用户应用,仅在启动应用的用户的上下文中运行。 对于 多用户应用,请使用 GetForUser 方法获取 StoreContext 对象而不是 GetDefault 方法。
注释
在本示例中,代码调用的 IsNowAGoodTimeToRestartApp、RetryDownloadAndInstallLater和 RetryInstallLater 方法是占位符方法,计划依据您自己的应用设计来按需实现。
private StoreContext context = null;
public async Task DownloadAndInstallAllUpdatesInBackgroundAsync()
{
if (context == null)
{
context = StoreContext.GetDefault();
}
// Get the updates that are available.
IReadOnlyList<StorePackageUpdate> storePackageUpdates =
await context.GetAppAndOptionalStorePackageUpdatesAsync();
if (storePackageUpdates.Count > 0)
{
if (!context.CanSilentlyDownloadStorePackageUpdates)
{
return;
}
// Start the silent downloads and wait for the downloads to complete.
StorePackageUpdateResult downloadResult =
await context.TrySilentDownloadStorePackageUpdatesAsync(storePackageUpdates);
switch (downloadResult.OverallState)
{
case StorePackageUpdateState.Completed:
// The download has completed successfully. At this point, confirm whether your app
// can restart now and then install the updates (for example, you might only install
// packages silently if your app has been idle for a certain period of time). The
// IsNowAGoodTimeToRestartApp method is not implemented in this example, you should
// implement it as needed for your own app.
if (IsNowAGoodTimeToRestartApp())
{
await InstallUpdate(storePackageUpdates);
}
else
{
// Retry/reschedule the installation later. The RetryInstallLater method is not
// implemented in this example, you should implement it as needed for your own app.
RetryInstallLater();
return;
}
break;
// If the user cancelled the download or you can't perform the download for some other
// reason (for example, Wi-Fi might have been turned off and the device is now on
// a metered network) try again later. The RetryDownloadAndInstallLater method is not
// implemented in this example, you should implement it as needed for your own app.
case StorePackageUpdateState.Canceled:
case StorePackageUpdateState.ErrorLowBattery:
case StorePackageUpdateState.ErrorWiFiRecommended:
case StorePackageUpdateState.ErrorWiFiRequired:
case StorePackageUpdateState.OtherError:
RetryDownloadAndInstallLater();
return;
default:
break;
}
}
}
private async Task InstallUpdate(IReadOnlyList<StorePackageUpdate> storePackageUpdates)
{
// Start the silent installation of the packages. Because the packages have already
// been downloaded in the previous method, the following line of code just installs
// the downloaded packages.
StorePackageUpdateResult downloadResult =
await context.TrySilentDownloadAndInstallStorePackageUpdatesAsync(storePackageUpdates);
switch (downloadResult.OverallState)
{
// If the user cancelled the installation or you can't perform the installation
// for some other reason, try again later. The RetryInstallLater method is not
// implemented in this example, you should implement it as needed for your own app.
case StorePackageUpdateState.Canceled:
case StorePackageUpdateState.ErrorLowBattery:
case StorePackageUpdateState.OtherError:
RetryInstallLater();
return;
default:
break;
}
}
强制软件包更新
在合作伙伴中心为面向 Windows 10 版本 1607 或更高版本的应用创建程序包提交时,可以 将包标记为必需 以及它变为强制性的日期和时间。 设置此属性并且应用发现包更新可用时,你的应用可以确定更新包是否是必需的,并在安装更新之前更改其行为(例如,你的应用可以禁用功能)。
注释
包更新的强制状态不是由Microsoft强制执行的,并且 OS 不提供 UI 来向用户指示必须安装强制应用更新。 开发人员打算使用强制设置在自己的代码中强制实施强制应用更新。
若要将包提交标记为强制,
- 登录到 合作伙伴中心 并导航到应用的概述页。
- 单击包含要强制进行包更新的提交的名称。
- 导航到提交 包 页。 在此页底部附近,选择“使此更新强制”,然后选择包更新变为强制的日期和时间。 此选项适用于提交中的所有 UWP 包。
有关详细信息,请参阅 上传应用包。
注释
如果创建 包飞行,可以在 包 页上使用相似的用户界面将包标记为必需。 在这种情况下,强制软件包更新仅适用于属于试用组的客户。
强制包的代码示例
下面的代码示例演示如何确定任何更新包是否是必需的。 通常情况下,如果强制包更新未能成功下载或安装,您应当优雅地为用户降低应用体验。
private StoreContext context = null;
// Downloads and installs package updates in separate steps.
public async Task DownloadAndInstallAllUpdatesAsync()
{
if (context == null)
{
context = StoreContext.GetDefault();
}
// Get the updates that are available.
IReadOnlyList<StorePackageUpdate> updates =
await context.GetAppAndOptionalStorePackageUpdatesAsync();
if (updates.Count != 0)
{
// Download the packages.
bool downloaded = await DownloadPackageUpdatesAsync(updates);
if (downloaded)
{
// Install the packages.
await InstallPackageUpdatesAsync(updates);
}
}
}
// Helper method for downloading package updates.
private async Task<bool> DownloadPackageUpdatesAsync(IEnumerable<StorePackageUpdate> updates)
{
bool downloadedSuccessfully = false;
IAsyncOperationWithProgress<StorePackageUpdateResult, StorePackageUpdateStatus> downloadOperation =
this.context.RequestDownloadStorePackageUpdatesAsync(updates);
// The Progress async method is called one time for each step in the download process for each
// package in this request.
downloadOperation.Progress = async (asyncInfo, progress) =>
{
await this.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal,
() =>
{
downloadProgressBar.Value = progress.PackageDownloadProgress;
});
};
StorePackageUpdateResult result = await downloadOperation.AsTask();
switch (result.OverallState)
{
case StorePackageUpdateState.Completed:
downloadedSuccessfully = true;
break;
default:
// Get the failed updates.
var failedUpdates = result.StorePackageUpdateStatuses.Where(
status => status.PackageUpdateState != StorePackageUpdateState.Completed);
// See if any failed updates were mandatory
if (updates.Any(u => u.Mandatory && failedUpdates.Any(
failed => failed.PackageFamilyName == u.Package.Id.FamilyName)))
{
// At least one of the updates is mandatory. Perform whatever actions you
// want to take for your app: for example, notify the user and disable
// features in your app.
HandleMandatoryPackageError();
}
break;
}
return downloadedSuccessfully;
}
// Helper method for installing package updates.
private async Task InstallPackageUpdatesAsync(IEnumerable<StorePackageUpdate> updates)
{
IAsyncOperationWithProgress<StorePackageUpdateResult, StorePackageUpdateStatus> installOperation =
this.context.RequestDownloadAndInstallStorePackageUpdatesAsync(updates);
// The package updates were already downloaded separately, so this method skips the download
// operation and only installs the updates; no download progress notifications are provided.
StorePackageUpdateResult result = await installOperation.AsTask();
switch (result.OverallState)
{
case StorePackageUpdateState.Completed:
break;
default:
// Get the failed updates.
var failedUpdates = result.StorePackageUpdateStatuses.Where(
status => status.PackageUpdateState != StorePackageUpdateState.Completed);
// See if any failed updates were mandatory
if (updates.Any(u => u.Mandatory && failedUpdates.Any(failed => failed.PackageFamilyName == u.Package.Id.FamilyName)))
{
// At least one of the updates is mandatory, so tell the user.
HandleMandatoryPackageError();
}
break;
}
}
// Helper method for handling the scenario where a mandatory package update fails to
// download or install. Add code to this method to perform whatever actions you want
// to take, such as notifying the user and disabling features in your app.
private void HandleMandatoryPackageError()
{
}
卸载可选包
从 Windows 10 版本 1803 开始,可以使用 RequestUninstallStorePackageAsync 或 RequestUninstallStorePackageByStoreIdAsync 方法卸载当前应用的 可选包 (包括 DLC 包)。 例如,如果你有一个应用,其中包含通过可选包安装的内容,你可能想要提供一个 UI,使用户能够卸载可选包以释放磁盘空间。
下面的代码示例演示如何调用 RequestUninstallStorePackageAsync。 此示例假定:
- 代码文件包含适用于
Windows.Services.Store 和System.Threading.Tasks 命名空间的 语句。 - 该应用是一个单用户应用,仅在启动应用的用户的上下文中运行。 对于 多用户应用,请使用 GetForUser 方法获取 StoreContext 对象而不是 GetDefault 方法。
public async Task UninstallPackage(Windows.ApplicationModel.Package package)
{
if (context == null)
{
context = StoreContext.GetDefault();
}
var storeContext = StoreContext.GetDefault();
IAsyncOperation<StoreUninstallStorePackageResult> uninstallOperation =
storeContext.RequestUninstallStorePackageAsync(package);
// At this point, you can update your app UI to show that the package
// is installing.
uninstallOperation.Completed += (asyncInfo, status) =>
{
StoreUninstallStorePackageResult result = uninstallOperation.GetResults();
switch (result.Status)
{
case StoreUninstallStorePackageStatus.Succeeded:
{
// Update your app UI to show the package as uninstalled.
break;
}
default:
{
// Update your app UI to show that the package uninstall failed.
break;
}
}
};
}
获取下载队列信息
从 Windows 10 版本 1803 开始,可以使用 GetAssociatedStoreQueueItemsAsync 和 GetStoreQueueItemsAsync 方法来获取当前下载和安装队列中有关包的信息。 如果你的应用或游戏支持大型可选包(包括 DLL),可能需要数小时或几天才能下载和安装,并且你想要在下载和安装过程完成之前正常处理客户关闭你的应用或游戏的情况时,这些方法非常有用。 当客户再次启动应用或游戏时,你的代码可以使用这些方法来获取有关仍在下载和安装队列中的包状态的信息,以便你可以向客户显示每个包的状态。
下面的代码示例演示如何调用 GetAssociatedStoreQueueItemsAsync 以获取当前应用的正在进行的包更新列表,并检索每个包的状态信息。 此示例假定:
- 代码文件包含适用于
Windows.Services.Store 和System.Threading.Tasks 命名空间的 语句。 - 该应用是一个单用户应用,仅在启动应用的用户的上下文中运行。 对于 多用户应用,请使用 GetForUser 方法获取 StoreContext 对象而不是 GetDefault 方法。
注释
在此示例中,代码调用的 MarkUpdateInProgressInUI、RemoveItemFromUI、MarkInstallCompleteInUI、MarkInstallErrorInUI和 MarkInstallPausedInUI 方法是占位符方法,它们旨在根据您的应用设计按需实现。
private StoreContext context = null;
private async Task GetQueuedInstallItemsAndBuildInitialStoreUI()
{
if (context == null)
{
context = StoreContext.GetDefault();
}
// Get the Store packages in the install queue.
IReadOnlyList<StoreQueueItem> storeUpdateItems = await context.GetAssociatedStoreQueueItemsAsync();
foreach (StoreQueueItem storeItem in storeUpdateItems)
{
// In this example we only care about package updates.
if (storeItem.InstallKind != StoreQueueItemKind.Update)
continue;
StoreQueueItemStatus currentStatus = storeItem.GetCurrentStatus();
StoreQueueItemState installState = currentStatus.PackageInstallState;
StoreQueueItemExtendedState extendedInstallState =
currentStatus.PackageInstallExtendedState;
// Handle the StatusChanged event to display current status to the customer.
storeItem.StatusChanged += StoreItem_StatusChanged;
switch (installState)
{
// Download and install are still in progress, so update the status for this
// item and provide the extended state info. The following methods are not
// implemented in this example; you should implement them as needed for your
// app's UI.
case StoreQueueItemState.Active:
MarkUpdateInProgressInUI(storeItem, extendedInstallState);
break;
case StoreQueueItemState.Canceled:
RemoveItemFromUI(storeItem);
break;
case StoreQueueItemState.Completed:
MarkInstallCompleteInUI(storeItem);
break;
case StoreQueueItemState.Error:
MarkInstallErrorInUI(storeItem);
break;
case StoreQueueItemState.Paused:
MarkInstallPausedInUI(storeItem, installState, extendedInstallState);
break;
}
}
}
private void StoreItem_StatusChanged(StoreQueueItem sender, object args)
{
StoreQueueItemStatus currentStatus = sender.GetCurrentStatus();
StoreQueueItemState installState = currentStatus.PackageInstallState;
StoreQueueItemExtendedState extendedInstallState = currentStatus.PackageInstallExtendedState;
switch (installState)
{
// Download and install are still in progress, so update the status for this
// item and provide the extended state info. The following methods are not
// implemented in this example; you should implement them as needed for your
// app's UI.
case StoreQueueItemState.Active:
MarkUpdateInProgressInUI(sender, extendedInstallState);
break;
case StoreQueueItemState.Canceled:
RemoveItemFromUI(sender);
break;
case StoreQueueItemState.Completed:
MarkInstallCompleteInUI(sender);
break;
case StoreQueueItemState.Error:
MarkInstallErrorInUI(sender);
break;
case StoreQueueItemState.Paused:
MarkInstallPausedInUI(sender, installState, extendedInstallState);
break;
}
}