код, использующий boost :: asio :: overlapped_ptr, не работает как эквивалентный код winapi - PullRequest
1 голос
/ 25 марта 2020

Я пытаюсь выполнить асинхронный вызов DeviceIOControl с намерением, что после того, как устройство выполнит запрос, будет вызван связанный обработчик.

У меня есть рабочий код, использующий winapi, который работает, как и ожидалось. Однако эквивалентный код, использующий boost :: asio, не работает так же. Код выполняет следующие действия:

  • Инициализация порта завершения ввода-вывода (CreateIoCompletionPort / boost :: asio :: io_context)
  • Создание РУЧКИ устройства с OVERLAPPED_FLAG
  • Свяжите HANDLE с устройством с портом завершения ввода-вывода (CreateIoCompletionPort / boost :: asio :: detail :: io_context_impl :: register_handle)
  • Выполните один DeviceIOControl со структурой OVERLAPPED (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, никогда не вызывается?

Смежный вопрос * 10 33 *

...