CloseHandle () возвращается до фактического закрытия последовательного порта

Я дергаю за волосы, пытаясь понять, когда закрывается последовательный порт, чтобы я мог снова его открыть. Оказывается, CloseHandle() возвращается до того, как порт действительно разблокируется.

Я открываю последовательный порт с помощью CreateFile(FILE_FLAG_OVERLAPPED), связываю его с CompletionPort с помощью CreateIoCompletionPort(), читаю / записываю в него с помощью ReadFile(), WriteFile() и закрываю с помощью CloseHandle().

Я заметил, что если я закрываю и снова открываю последовательный порт достаточно быстро, я получаю ERROR_ACCESS_DENIED обратно от CreateFile(). Это происходит, несмотря на то, что я жду возврата CloseHandle(), а затем ожидаю, что все невыполненные операции чтения / записи, связанные с этим дескриптором, вернутся из порта завершения. Наверняка есть способ получше :)

Как синхронно закрыть последовательный порт? Не используйте повторные попытки, режим сна () или другие дешевые приемы.

РЕДАКТИРОВАТЬ: возможно, это как-то связано с тем, что я использую порты завершения и FILE_FLAG_OVERLAPPED. Я получаю обратный вызов после завершения операций чтения / записи. Есть ли какой-то обратный вызов на закрытие порта?


person Gili    schedule 16.01.2012    source источник
comment
Возможно, связано с вашим конкретным драйвером последовательного порта. Вы видели это здесь, на SO: stackoverflow.com/questions/2948428/   -  person Simon Mourier    schedule 17.01.2012
comment
На всякий случай .... вы нигде не использовали DuplicateHandle?   -  person Ben Voigt    schedule 17.01.2012
comment
Я не уверен, связан ли порт завершения, я всегда использовал обратные вызовы APC с ReadFileEx и WriteFileEx. Это также проще, потому что APC запускаются только тогда, когда поток переходит в тревожное ожидание, поэтому нет проблем с синхронизацией между потоками (просто будьте осторожны с повторным входом).   -  person Ben Voigt    schedule 17.01.2012
comment
Я подозреваю, что это вызвано неполным запросом ввода-вывода. Но решайте проблему в корне, нет смысла закрывать последовательный порт и снова открывать его.   -  person Hans Passant    schedule 17.01.2012
comment
@BenVoigt: Нет, я нигде не использую DuplicateHandle.   -  person Gili    schedule 17.01.2012
comment
@SimonMourier: Я использую аутентичный компорт, это вряд ли проблема с драйвером.   -  person Gili    schedule 17.01.2012
comment
На самом деле, это не уникально для последовательных портов, и я не думаю, что вы что-то делаете неправильно. Я видел ту же проблему с дескрипторами файлов. Между моментом возврата CloseHandle и фактическим освобождением объекта существует значительная задержка. Я подозреваю, что в конце концов вы обнаружите, что единственным решением будет какой-нибудь дешевый прием, включающий цикл повтора и сон.   -  person Carey Gregory    schedule 17.01.2012


Ответы (3)


Я считаю, что проблема в драйвере, обслуживающем COM-порт. Следовательно - не будет API для «фактического закрытия» COM-порта.

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

В частности, драйверы FTDI (те, которые имитируют COM-> USB), как известно, очень глючны.

Я могу только порекомендовать попробовать сбросить данные перед закрытием дескриптора. Вы можете дождаться завершения всех операций ввода-вывода перед закрытием COM-порта (если это применимо в вашем случае). В качестве альтернативы вы можете вызвать SetCommMask и WaitCommEvent, чтобы убедиться, что нет ожидающих отправки данных. Надеюсь, это поможет.

РЕДАКТИРОВАТЬ:

CloseHandle немедленно (перед возвратом) отменяет все ожидающие операции ввода-вывода для дескриптора файла?

Строго говоря - нет.

Могут остаться и другие ссылки на файловый объект. Например, код пользовательского режима может вызывать DuplicateHandle или драйвер режима ядра может вызывать ObReferenceObjectByXXXX. В таком случае объект, на который ссылается дескриптор, не обязательно освобождается.

