Можно ли изменить дескриптор, который был открыт для синхронного ввода-вывода, чтобы быть открытым для асинхронного ввода-вывода в течение его жизни?

большая часть моей ежедневной работы по программированию в Windows в настоящее время вокруг операций ввода-вывода всех видов (трубы, консоли, файлы, сокеты, ...). Я хорошо знаю различные методы чтения и записи из/в различные типы дескрипторов (Синхронное, асинхронное ожидание завершения событий, ожидание дескрипторов файлов, портов завершения ввода-вывода и предупреждаемый ввод-вывод). Мы используем многие из них.

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

поэтому сначала я бы спросил:

предположим, у меня есть ручка:

HANDLE h;

который был получен моим процессом для ввода-вывода откуда-то. Есть ли простой и надежный способ узнать, с какими флагами он был создан? Главный флаг в вопросе FILE_FLAG_OVERLAPPED.

единственный способ, известный мне до сих пор, чтобы попытаться зарегистрировать такой дескриптор в порт завершения ввода-вывода (используя CreateIoCompletionPort()). Если это удастся, дескриптор был создан с FILE_FLAG_OVERLAPPED. Но тогда должен использоваться только порт завершения ввода-вывода, так как дескриптор не может быть незарегистрирован из него без закрытия HANDLE .

при условии, что есть простой способ определить наличие FILE_FLAG_OVERLAPPED, придет мой второй вопрос:

есть ли способ добавить такой флаг к уже существующему дескриптору? Что сделает дескриптор, который был первоначально открыт для синхронных операций, открытым для асинхронных. Был бы способ, как создать противоположное (удалить FILE_FLAG_OVERLAPPED создать синхронно ручки от асинхронного)?

Я не нашел никакого прямого способа после чтения через MSDN и googling много. Может, найдется какой-нибудь трюк, который сделает то же самое? Как воссоздать дескриптор таким же образом, используя

6 ответов


Я вижу, что я был плохим читателем MSDN: / я полностью пропустил функцию ReOpenFile() который был представлен, вероятно, еще в июне 2003 года в Windows Server 2003 (согласно в этой статье). Чтобы хоть немного защититься: я бы ожидал CreateFile() описание ссылка ReOpenFile() описание. Есть ссылка на ReOpenFile() страница CreateFile() страница, но не наоборот.

эта функция, похоже, включает именно то, что я нужно: добавление или удаление FILE_FLAG_OVELRAPPED в / из уже существующих дескрипторов путем создания нового дескриптора с желаемыми свойствами! :- D я еще не тестировал его, Хотя. Это, к сожалению, доступен только в Windows Server 2003 и Windows Vista и далее. На вопрос о предыдущих версиях ОС был дан ответ здесь. Функция не существует в общедоступном API в ОС до Windows 2003 Server. Он используется базовой реализацией, но он недоступен разработчикам на тех системы (не поддерживаются).

это практически означает, что для меня нет надежды, по крайней мере, в течение следующих нескольких лет, пока мы не прекратим поддержку старых платформ Windows. Это также означает, что ситуация с вводом-выводом была очень плохой на ОС старше Windows Vista. Другой болезненной частью, которая вообще отсутствовала, была возможность отменить синхронный и асинхронный ввод-вывод в этих старых системах.

кроме того, я все еще пропускаю одну часть ответа: может ли наличие флагов проверяется любыми средствами? Я не нашел функции для этого. Это означает, что если мы хотим гарантировать наличие какого-либо флага в объекте file, то файл всегда должен быть повторно открыт.


если я понимаю, что вам нужно, я хотел бы предложить, что вам все равно, был ли открыт с перекрытым флагом или нет. Я считаю, что вы можете безопасно пройти в OVERLAPPED структура как в синхронном, так и в асинхронном случаях. Ваш код должен иметь возможность обрабатывать ReadFile() возвращение false и GetLastError() возвращение ERROR_IO_PENDING. Вам также необходимо добавить соответствующие вызовы в GetOverlappedResult(), WaitForSingleObject(), etc.

статья MSDN на ReadFile() имеет некоторую хорошую информацию об этом под "Рекомендации по работе с синхронными дескрипторами файлов "и" рекомендации по работе с асинхронными дескрипторами файлов "в разделе" синхронизация и положение файла".


прошло 3 года, и Windows 8 была выпущена. Благодаря регрессии, введенной в реализацию консоли в Windows 8, я должен что-то сделать с проблемой, которая вызвала этот вопрос. Поэтому я, наконец, попытался использовать вызов функции ReOpenFile ().

в одном предложении: для моих целей бесполезно.

API ReOpenFile () используется для "взятия существующего дескриптора файла и получения другого дескриптора с другим набором прав доступа". Хотя бы те указано в Оригинал статьи.

Я попытался использовать ReOpenFile () на ручке ввода консоли:

  stdin_in = GetStdHandle(STD_INPUT_HANDLE);
  stdin_in_operlapped = ReOpenFile(stdin_in, GENERIC_READ | GENERIC_WRITE,
                                   FILE_SHARE_READ, FILE_FLAG_OVERLAPPED);
  if (stdin_in_operlapped ==  INVALID_HANDLE_VALUE)
    {
      my_debug("failed to ReOpen stdin handle with OVERLAPPED flag: %d", GetLastError());
      exit(1);
    }

