Можно ли изменить HANDLE, который был открыт для синхронного ввода-вывода, чтобы он был открыт для асинхронного ввода-вывода в течение срока его службы? - PullRequest
9 голосов
/ 19 марта 2010

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

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

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

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

HANDLE h;

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

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

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

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

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

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

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

С уважением

Martin

Ответы [ 6 ]

5 голосов
/ 20 марта 2010

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

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

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

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

3 голосов
/ 10 мая 2013

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

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

ReOpenFile () API используется для «получения существующего дескриптора файла и получения другого дескриптора с другим набором прав доступа». По крайней мере, это указано в оригинальной статье .

Я пытался использовать 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.

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

2 голосов
/ 02 ноября 2010

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

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

1 голос
/ 29 января 2013

Я не знаю, как определить флаг дескриптора и побочные эффекты использования 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
    }*/
}
1 голос
/ 19 марта 2010

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

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

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

0 голосов
/ 21 февраля 2017

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

...