Когда закрывается последний дескриптор, вызывается DispatchCleanup драйвера. Он должен отменить все невыполненные операции ввода-вывода в соответствии с это.

Однако пока один поток находится внутри CloseHandle - теоретически вы можете выполнить другой ввод-вывод из другого потока (если вам повезет - дескриптор все еще будет действителен). Пока вы вызываете функцию ввода-вывода (например, WriteFile и т. Д.), ОС временно увеличивает счетчик ссылок на объект. Это, в свою очередь, может запустить другой ввод-вывод, который не будет отменен непосредственно вызовом CloseHandle.

Однако в этом случае дескриптор будет закрыт оператором ввода-вывода сразу после того, как будет выпущен новый ввод-вывод, потому что счетчик ссылок на объект снова достиг 0.

Так что в таком извращенном сценарии может возникнуть ситуация, когда сразу после вызова CloseHandle вы не сможете снова открыть файл.

person valdo    schedule 17.01.2012
comment
Я использую последовательный порт на материнской плате (используя встроенные драйверы Microsoft). В игре нет адаптера COM-USB. Проблема с очисткой данных заключается в том, что они могут заблокироваться навсегда, если включено аппаратное управление потоком. В идеале я бы хотел прервать все невыполненные команды, закрыть порт и вернуться из моей функции, как только закрытие действительно будет выполнено. Это все спорный вопрос, если нет API для обеспечения закрытия. PS: Откуда вы знаете, что CloseHandle() отменяет все невыполненные операции ввода-вывода перед возвратом? В спецификации не вижу такой гарантии. - person Gili; 17.01.2012
comment
Пожалуйста, посмотрите мою правку. Вы также можете попробовать использовать CancelIoEx. И, конечно же, не выполняйте никаких операций ввода-вывода в другом потоке (надеюсь, вы все равно этого не делаете) - person valdo; 18.01.2012
comment
Хорошо, я закончил тем, что использовал цикл повтора при попытке открыть порт. Спасибо за справочную информацию. - person Gili; 20.01.2012

Перед повторной попыткой прикоснуться к устройству убедитесь, что ваш читатель и писатель не работают (с недопустимым дескриптором) после того, как вы отправили запрос CloseHandle () (предположительно в каком-то другом потоке). Вы можете использовать это как сигнал, чтобы очистить всю вашу обработку io перед попыткой создать другой CreateFile. Я должен сказать, что порты завершения кажутся излишне причудливыми.

person jolyon    schedule 20.09.2012
comment
Хороший трюк, но ненадежный. Потому что один и тот же дескриптор может (теоретически) использоваться повторно. - person valdo; 20.09.2012
comment
Кроме того, у вас может не быть невыполненных операций чтения / записи, поэтому теперь вам нужно отслеживать и это. - person Gili; 21.09.2012

Когда вы открываете COM-порт в потоке, операционная система открывает другой скрытый поток для выполнения ввода-вывода. Я также пытаюсь решить проблему закрытия порта. Насколько я могу судить, один метод, который может работать, это: 1) Приостановить поток, открывший COM-порт, 2) PurgeComm (), чтобы остановить все операции ввода-вывода и очистить все буферы ввода-вывода для чтения и записи, и 3) затем попытайтесь закрыть COM-порт. Может быть задействован объект waitforsingle, чтобы определить, когда поток с COM-портом фактически приостанавливается. Если не требуется закрытие COM-порта, я рассматриваю возможность оставить COM-порт открытым и позволить WinProc обработать команду IDM_Exit, чтобы закрыть COM-порт, потоки и приложение. Не то, что я хотел, а вариант, с которым я мог бы жить. Я использую соединение USB с последовательным портом и не знаю, какие проблемы это вызывает у меня. Я знаю, что если вы используете интерфейс USB для последовательного порта, вам нужно установить высокий контакт 20 (DTR). Контакт 20 помогает питать интерфейс, но обеспечивает только около 30 мА или около того. Если все это сработает, я обновлю этот пост и дам вам знать, как я смог закрыть COM-порт. Windows испортила драйвер последовательного порта и, похоже, довольна тем, что оставляет все в беспорядке!

person Chuck L.    schedule 06.07.2016