從 Microsoft Store 下載與安裝套件更新

從 Windows 10 版本 1607 開始,你可以使用 Windows.Services.Store 命名空間中 StoreContext 類別的方法,程式化地檢查 Microsoft Store 中目前應用程式的套件更新,並下載及安裝更新後的套件。 你也可以查詢在合作夥伴中心標記為強制的套件,並在強制更新安裝前停用應用程式功能。

Windows 10 1803 版本引入的額外 StoreContext 方法,讓你能靜默下載和安裝套件更新(不會顯示通知介面)、卸載可選套件,以及在應用程式的下載與安裝佇列中取得套件資訊。

這些功能幫助你自動讓用戶隨時掌握應用程式的最新版本、選用套件及商店中的相關服務。

Note

這些 Windows.Services.Store API 要求任何透過 Microsoft Store 發佈的 MSIX 套件應用程式,包括使用 Windows 應用程式 SDK 建置的 WinUI 3 應用程式,皆需具備套件身份與運作權限。 本文中的程式碼範例最初是為 UWP 撰寫的。 如果你要將它們調整為適用於 WinUI 3 應用程式,請將 this.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, ...) 替換為 DispatcherQueue.GetForCurrentThread().TryEnqueue(...),並將 MessageDialog(其在 WinUI 3 中需要 HWND 互通)替換為 ContentDialog,或使用 WinRT.Interop.InitializeWithWindow.Initialize 將對話方塊與你的視窗控制代碼建立關聯。

在使用者許可下下載並安裝套件更新

此程式碼範例示範如何使用 GetAppAndOptionalStorePackageUpdatesAsync 方法來發現所有可用的套件更新,然後呼叫 RequestDownloadAndInstallStorePackageUpdatesAsync 方法來下載與安裝更新。 使用此方法下載與安裝更新時,作業系統會顯示一個對話框,在下載更新前詢問使用者的權限。

Note

這些方法支援您的應用程式所需的 與可選套件 。 可選套件對於可下載內容(DLC)附加元件、因容量限制而分割大型應用程式,或是將額外內容與核心應用程式分開運送非常有用。 若欲取得授權,將使用可選套件(包括 DLC 外掛)的應用程式提交至 Store,請參閱 Windows 開發者支援

此程式碼範例假設:

  • 程式碼在 UWP XAML 頁面Windows.UI.Xaml.Controls.Page()的上下文中執行。 這些範例是為 UWP 撰寫的;請參閱本文頂端的備註,了解 WinUI 3 的改編版本。
  • 頁面包含一個 downloadProgressBar,用以提供下載操作的狀態。
  • 程式碼檔案包含針對 Windows.Services.StoreSystem.Threading.TasksWindows.UI.Popups 命名空間的 using 陳述式。
  • 該應用程式為單用戶應用程式,僅在啟動該應用程式的使用者情境下執行。 對於 多使用者應用程式,請使用 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();
        }
    }
}

Note

若要只下載(但不安裝)可用的套件更新,請使用 RequestDownloadStorePackageUpdatesAsync 方法。

顯示下載與安裝進度資訊

當你呼叫 RequestDownloadStorePackageUpdatesAsyncRequestDownloadAndInstallStorePackageUpdatesAsync 方法時,你可以指 派一個進度 處理器,該處理器會針對這個請求中的每個套件,在下載(或下載與安裝)過程中的每個步驟呼叫一次。 處理常式會收到一個 StorePackageUpdateStatus 物件,其中包含觸發進度通知之更新套件的資訊。 前一個範例使用 StorePackageUpdateStatus 物件的 PackageDownloadProgress 欄位來顯示下載與安裝的進度。

請注意,當你呼叫 RequestDownloadAndInstallStorePackageUpdatesAsync 來一次性下載和安裝套件更新時,套件的 PackageDownloadProgress 欄位在下載過程中會從 0.0 增加到 0.8,然後在安裝過程中從 0.8 增加到 1.0。 因此,如果你將自訂進度介面中顯示的百分比直接映射到 PackageDownloadProgress 欄位的值,當套件完成下載並顯示安裝對話框時,UI 會顯示 80%。 如果你希望自訂進度介面在套件下載並準備安裝時顯示 100%,你可以修改程式碼,當 PackageDownloadProgress 欄位達到 0.8 時,將 100% 指派給進度介面。

靜默下載並安裝套件更新

從 Windows 10 版本 1803 開始,你可以使用 TrySilentDownloadStorePackageUpdatesAsyncTrySilentDownloadAndInstallStorePackageUpdatesAsync 方法,靜默下載並安裝套件更新,不會顯示通知介面給使用者。 此操作僅在使用者已啟用商店自動啟用 更新應用程式 設定且未使用計量網路時成功。 在呼叫這些方法之前,你可以先檢查 CanSilentlyDownloadStorePackageUpdates 屬性,以判斷這些條件目前是否已滿足。

