【问题标题】:code using boost::asio::overlapped_ptr not working as the equivalent winapi code使用 boost::asio::overlapped_ptr 的代码不能作为等效的 winapi 代码工作
【发布时间】:2020-07-05 11:09:33
【问题描述】:

我正在尝试对 DeviceIOControl 执行异步调用,目的是在设备完成请求后,将调用关联的处理程序。

我有一个使用 winapi 的工作代码,它按预期工作。然而,使用 boost::asio 的等效代码并不相同。 代码执行以下操作:

  • 初始化 IO 完成端口 (CreateIoCompletionPort / boost::asio::io_context)
  • 使用 OVERLAPPED_FLAG 创建设备 HANDLE
  • 将 HANDLE 与具有 IO 完成端口的设备相关联 (CreateIoCompletionPort / boost::asio::detail::io_context_impl::register_handle)
  • 使用 OVERLAPPED 结构执行单个 DeviceIOControl (boost::asio::windows::overlapped_ptr)
  • 等待调用完成 (GetQueuedCompletionStatus / boost::asio::io_context::run)

这是 WinAPI 代码(有效)

auto iocp_handle = ::CreateIoCompletionPort(INVALID_HANDLE_VALUE, 0, 0, 0);
assert(iocp_handle);

auto str = GetDeviceInterfaceInstancesList(&GUID_DEVINTERFACE_cvbkmd);

HANDLE dev_handle = CreateFileW(
    str.c_str(),
    GENERIC_WRITE | GENERIC_READ,
    FILE_SHARE_READ | FILE_SHARE_WRITE,
    NULL,
    OPEN_EXISTING,
    FILE_FLAG_OVERLAPPED,
    nullptr
);

uint64_t completion_key = 3082;
assert(::CreateIoCompletionPort(dev_handle, iocp_handle, completion_key, 0));

std::string writeString = "test write\n";
std::string readString = "test read\n";
unsigned long bytes_returned;

OVERLAPPED overlapped{};
BOOL ok = DeviceIoControl(
    dev_handle,
    123,
    writeString.data(),
    writeString.size(),
    readString.data(),
    readString.size(),
    &bytes_returned,
    &overlapped
);

if (ok)
{
    std::cout << "done\n";
}
else
{
    auto err = GetLastError();
    assert(err == ERROR_IO_PENDING);

    OVERLAPPED* returned_overlapped{ nullptr };
    uint64_t returned_completion_key{};
    unsigned long bytes_transferred;
    BOOL ok = ::GetQueuedCompletionStatus(iocp_handle,
        &bytes_transferred, &returned_completion_key, &returned_overlapped,
        INFINITE);

    assert(ok);

    assert(returned_completion_key = completion_key);
    assert(returned_overlapped = &overlapped);
}

这是 boost::asio 代码(io_context::run 永远挂起,从未调用完成处理程序)

boost::asio::io_context context;


auto str = GetDeviceInterfaceInstancesList(&GUID_DEVINTERFACE_cvbkmd);

HANDLE dev_handle = CreateFileW(
    str.c_str(),
    GENERIC_WRITE | GENERIC_READ,
    FILE_SHARE_READ | FILE_SHARE_WRITE,
    NULL,
    OPEN_EXISTING,
    FILE_FLAG_OVERLAPPED,
    nullptr
);

auto& impl = boost::asio::use_service<boost::asio::detail::io_context_impl>(context);
boost::system::error_code ec;
impl.register_handle(dev_handle, ec);

assert(!ec);

boost::asio::windows::overlapped_ptr overlapped(
    context,
    [&](const boost::system::error_code& ec, std::size_t count)
{
    std::cout << "done"; // never called
}
);



std::string writeString = "test write\n";
std::string readString = "test read\n";
unsigned long bytes_returned;

BOOL ok = DeviceIoControl(
    dev_handle,
    123,
    writeString.data(),
    writeString.size(),
    readString.data(),
    readString.size(),
    &bytes_returned,
    overlapped.get()
);

if (ok)
{
    std::cout << "done\n";
}
else
{
    auto err = GetLastError();
    assert(err == ERROR_IO_PENDING);

    auto completions = context.run(); // hangs forever

    assert(completions == 1);
}

问题

  1. 为什么 2 个代码段的行为不同?
  2. 为什么 boost::asio::io_context::run 挂起并且与overlapped_ptr 关联的完成处理程序从未被调用?

Related Question

【问题讨论】:

    标签: c++ windows winapi boost boost-asio


    【解决方案1】:

    我认为您忘记在overlapped_ptr 对象上调用release。

    您的 DeviceIoControl 代码应如下所示:

    BOOL ok = DeviceIoControl(
        dev_handle,
        123,
        writeString.data(),
        writeString.size(),
        readString.data(),
        readString.size(),
        &bytes_returned,
        overlapped.get()
    );
    
    
    int last_error = GetLastError();
    if (!ok && last_error != ERROR_IO_PENDING)
    {
        // Some error occured, to be consistant post completion notification with the error code
        error_code ec{ last_error, error::get_system_category() };
        overlapped.complete(ec, bytes_returned);
    }
    else
    {
        // Either the operation completed synchronously or it is queued as async. 
        // Either way the IOCP will get a completion packet. 
        // BUT you must call release() to signal that you are done with the overlapped_ptr object
        overlapped.release();
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-02-11
      相关资源
      最近更新 更多