Можно ли изменить дескриптор, который был открыт для синхронного ввода-вывода, чтобы быть открытым для асинхронного ввода-вывода в течение его жизни?
большая часть моей ежедневной работы по программированию в 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.