本程式碼範例示範如何使用 GetAppAndOptionalStorePackageUpdatesAsync 方法來發現所有可用的套件更新,然後呼叫 TrySilentDownloadStoreUpdatesAsyncTrySilentDownloadAndInstallStorePackageUpdatesAsync 方法,靜默下載並安裝更新。

此程式碼範例假設:

  • 程式碼檔案中有 using 語句,用於 Windows.Services.StoreSystem.Threading.Tasks 命名空間。
  • 該應用程式為單用戶應用程式,僅在啟動該應用程式的使用者情境下執行。 對於 多使用者應用程式,請使用 GetForUser 方法取得 StoreContext 物件,而非 GetDefault 方法。

Note

本範例中程式碼呼叫的 IsNowAGoodTimeToRestartAppRetryDownloadAndInstallLaterRetryInstallLater 方法,都是根據你自己應用程式設計的必要實作的佔位符方法。

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 或更新版本的應用程式建立套件提交時,你可以將該套件標記為強制,並標示它成為強制的日期和時間。 當此屬性設定完成且應用程式發現套件更新可用時,應用程式可以判斷該更新套件是否為強制性,並在更新安裝前調整其行為(例如,應用程式可以停用功能)。

Note

Microsoft 並不會強制執行套件更新是否為必要更新的狀態,而作業系統也不會提供使用者介面,向使用者指出必須安裝必要的應用程式更新。 開發者應使用必要設定,在自己的程式碼中強制執行應用程式的必要更新。

若要將套件提交標示為必填:

  1. 登入 Partner Center ,並導覽您的應用程式總覽頁面。
  2. 點擊包含你想強制的套件更新的提交名稱。
  3. 請前往該提交項目的 套件 頁面。 在本頁底部附近,選擇 「讓此更新成為強制 」,然後選擇套件更新成為強制的日期和時間。 此選項適用於提交中的所有包裹。

欲了解更多資訊,請參閱 上傳應用程式套件

Note

如果您建立了 套件釋出批次,就可以在該釋出批次的 Packages 頁面上,使用類似的介面將這些套件標示為必要。 在這種情況下,強制的套件更新僅適用於屬於該航班群組的客戶。

強制套件的程式碼範例

以下程式碼範例示範如何判斷任何更新套件是否為強制性。 通常,如果強制套件更新無法成功下載或安裝,您應該優雅地降低使用者的應用程式體驗。

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 開始,你可以使用 RequestUninstallStorePackageAsyncRequestUninstallStorePackageByStoreIdAsync 方法來卸載目前應用程式的可選套件(包括 DLC 套件)。 舉例來說,如果你的應用程式內容是透過可選套件安裝的,你可能會想提供一個使用者移除可選套件以釋放磁碟空間的介面。

以下程式碼範例示範如何呼叫 RequestUninstallStorePackageAsync。 此範例假設:

  • 程式碼檔案中有 using 語句,用於 Windows.Services.StoreSystem.Threading.Tasks 命名空間。
  • 該應用程式為單用戶應用程式,僅在啟動該應用程式的使用者情境下執行。 對於 多使用者應用程式,請使用 GetForUser 方法取得 StoreContext 物件,而非 GetDefault 方法。
public async Task UninstallPackage(Windows.ApplicationModel.Package package)
{
    if (context == null)
    {
        context = StoreContext.GetDefault();
    }

    IAsyncOperation<StoreUninstallStorePackageResult> uninstallOperation =
        context.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 開始,你可以使用 GetAssociatedStoreQueueItemsAsyncGetStoreQueueItemsAsync 方法,從商店取得目前下載與安裝佇列中的套件資訊。 如果你的應用程式或遊戲支援大型可選套件(包括 DLC),這些套件可能需要數小時甚至數天才能下載安裝,且你想優雅地處理客戶在下載與安裝完成前關閉你的應用程式或遊戲的情況。 當客戶重新啟動你的應用程式或遊戲時,你的程式碼可以使用這些方法取得仍在下載和安裝佇列中的套件狀態資訊,讓你能向客戶顯示每個套件的狀態。

以下程式碼範例示範如何呼叫 GetAssociatedStoreQueueItemsAsync 以取得目前應用程式進行中的套件更新清單,並取得每個套件的狀態資訊。 此範例假設:

  • 程式碼檔案中有 using 語句,用於 Windows.Services.StoreSystem.Threading.Tasks 命名空間。
  • 該應用程式為單用戶應用程式,僅在啟動該應用程式的使用者情境下執行。 對於 多使用者應用程式,請使用 GetForUser 方法取得 StoreContext 物件,而非 GetDefault 方法。

Note

本範例中程式碼呼叫的 MarkUpdateInProgressInUIRemoveItemFromUIMarkInstallCompleteInUIMarkInstallErrorInUIMarkInstallPausedInUI 方法,都是根據你自己應用程式設計的必要實作的佔位方法。

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;
    }
}