> ## Documentation Index
> Fetch the complete documentation index at: https://devdocs.xbox.com/llms.txt
> Use this file to discover all available pages before exploring further.

# 管理并授权可下载内容 (DLC)

> 在游戏中将耐用品作为可下载内容 (DLC) 使用 — 合作伙伴中心设置、授权检查、安装管理以及运行时权益代码。

可下载内容 (DLC) 是一种产品，购买后会给用户提供一个可下载的包。DLC 需要单独授权和挂载后，其内容才能被访问。

DLC 包可根据每个设备的内容共享行为进行授权，如[游戏的产品共享模式](/publishing/xstore-commerce/xstore-product-sharing)所述。

合作伙伴中心还支持不带包的耐用品。无包的耐用品适用于仅授权的产品，用于启用基础游戏已经安装的内容。这类仅授权产品省去了创建空包所需的步骤，否则用户还需下载这些空包。有关更多信息，请参阅[如何使用无包的耐用品](/publishing/xstore-commerce/xstore-dwob)。

## DLC 的开发工作流程

请参阅可下载内容 (DLC) 包的文档，以了解如何配置 DLC 内容的更多信息，包括如何通过 MicrosoftGame.config 与基础游戏建立关联。

安装 DLC 有三种方式：

1. [Loose DLC 部署](#1-loose-dlc-deployment)
2. [本地安装 DLC 包](#2-locally-install-dlc-package)
3. [从商店安装 DLC 包](#3-install-dlc-package-from-store)

### 1. Loose DLC 部署

安装指向你的 loose DLC MicrosoftGame.config 和资产文件的目录：

**XBOX：**

```cmd theme={null}
> xbapp deploy <DLC directory>

Package Full Name: 41336MicrosoftATG.DLC1_2020.10.14.0_neutral__dspnxghe87tn0
The operation completed successfully.
```

**PC：**

```cmd theme={null}
> wdapp register <DLC directory>  

Registered 41336MicrosoftATG.DLC1_2020.10.14.0_x64__dspnxghe87tn0
Copied temporary generated AppXManifest.xml file to C:\Users\<ntuser>\AppData\Local\Temp\41336MicrosoftATG.DLC1_2020.10.14.0_x64__dspnxghe87tn0_AppXManifest.xml
The operation completed successfully.
```

### 2. 本地安装 DLC 包

安装由 makepkg 创建的 .xvc/.msixvc：

**XBOX：**

```cmd theme={null}
> xbapp install <xvc package>  

12:16:21.800 Registered for streaming: 41336MicrosoftATG.DLC1_2020.10.14.0_neutral__dspnxghe87tn0_xs.xvc
12:16:22.645 Launch   0.00% Package   0.00%
12:16:32.650 Launch 100.00% Package 100.00%
12:16:32.651 Streaming install finished

The operation completed successfully.
```

**PC：**

```cmd theme={null}
> wdapp install <.msixvc package>  

Starting installation. See the Microsoft Store app for further details.
Launch   100% Package   100%
Installed 100%.
Installed 41336MicrosoftATG.DLC1_2020.10.14.0_x64__dspnxghe87tn0
```

### 3. 从商店安装 DLC 包

使用登录到开发沙盒的测试账号，在 Store 应用中搜索或直接链接到 DLC 产品页面并进行安装。

## 验证 DLC 安装

**XBOX：**

```cmd theme={null}
> xbapp listdlc

Registered DLC by Package Full Name:

   41336MicrosoftATG.DLC1_2020.10.14.0_neutral__dspnxghe87tn0

The operation completed successfully.
```

**PC：**

```cmd theme={null}
> wdapp listdlc

Registered DLC packages by Package Full Name:

41336MicrosoftATG.DLC1_2020.10.14.0_x64__dspnxghe87tn0

The operation completed successfully.
```

在 PC 上，也可以使用 PowerShell 中的 `get-appxpackage` 查看特定游戏已安装的 DLC，请注意 **Dependencies** 部分：

```cmd theme={null}
> get-appxpackage 41336MicrosoftATG.DownloadableContent

Name              : 41336MicrosoftATG.DownloadableContent
Publisher         : CN=A4954634-DF4B-47C7-AB70-D3215D246AF1
Architecture      : X64
ResourceId        :
Version           : 2020.10.14.0
PackageFullName   : 41336MicrosoftATG.DownloadableContent_2020.10.14.0_x64__dspnxghe87tn0
InstallLocation   : E:\Repos\ATG\gx_dev\Samples\Live\DownloadableContent\Gaming.Desktop.x64\Debug
IsFramework       : False
PackageFamilyName : 41336MicrosoftATG.DownloadableContent_dspnxghe87tn0
PublisherId       : dspnxghe87tn0
IsResourcePackage : False
IsBundle          : False
IsDevelopmentMode : True
NonRemovable      : False
Dependencies      : {41336MicrosoftATG.DLC1_2020.10.14.0_x64__dspnxghe87tn0}
IsPartiallyStaged : False
SignatureKind     : None
Status            : Ok
```

**SignatureKind** 表示已安装包的类型：本地构建并安装的包为 *None*，从商店安装的包为 *Store*。

## 购买并安装 DLC

请参阅[商店基本操作](/publishing/xstore-commerce/xstore-basic-operations)一文，了解如何枚举目录并提供购买附加内容的功能。可通过检查 `[XStoreProduct](/reference/system/xstore/xstore_members).hasDigitalDownload` 判断附加内容产品是否为 DLC。

如果 DLC 是从商店购买的，则 DLC 会被排队等待下载。

如果 DLC 是使用 [XStoreShowPurchaseUiAsync](/reference/system/xstore/xstore_members) 购买的，DLC 不会排队等待下载。相反，游戏应根据代码手动请求下载。也可以选择创建监控器以跟踪进度。

**包标识符 (package identifier)** 是一个用于标识特定包的不透明字符串。该标识符在每个包上唯一，但游戏每次启动实例都不同。因此，请勿在当前会话之外存储或复用该标识符。

根据不同场景，你可以使用以下方法获取包的包标识符：

* 调用 [XStoreDownloadAndInstallPackagesAsync](/reference/system/xstore/xstore_members) 下载并安装包后，可以通过调用 [XStoreDownloadAndInstallPackagesResult](/reference/system/xstore/xstore_members) 获取这些包的包标识符。
* 对于已下载并已安装的包，可以通过调用 [XPackageEnumeratePackages](/reference/system/xstore/xstore_members)，然后从每个枚举包传回给你的 [XPackageEnumerationCallback](/reference/system/xstore/xstore_members) 回调函数的 [XPackageDetails](/reference/system/xstore/xstore_members) 结构中检索包标识符。
* 可以通过调用 [XPackageGetCurrentProcessPackageIdentifier](/reference/system/xstore/xstore_members) 获取当前游戏的包标识符。

```cpp theme={null}
void StartDownload()
{
    auto async = new XAsyncBlock{};
    async->queue = m_asyncQueue;
    async->context = this;
    async->callback = [](XAsyncBlock* asyncBlockInner)
    {
        uint32_t count = 0;
        HRESULT hr = XStoreDownloadAndInstallPackagesResultCount(asyncBlockInner, &count);

        std::vector<char[XPACKAGE_IDENTIFIER_MAX_LENGTH]> packageIds(count);

        XStoreDownloadAndInstallPackagesResult(asyncBlockInner, count, packageIds.data());

        for(auto packageId : packageIds)
        {
            hr = XPackageCreateInstallationMonitor(
                packageId,  
                0,  
                nullptr,  
                1000,  
                m_asyncQueue,
                &m_pimHandle);

            if(SUCCEEDED(hr))
            {
                XTaskQueueRegistrationToken callbackToken;

                XPackageRegisterInstallationProgressChanged(
                    m_pimHandle,
                    this,
                    [](void* context, XPackageInstallationMonitorHandle pimHandle)
                    {
                        XPackageInstallationProgress progress;
                        XPackageGetInstallationProgress(pimHandle, &progress);

                        if(!progress.completed)
                        {
                            printf("%llu%% installed\n", static_cast<double>(progress.installedBytes) / static_cast<double>(progress.totalBytes);
                        }
                        else
                        {
                            XPackageCloseInstallationMonitorHandle(pimHandle);
                        }
                    }, &callbackToken);

                // Persist callbackToken to unregister upon completion
            }
        }

        delete asyncBlockInner;
    }

    const char* storeIds[] =  
    {
        "9PLNMXRKNM4C",  
        "9PLNMXRKNM5D"
    };

    HRESULT hr = XStoreDownloadAndInstallPackagesAsync(storeContext, storeIds, ARRAYSIZE(storeIds), async);

    if (FAILED(hr))
    {
        delete async;
        return;
    }
}
```

## 枚举 DLC 包

无论 DLC 以何种方式安装，游戏在使用之前都需要枚举已安装的 DLC。枚举已安装的 DLC 是获取包标识符的地方。

```cpp theme={null}
bool CALLBACK DlcCallback(void* context, const XPackageDetails* details)
{
    printf("DLC found: name: %s packageId: %s\n", details->displayName, details->packageIdentifier);

    return true;
}

void RefreshInstalledPackages()
{
    HRESULT hr = XPackageEnumeratePackages(
        XPackageKind::Content,  
        XPackageEnumerationScope::ThisAndRelated,  
        this,  
        DlcCallback);
}
```

## 检测 DLC 包已安装

```cpp theme={null}
void RegisterPackageInstalledEvent()
{
    XPackageRegisterPackageInstalled(
        m_asyncQueue,
        this,  
        [](void *context, const XPackageDetails *package)
        {
            printf("Package Installed event received: %s\n", package->displayName);
        },  
        &m_packageInstallToken);
}
```

如果代码示例未被触及，请参阅[故障排除](/publishing/xstore-commerce/xstore-troubleshooting)部分。无论 DLC 包通过哪种方法安装，事件都应被触发。

## 获取 DLC 的许可证

在开发阶段，请参阅[在开发中测试 DLC 授权](#testing-dlc-licensing-in-development)中的注意事项。

基础游戏需要为 DLC 获取许可证，以决定是否应向用户提供其内容的访问权限。

游戏通常使用限制式授权 (Restrictive Licensing)：在游戏获取包的许可证后，对该包的访问将锁定到该设备和产品的该实例上。游戏必须释放该许可证，另一个实例或设备才能获得访问权和许可证。请与你的 Microsoft 客户代表沟通，以确保你的游戏被配置为使用限制式授权（不在合作伙伴中心中配置）。有关更多信息，请参阅[开放式与限制式授权](/publishing/xstore-commerce/xstore-open-restrictive-licensing)。

由于游戏必须释放许可证，因此游戏负责跟踪它获取的所有 DLC 许可证，并在不再需要时或终止时释放它们。如果游戏未能释放许可证，则会在超时期后自动释放该许可证。

```cpp theme={null}
void CALLBACK AcquireLicenseForPackageCallback(XAsyncBlock* async)
{
    XStoreLicenseHandle licenseHandle = nullptr;

    HRESULT hr = XStoreAcquireLicenseForPackageResult(
        async,
        &licenseHandle);

    if (FAILED(hr))
    {
        printf("Failed retrieve the license handle: 0x%x\n", hr);
        return;
    }

    bool isValid = XStoreIsLicenseValid(licenseHandle);

    printf("isValid: %s\n", isValid ? "true" : "false");

    hr = XStoreRegisterPackageLicenseLost(licenseHandle, m_asyncQueue, context,
       [](void *context)  
       {
           // Check if the license lost corresponded to any mounted DLC
           // If so, it is up to the game to determine an appropriate time
           // to unmount the DLC, e.g. after the current match is completed
       });

    delete async;
}

void AcquireLicenseForPackage(const char* packageIdentifier)
{
    auto async = new XAsyncBlock{};
    async->context = this;
    async->queue = m_asyncQueue;
    async->callback = AcquireLicenseForPackageCallback;

    HRESULT hr = XStoreAcquireLicenseForPackageAsync(
        m_storeContext,
        packageIdentifier,
        async);

    if (FAILED(hr))
    {
        delete async;
        return;
    }
}
```

## 确定 DLC 的许可证来源

使用 [XStoreCanAcquireLicenseForPackageAsync](/reference/system/xstore/xstore_members) 或 [XStoreCanAcquireLicenseForStoreIdAsync](/reference/system/xstore/xstore_members)（取决于标识符类型），可以判断 DLC 是否：

1. 可授权，以便在不可授权时呈现购买选项
2. 通过光盘或数字许可证进行授权

```cpp theme={null}
void PreviewLicense(const char* storeId)
{
    auto async = new XAsyncBlock{};
    async->queue = m_asyncQueue;
    async->callback = [](XAsyncBlock* async)
    {
        XStoreCanAcquireLicenseResult result;

        HRESULT hr = XStoreCanAcquireLicenseForStoreIdResult(
            async,
            &result);

        if (FAILED(hr))
        {
            printf("Error calling XStoreCanAcquireLicenseForStoreIdResult: 0x%x\n", hr);
        }
        else
        {
            // Status = 1 Licensable and
            // a. licensableSku = "DISC" if disc licensed
            // b. licensableSku = "0010" or similar if digital licensed
            printf("Status: %u LicensableSku: %s\n", result.status, result.licensableSku);
        }

        delete async;
    };

    HRESULT hr = XStoreCanAcquireLicenseForStoreIdAsync(
        m_xStoreContext,
        storeId,
        async);

    if (FAILED(hr))
    {
        delete async;

        printf("Error calling XStoreCanAcquireLicenseForStoreIdAsync: 0x%x", hr);
        return;
    }
}
```

## 挂载和卸载 DLC

一旦成功获取 DLC 许可证，就可以挂载并访问 DLC 内容。

```cpp theme={null}

void CALLBACK MountPackageCallback(XAsyncBlock* async)
{
    XPackageMountHandle mountHandle = {};

    HRESULT hr = XPackageMountWithUiResult(async, &mountHandle);

    if (SUCCEEDED(hr))
    {
        // Access DLC goodness
    }
    else
    {
        XStoreCloseLicenseHandle(license);
        printf("Error mounting package: 0x%x\n", hr);
    }

    delete async;
};

void MountPackage(const char* packageIdentifier)
{
    auto async = new XAsyncBlock{};
    async->queue = m_asyncQueue;
    async->context = context;
    async->callback = MountPackageCallback;

    HRESULT hr = XPackageMountWithUiAsync(packageIdentifier, async);

    if (FAILED(hr))
    {
        printf("XPackageMountWithUiAsync failed : 0x%x\n", hr);
        delete async;
    }
}
```

卸载时，请释放所有令牌和句柄：

```cpp theme={null}
void UnmountPackage(XPackageMountHandle mountHandle, XStoreLicenseHandle license, XTaskQueueRegistrationToken licenseLostToken)
{
    XStoreUnregisterPackageLicenseLost(license, licenseLostToken, false);

    XPackageCloseMountHandle(mountHandle);

    XStoreCloseLicenseHandle(license);
}
```

## 卸载 DLC

使用 [XPackageUninstallPackage](/reference/system/xstore/xstore_members) 卸载 DLC 包。必须先卸载挂载。

## Smart Delivery 与 DLC

XBOX Series X/S 游戏可以授权并挂载为 XBOX One 游戏创建的 DLC。通常，这种情况发生在 DLC 包不包含游戏使用的数据时。唯一的检查方法是查看 ERA DLC 的 package.appxmanifest 中的 `AllowedProduct` ID（一个 GUID）是否与在合作伙伴中心分配给该产品的 Legacy XBOX Product ID 相匹配。

如果不匹配，则该游戏可能是从已停用的 XBOX Developer Portal (XDP) 迁移而来，并且只能与从商店下载的包一起使用，因为 XBOX Series X/S 版本被分配到 XBOX One 的 product ID。出于开发目的，请参阅[故障排除](/publishing/xstore-commerce/xstore-troubleshooting)部分中的说明。

## 来自不同产品的 DLC

可以枚举并使用来自不同产品的 DLC。对于希望使用来自不同产品的 DLC 的游戏，请在合作伙伴中心的 **产品关系设置 (Product relationship setup)** 部分为该 DLC 产品分配 “Can sell and use” 关系。可选的产品仅限于发行商账户。

## 在开发中测试 DLC 授权

有关更多信息，请参阅[启用授权测试](/publishing/xstore-commerce/xstore-licensing-setup)。

本地 DLC 包必须使用 /contentid 参数创建。

每个 DLC 包的 EKBID 都必须相对于默认值被覆盖。

## 参考 API 文档

* [XStore（API 内容）](/reference/system/xstore/xstore_members)
  * 函数
    * [XStoreShowPurchaseUiAsync](/reference/system/xstore/xstore_members)
    * [XStoreDownloadAndInstallPackagesAsync](/reference/system/xstore/xstore_members)
    * [XStoreDownloadAndInstallPackagesResult](/reference/system/xstore/xstore_members)
    * [XStoreCanAcquireLicenseForPackageAsync](/reference/system/xstore/xstore_members)
    * [XStoreCanAcquireLicenseForStoreIdAsync](/reference/system/xstore/xstore_members)
  * 结构体
    * [xstoreproduct](/reference/system/xstore/xstore_members)
* [XPackage（API 内容）](/reference/system/xstore/xstore_members)
  * 函数
    * [XPackageEnumeratePackages](/reference/system/xstore/xstore_members)
    * [XPackageEnumerationCallback](/reference/system/xstore/xstore_members)
    * [XPackageGetCurrentProcessPackageIdentifier](/reference/system/xstore/xstore_members)
    * [XPackageUninstallPackage](/reference/system/xstore/xstore_members)
  * 结构体
    * [XPackageDetails](/reference/system/xstore/xstore_members)

## 另请参阅

[商务概述](/publishing/xstore-commerce/xstore-commerce-overview)

[启用授权测试](/publishing/xstore-commerce/xstore-licensing-setup)

[如何使用无包的耐用品](/publishing/xstore-commerce/xstore-dwob)

[XStore API 参考](/reference/system/xstore/xstore_members)


## Related topics

- [如何使用无包的耐用品](/zh-CN/publishing/xstore-commerce/xstore-dwob.md)
- [选择合适的产品类型](/zh-CN/publishing/xstore-commerce/xstore-choosing-product-type.md)
- [启用授权测试](/zh-CN/publishing/xstore-commerce/xstore-licensing-setup.md)
- [GDK 商务系统概述](/zh-CN/publishing/xstore-commerce/xstore-overview.md)
- [为玩家授予对附加内容的访问权限](/zh-CN/publishing/xstore-commerce/xstore-granting-access.md)
