【问题标题】:IHttpFilter implementation causes access violation for HttpClient::GetStringAsync callIHttpFilter 实现导致 HttpClient::GetStringAsync 调用的访问冲突
【发布时间】:2018-11-02 05:57:50
【问题描述】:

我正在将 OAuth 身份验证分解为 IHttpFilter,以便与 HttpClient 一起使用。我正在使用以下代码进行测试,期望它会将所有请求简单地转发到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 头文件 内的 put_abi 调用中取消引用的 NULL 指针(为简洁起见,省略了命名空间):

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

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

  • TestHttpFilter 实现有什么问题吗?
  • HTTP 过滤器能否与HttpClient 的便捷实现一起使用(例如GetStringAsync),或者我们在使用过滤器时是否需要通过SendRequestAsync 接口?
  • 极不可能,但这可能是生成的 SDK 标头的问题,为本地 operation 对象选择了不合适的 c'tor?

我可以使用官方 Windows SDK 版本 10.0.17134.0 重现该问题。

【问题讨论】:

  • 您需要使用 make() 函数模板来确保您的实现(例如 TestHttpFilter)是堆分配的。 make 函数模板将返回投影类型。
  • @KennyKerr:已经解决了,谢谢!不过,我仍然不完全确定,为什么。据推测,分配存储(堆与堆栈)只是使用make&lt;&gt;() 模板的副作用,而不是实际修复的一部分。毕竟,示例代码中的对象生命周期就足够了。我想这是事实,make&lt;&gt;() 从实现类型返回投影类型,WinRT 类型正在使用该类型(在本例中为HttpClient)。你能否为我澄清一下,以防你自己没有时间回答,所以我可以结束这个问答?
  • 实现必须在堆上分配。它比这更微妙(正如您所猜测的那样),但这是无需任何技巧即可获得正确实现的简单且安全的方法。我在这里youtu.be/nOFNc2uTmGs?t=1h3m29skennykerr.ca/2016/11/09/cppwinrt-working-with-implementations 在这里简要讨论这个问题。各种辅助函数也开始记录在这里 docs.microsoft.com/en-us/uwp/cpp-ref-for-winrt/winrt 我计划在编译时让这个错误变得更难一些,但我想小心不要限制你的选择。
  • @KennyKerr:这很有帮助,谢谢。在历史的短暂时刻,我很肯定地掌握了实现类型和投影类型之间的关系。只是发现自己想知道,为什么核心应用程序模板调用CoreApplication::Run(App())。根据我所学到的,我本来希望看到CoreApplication::Run(make&lt;App&gt;())。无论如何,我整理了这个问题的答案,希望它至少是大致正确的。不过,我们将不胜感激。
  • 那些样本不正确。 :) 由于一生的巧合,它们恰好可以工作,但它们确实也应该使用 make() 。之所以以这种方式编写这些示例,是因为我很久以前就在它被称为 C++/WinRT 之前编写了它们。投影的最早版本试图使实现类看起来也是堆栈分配的,但效率不高。

标签: uwp windows-runtime c++-winrt


【解决方案1】:

C++/WinRT 投影带有一点对偶类型1implementation types 提供运行时类的实现,projected types 包含所需的脚手架Windows 运行时,并充当实现类型的代理。

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

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

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

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

一般建议:

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

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-05
    • 1970-01-01
    • 1970-01-01
    • 2020-10-12
    • 2010-11-19
    相关资源
    最近更新 更多