Можно ли изменить ДЕСКРИПТОР, который был открыт для синхронного ввода-вывода, на открытый для асинхронного ввода-вывода в течение его срока службы?

StackOverflow https://stackoverflow.com/questions/2475713

Вопрос

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

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

Итак, сначала я хотел бы спросить:

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

HANDLE h;

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

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

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

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

Я не нашел никакого прямого способа после прочтения MSDN и долгих поисков в Google.Есть ли хоть какой-нибудь трюк, который мог бы сделать то же самое?Например, воссоздать дескриптор таким же образом, используя CreateFile() функция или что-то подобное?Что-то хотя бы частично задокументированное или не задокументированное вообще?

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

Уважаемые гуру Windows:помогите, пожалуйста!

С уважением

Мартин

Это было полезно?

Решение 2

прошло 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() еще не вызвана, то операция отмены не выполняется, и последующее чтение будет зависать.Единственным способом было бы закрыть дескриптор, но эта операция зависает или завершается с ошибкой на некоторых объектах и в некоторых операционных системах.Единственным правильным решением этой проблемы является асинхронный ввод-вывод.Но, как я упоминал в начале, наше приложение запускается сторонними приложениями, и мы не можем заставить их всех всегда создавать именованные каналы с перекрывающимся флагом, установленным для стандартного интерфейса.

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

Другие советы

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

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

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

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

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

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

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

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

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

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

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

Если вы хотите синхронного поведения (я имею в виду использование синхронного API для неперекрывающихся дескрипторов и асинхронного API, подаваемого со структурой OVERLAPPED с последующим ожиданием перекрывающегося события) вы всегда можете использовать async API, если дескриптор был открыт в режиме без перекрытия. как уже сказал @Brett
Я могу подтвердить, что это работает (ПО МИНИМУМ для именованных каналов):

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 является создание нулевого байта ReadFile с NULL lpOverlapped.Если произойдет сбой с ERROR_INVALID_PARAMETER, предположим, что он был открыт с FILE_FLAG_OVERLAPPED.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top