> ## 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.

# 偵錯自訂 HTTP 堆疊

> 偵錯自訂 HTTP 堆疊

# 偵錯自訂 HTTP 堆疊

當您的遊戲直接使用自訂 HTTP 堆疊而非 xCurl，且您需要透過 [XBOX Multiplayer Analysis Tool (XMAT)](/zh-TW/tools/tools-services/live-xbox-multiplayer-analysis-tool) 等 Proxy 偵錯流量時，請使用本文。如需上游指示和下載，請參閱 [XMAT 專案文件](https://github.com/microsoft/xbox-multiplayer-analysis-tool/blob/main/README.md)和 [XMAT 版本](https://github.com/microsoft/xbox-multiplayer-analysis-tool/releases)。也支援 Fiddler。

## 使用此指引的時機

當下列一或多項情況適用時，此指引最為實用：

* 您的遊戲需要直接檢查或套用 Proxy 設定。
* 您的偵錯工作流程依賴 XMAT、Fiddler 或其他開發用 Proxy。
* 您的 HTTP 堆疊不使用 Schannel 作為安全性提供者，且必須明確載入 Proxy 憑證。

如需一般安全性指引，請參閱 [Microsoft Game Development Kit 遊戲的安全 Web 要求與 WebSocket 最佳做法](/zh-TW/build/game-principles/security/secure-webrequest-impl)。

## XBOX 與 PC 上的 Proxy 設定

在 XBOX 上處理 Proxy 設定時，如果您的組建和目標環境中可使用該路徑，請優先搭配 `WinHttpProxySettingsTypeXBox` 使用 `WinHttpGetProxySettingsEx`。在 XBOX 上，從 2026 年 4 月版 Microsoft Game Development Kit (GDK) 開始，此 API 是讀取 Proxy 設定的前瞻性 API。

如果您的遊戲在 XBOX 上以較早的 GDK 為目標，則無法使用 XBOX 專用的 Proxy 設定路徑。在此情況下，請在您的 HTTP 堆疊中手動設定 Proxy 位址和連接埠。即使無法使用 XBOX 專用的 Proxy 位址 API，本文稍後所述的憑證載入路徑仍適用於這些較舊的 GDK。

PC 遊戲也可以透過 WinHTTP 查詢 Proxy 設定，但請勿直接複製 XBOX 的流程。在 PC 上，`WinHttpGetProxySettingsEx` 是非同步的，因此請等候作業完成，再呼叫 `WinHttpGetProxySettingsResultEx` 並將傳回的 Proxy 值複製到您的 HTTP 堆疊中。

以 libcurl 為例，可以使用 XBOX Proxy 設定 API 解析 Proxy 位址，然後直接將其傳入堆疊。此範例使用 `WINHTTP_FLAG_ASYNC` 開啟工作階段並註冊狀態回呼，讓程式碼同時處理同步完成和 `ERROR_IO_PENDING` 傳回值。下列範例顯示呼叫流程：

```cpp theme={null}
// WinHttpCreateProxyResolver is not declared in WinHttp.h when using the GDK.
WINHTTPAPI DWORD WINAPI WinHttpCreateProxyResolver(HINTERNET hSession, HINTERNET* phResolver);

std::string ResolveXboxProxy()
{
    HINTERNET session = WinHttpOpen(
        L"CustomHttp/1.0",
        WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY,
        WINHTTP_NO_PROXY_NAME,
        WINHTTP_NO_PROXY_BYPASS,
        WINHTTP_FLAG_ASYNC);
    if (!session)
        return {};

    struct ProxyContext
    {
        HANDLE event;
        DWORD  error;
    };

    ProxyContext ctx{ CreateEventW(nullptr, TRUE, FALSE, nullptr), ERROR_SUCCESS };

    WinHttpSetStatusCallback(session,
        [](HINTERNET, DWORD_PTR context, DWORD status, LPVOID info, DWORD)
        {
            auto* c = reinterpret_cast<ProxyContext*>(context);
            if (status == WINHTTP_CALLBACK_STATUS_GETPROXYFORURL_COMPLETE)
            {
                c->error = ERROR_SUCCESS;
                SetEvent(c->event);
            }
            else if (status == WINHTTP_CALLBACK_STATUS_REQUEST_ERROR)
            {
                c->error = reinterpret_cast<WINHTTP_ASYNC_RESULT*>(info)->dwError;
                SetEvent(c->event);
            }
        },
        WINHTTP_CALLBACK_FLAG_ALL_COMPLETIONS,
        0);

    HINTERNET resolver = nullptr;
    if (WinHttpCreateProxyResolver(session, &resolver) != ERROR_SUCCESS)
    {
        CloseHandle(ctx.event);
        WinHttpCloseHandle(session);
        return {};
    }

    DWORD result = WinHttpGetProxySettingsEx(
        resolver,
        WinHttpProxySettingsTypeXBox,
        nullptr,
        reinterpret_cast<DWORD_PTR>(&ctx));

    if (result == ERROR_IO_PENDING)
    {
        // Wait for the async callback to fire (5-second timeout).
        WaitForSingleObject(ctx.event, 5000);
        result = ctx.error;
    }

    std::string proxyAddress;
    if (result == ERROR_SUCCESS)
    {
        WINHTTP_PROXY_SETTINGS_EX proxySettings{};
        if (WinHttpGetProxySettingsResultEx(resolver, &proxySettings) == ERROR_SUCCESS)
        {
            PCWSTR proxy =
                proxySettings.pcwszSecureProxy != nullptr
                    ? proxySettings.pcwszSecureProxy
                    : proxySettings.pcwszProxy;

            if (proxy != nullptr)
            {
                // Example function for conversion.
                proxyAddress = Utf16ToUtf8(std::wstring(proxy));
            }

            WinHttpFreeProxySettingsEx(WinHttpProxySettingsTypeXBox, &proxySettings);
        }
    }

    CloseHandle(ctx.event);
    WinHttpCloseHandle(resolver);
    WinHttpCloseHandle(session);
    return proxyAddress;
}

std::string proxyAddress = ResolveXboxProxy();
if (!proxyAddress.empty())
{
    curl_easy_setopt(curlHandle, CURLOPT_PROXY, proxyAddress.c_str());
}
```

## TLS 提供者行為

### 以 Schannel 為基礎的堆疊

如果您的堆疊依賴 Schannel 支援的 TLS 行為，主機會自動套用您設定的 Proxy 憑證。此行為也適用於使用 Schannel 作為 TLS 提供者的 libcurl 組建。

### 非 Schannel 堆疊

如果您的堆疊依賴 OpenSSL 或其他非 Schannel 的 TLS 提供者，您的遊戲必須負責明確載入作用中偵錯工具所需的憑證。請在發行程式碼中保留此憑證載入路徑。在主機上，遊戲也應在 `RETAIL` 組建中保留相關的 Proxy 偵錯路徑，以便在需要時仍能診斷經由 Proxy 的流量。

如需信任、Proxy 及安全預設值的指引，請參閱 [Microsoft Game Development Kit 遊戲的安全 Web 要求與 WebSocket 最佳做法](/zh-TW/build/game-principles/security/secure-webrequest-impl)。

## 尋找 Proxy 根憑證

對於非 Schannel 堆疊，請明確載入對應於偵錯工具的憑證，並在發行程式碼中保留此路徑。憑證位置和主體取決於工具。例如，[XBOX Multiplayer Analysis Tool (XMAT)](/zh-TW/tools/tools-services/live-xbox-multiplayer-analysis-tool) 使用 `CurrentUser\Root` 中的憑證 `Xbox Multiplayer Analysis Tool Root Cert Authority`。此範例會查詢該憑證，並將 DER 編碼的憑證位元組複製到遊戲擁有的緩衝區中。

```cpp theme={null}
#include <wincrypt.h>
#include <vector>

HRESULT ExtractXmatRootCertificateDer(_Out_ std::vector<uint8_t>& encodedCertificate)
{
    encodedCertificate.clear();

    constexpr DWORD certificateStoreLocation = CERT_SYSTEM_STORE_CURRENT_USER;
    constexpr wchar_t certificateStoreName[] = L"Root";
    constexpr wchar_t certificateSubjectName[] =
        L"Xbox Multiplayer Analysis Tool Root Cert Authority";

    HCERTSTORE certificateStore = CertOpenStore(
        CERT_STORE_PROV_SYSTEM_W,
        0,
        0,
        certificateStoreLocation |
            CERT_STORE_OPEN_EXISTING_FLAG |
            CERT_STORE_READONLY_FLAG,
        certificateStoreName);
    if (certificateStore == nullptr)
    {
        return HRESULT_FROM_WIN32(GetLastError());
    }

    PCCERT_CONTEXT certificate = CertFindCertificateInStore(
        certificateStore,
        X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
        0,
        CERT_FIND_SUBJECT_STR_W,
        certificateSubjectName,
        nullptr);
    if (certificate == nullptr)
    {
        HRESULT hr = HRESULT_FROM_WIN32(GetLastError());
        CertCloseStore(certificateStore, 0);
        return hr;
    }

    encodedCertificate.assign(
        certificate->pbCertEncoded,
        certificate->pbCertEncoded + certificate->cbCertEncoded);

    CertFreeCertificateContext(certificate);
    CertCloseStore(certificateStore, 0);
    return S_OK;
}
```

完成此步驟後，請將 `encodedCertificate` 傳入您的 TLS 提供者所使用的任何憑證載入路徑。

## 建議的偵錯工作流程

1. 針對偵錯 Proxy 設定主機：
   * 若為 XMAT，請為主機新增 **Web Proxy** 擷取，然後選取 **Enable XBOX Proxy**。如需詳細資訊，請參閱[使用 XBOX Multiplayer Analysis Tool 檢查及修改網路流量](/zh-TW/tools/tools-services/live-xbox-multiplayer-analysis-tool)。
   * 若為 Fiddler，請依照 [XBOX 開發套件上的 Fiddler](/zh-TW/build/console-features/networking/tools/fiddler-setup-networking) 中的說明，在 XBOX Device Portal 中設定主機 Proxy 設定。
2. 如果您以 2026 年 4 月版或更新版本的 GDK 為 XBOX 目標，且您的遊戲需要直接檢查 Proxy 設定，請搭配 `WinHttpProxySettingsTypeXBox` 使用 `WinHttpGetProxySettingsEx`。
3. 如果您的 PC 程式碼路徑透過 WinHTTP 查詢 Proxy 設定，請等候非同步結果，再呼叫 `WinHttpGetProxySettingsResultEx` 並套用傳回的 Proxy 值。
4. 如果您在 XBOX 上以較早的 GDK 為目標，或使用不採用 XBOX 專用 Proxy 設定路徑的堆疊，請在遊戲內手動設定 Proxy 位址和連接埠。
5. 如果您使用 Schannel，請依賴主機套用的憑證。如果您使用 OpenSSL 或其他堆疊，請從憑證存放區擷取您所使用偵錯工具的憑證，並明確載入。

## 相關頁面

* [使用 XBOX Multiplayer Analysis Tool 檢查及修改網路流量](/zh-TW/tools/tools-services/live-xbox-multiplayer-analysis-tool)
* [XBOX 開發套件上的 Fiddler](/zh-TW/build/console-features/networking/tools/fiddler-setup-networking)
* [Microsoft Game Development Kit 遊戲的安全 Web 要求與 WebSocket 最佳做法](/zh-TW/build/game-principles/security/secure-webrequest-impl)


## Related topics

- [NDA 主題目錄 - Microsoft Learn 線上連結](/zh-TW/nda/nda-topic-directory.md)
- [存取 GDK 資源與下載](/zh-TW/home/onboarding-access/access-resources.md)
- [Windows 與 ROG XBOX Ally 總覽](/zh-TW/paths/windows/overview.md)
- [GDK 名詞與縮寫詞彙表](/zh-TW/home/glossary.md)
- [XBOX Series X|S 總覽](/zh-TW/paths/series-xs/overview.md)
