IHttpFilter 实现导致 HttpClient::GetStringAsync 调用的访问冲突

IHttpFilter implementation causes access violation for HttpClient::GetStringAsync call

我正在将 OAuth 身份验证分解为 IHttpFilter for use with an HttpClient. I'm using the following code for testing, expecting that it would simply forward all requests to the HttpBaseProtocolFilter:

#include <winrt/Windows.Foundation.h>
#include <winrt/Windows.Web.Http.h>
#include <winrt/Windows.Web.Http.Filters.h>

using namespace winrt;
using namespace Windows::Foundation;
using namespace Windows::Web::Http;
using namespace Windows::Web::Http::Filters;

struct TestHttpFilter : implements<TestHttpFilter, IHttpFilter>
{
    TestHttpFilter(IHttpFilter inner_filter) : inner_filter_{ inner_filter } {}
    IAsyncOperationWithProgress<HttpResponseMessage, HttpProgress> SendRequestAsync(HttpRequestMessage const& request) const
    {
        auto const result{ co_await inner_filter_.SendRequestAsync(request) };
        co_return result;
    }
private:
    IHttpFilter inner_filter_{ nullptr };
};

int main()
{
    init_apartment();

    IHttpFilter const base_filter{ HttpBaseProtocolFilter{} };
    IHttpFilter const test_filter{ TestHttpFilter{ base_filter } };
    HttpClient const http_client{ test_filter };

    auto const result{ http_client.GetStringAsync({ L"http://aka.ms/cppwinrt" }).get() };

    printf("Response: %ls!\n", result.c_str());
}

过滤器链已正确创建,并传递到 HttpClient c'tor。发出 GetStringAsync 调用时,代码在 TestHttpFilter::SendRequestAsync 内崩溃并出现以下错误:

Exception thrown at <address> in <app>.exe: 0xC0000005: Access violation reading location 0x0000000000000000.

这看起来是在 SDK header 文件 (命名空间为简洁起见省略):

template <typename D> IAsyncOperationWithProgress<HttpResponseMessage, HttpProgress> consume_Windows_Web_Http_Filters_IHttpFilter<D>::SendRequestAsync(HttpRequestMessage const& request) const
{
    IAsyncOperationWithProgress<HttpResponseMessage, HttpProgress> operation{ nullptr };
    check_hresult(WINRT_SHIM(IHttpFilter)->SendRequestAsync(get_abi(request), put_abi(operation)));
    return operation;
}

我不明白,我哪里做错了。我希望通过回答以下问题来触及它的核心:

我可以使用官方 Windows SDK 版本 10.0.17134.0.

重现问题

C++/WinRT projection comes with a bit of type duality1: There are implementation types that provide the implementation of a runtime class, as well as projected types 包含 Windows 运行时所需的脚手架,并充当实现类型的代理。

当您编写要由 Windows 运行时 API 使用的运行时 class 时,这种区别变得很重要。有问题的代码实现了一个类型,但未能构造相应的投影类型。错误在以下代码行中:

IHttpFilter const test_filter{ TestHttpFilter{ base_filter } };
HttpClient const http_client{ test_filter };

第一行构造了一个实现类型的实例。第二行将它传递给 HttpClient c'tor,它需要一个 projected type2。解决方法是为 TestHttpFilter 构造投影类型,这可以使用 make 函数模板方便地完成:

IHttpFilter const test_filter{ make<TestHttpFilter>(base_filter) };
HttpClient const http_client{ test_filter };

一般建议:

  • 始终确保您了解您使用的是哪种类型。建立命名约定以区分实现类型和投影类型。
  • 当跨越 ABI 时,(通常)需要传递一个投影类型。您通常会在调用 Windows 运行时 API 或 return 来自实现类型的值时跨越 ABI。
  • 无论你做什么,都不要让它在运行时失败。很可能在这个宇宙中最多只有一个人可以在你到达那里时帮助你。

1 What do the projected type and the implementation type mean?

2 理想情况下,这应该无法编译,尽管我不知道如何在不(过度地)限制通用的多功能性的情况下实现它图书馆.