и я получаю: ошибка 1168: "элемент не найден". "Спасибо Microsoft". Я даже не буду пытаться использовать его для анонимных труб, так как документация гласит:

" асинхронные (перекрывающиеся) операции чтения и записи не поддерживаются анонимными каналами. Это означает, что вы не можете использовать функции ReadFileEx и WriteFileEx с анонимные каналы. Кроме того, параметр Lpoverlapped ReadFile и WriteFile игнорируется при использовании этих функций с анонимными каналами."

Спасибо, люди, все за ваши предложения. При асинхронном считывании с дескриптора необходимо быть готовым и к тому, что операция может завершиться синхронно. Это я знаю. Основная причина, по которой я задал этот вопрос:

когда Синхронное чтение было выдано для некоторых объектов (по крайней мере анонимных каналов и консольный ввод в Windows 8) затем вызов CloseHandle() из другого потока на том же дескрипторе либо завершится ошибкой, либо зависнет, пока ReadFile () не завершится; это означает, что во многих случаях он будет висеть бесконечно. Вот почему я хотел заменить синхронный дескриптор на асинхронный.

теперь мне ясно, что в операционных системах Windows просто невозможно отменить некоторые операции чтения простым способом. При чтении из синхронных дескрипторов просто необходимо выйдите из приложения, даже если ReadFile() все еще читает из дескриптора в каком-то потоке, потому что просто невозможно надежно разбудить такую операцию чтения. В курсе... В новой ОС можно отменить эту операцию. Однако нет способа узнать погоду, когда поток находится в вызове ReadFile() уже или еще нет. Если ReadFile () еще не вызван, то нет операции для отмены, и последующее чтение будет зависать. Единственный способ-закрыть ручку, но эта операция зависает, или сбой на некоторых объектах и в некоторых операционных системах. Единственным правильным решением является асинхронный ввод-вывод, но, как я уже упоминал в начале, наше приложение запускается сторонними приложениями, и мы не можем заставить их всегда создавать именованные каналы с перекрывающимся флагом для stdio.

Я сдаюсь и собираюсь реализовать неприятные уродливые хаки... нам придется продолжать чтение без ПЕРЕКРЫВАЮЩЕЙСЯ структуры из дескрипторов, которые были созданы с перекрывающимся флагом, и протекающих дескрипторов и нити....


флаги дескриптора тестирования, вероятно, должны выполняться так же, как и тестирование разрешений, с которыми был создан дескриптор. Попробовать его. Если API не удается, попробуйте запасной вариант. Если это не удается, возвращается ошибка.

Я думаю, что действительно говорит то, как документация для ReadFile говорит: "Если hFile открыт с FILE_FLAG_OVERLAPPED,... функция может неправильно сообщить, что операция чтения завершена."

моя интерпретация ошибки, (и вопрос, который вам нужен чтобы спросить себя, есть): если можно было проверить перекрывающееся состояние дескриптора файла, почему ReadFile не сделает эту проверку, а затем не проверит перекрывающуюся структуру соответственно, чтобы явно потерпеть неудачу, если вызывается не перекрывающимся способом с перекрывающимся дескриптором?


Я не знаю, как определить флаг дескриптора и побочные эффекты использования ReOpen api, но так как ваша цель была

было бы очень полезно иметь только один способ обращаться со всеми ручками

если вы желаете синхронного поведения (Я имею в виду использование синхронного api для неперекрывающихся дескрипторов и асинхронного api, подаваемого с ПЕРЕКРЫВАЮЩЕЙСЯ структурой с последующим ожиданием события overlapped) вы также всегда можете использовать асинхронный api если дескриптор был открыт в режиме без перекрытия как уже заявил @Brett
Я могу подтвердить, что это работает (по крайней мере, для именованных каналов) es:

void connectSynchronous(HANDLE hPipeThatWeDontKnowItsFlag){
    ...
    BOOL bRet = ::ConnectNamedPipe(hPipeThatWeDontKnowItsFlag, pOverlapped);

    if(bRet == FALSE){
        DWORD dwLastErr = ::GetLastError();

        if(dwLastErr == ERROR_IO_PENDING){
            //The handle was opened for asynchronous IO so we have to wait for the operation
            ...waitFor on the overlapped hEvent;

        }else if(dwLastErr == ERROR_PIPE_CONNECTED){
            //The handle was opened for synchronous IO and the client was already connected before this call: that's OK!
            return;
        }else{
            throw Error(dwLastErr);
        }
    }/*else{
        //The handle was opened for synchronous IO and the client has connected: all OK
    }*/
}

альтернативой взлому CreateIoCompletionPort является выполнение нулевого байтового файла чтения с нулевым lpOverlapped. Если это не удается с ERROR_INVALID_PARAMETER предположим, что он был открыт с FILE_FLAG_OVERLAPPED.