CloseHandle() возвращает до того, как последовательный порт будет фактически закрыт
Я дергаю себя за волосы, пытаясь понять, когда завершается закрытие последовательного порта, чтобы я мог открыть его снова. Оказывается, что CloseHandle()
возвращается до того, как порт фактически разблокирован.
Я открываю последовательный порт используя CreateFile(FILE_FLAG_OVERLAPPED)
, связав его с CompletionPort используя CreateIoCompletionPort()
, чтение / запись с помощью ReadFile()
, WriteFile()
и закрывая его, используя CloseHandle()
,
Я заметил, что если я закрою и снова открою последовательный порт достаточно быстро, я получу ERROR_ACCESS_DENIED
обратно из CreateFile()
, Это происходит несмотря на то, что я жду CloseHandle()
для возврата, затем ожидая возврата всех ожидающих операций чтения / записи, связанных с этим дескриптором, из порта завершения. Конечно, есть лучший способ:)
Как закрыть последовательный порт синхронно? Пожалуйста, не повторяйте петли, сон () или другие дешевые хаки.
РЕДАКТИРОВАТЬ: Может быть, это связано с моим использованием портов завершения и FILE_FLAG_OVERLAPPED. Я получаю обратный вызов, когда операции чтения / записи завершены. Есть ли какой-то обратный вызов для закрытия порта?
3 ответа
Я считаю, что проблема с драйвером, который обслуживает COM-порт. Следовательно - не будет никакого API, чтобы "фактически закрыть" COM-порт.
Кстати, после закрытия дескриптора файла не нужно ждать завершения всех выдающихся операций ввода-вывода с ошибками. В это время CloseHandle
возвращает все невыполненные операции ввода / вывода уже завершены / отменены, вы просто получаете обратные вызовы асинхронно (будь то через порт завершения или очередь APC, неважно).
В частности, драйверы FTDI (те, которые эмулируют COM->USB), как известно, очень сложны.
Я могу только рекомендовать попробовать очистить данные перед закрытием ручки. Вы можете дождаться завершения всех операций ввода-вывода, прежде чем закрывать COM-порт (если это применимо для вашего случая). Или вы можете позвонить SetCommMask
а также WaitCommEvent
чтобы не было ожидающих отправки данных. Надеюсь, это может помочь.
РЕДАКТИРОВАТЬ:
Есть ли CloseHandle
немедленно (до его возвращения) отменить все ожидающие операции ввода-вывода для дескриптора файла?
Строго говоря - нет.
Могут быть другие ссылки, оставленные на объект файла. Например, код режима пользователя может вызвать DuplicateHandle
или драйвер режима ядра может вызвать ObReferenceObjectByXXXX
, В таком случае объект, на который ссылается дескриптор, не обязательно освобождается.
Когда последняя ручка закрыта DispatchCleanup
называется. В соответствии с этим он должен отменить все выдающиеся операции ввода-вывода.
Однако, хотя один поток находится в пределах CloseHandle
- теоретически вы можете выполнить другой ввод / вывод из другого потока (если вам повезет - дескриптор все равно будет действителен). Пока вы находитесь в вызове функции ввода / вывода (например, WriteFile
или т. д.) ОС временно увеличивает счетчик ссылок на объект. Это, в свою очередь, может запустить другой ввод / вывод, который не будет отменен напрямую CloseHandle
призывание.
Однако в этом случае дескриптор будет закрыт O / S сразу же после выдачи нового I/O, поскольку счетчик ссылок на объект снова достиг 0.
Так что в таком извращенном сценарии может возникнуть ситуация, когда сразу после звонка CloseHandle
Вы не можете снова открыть файл.
Убедитесь, что ваше устройство чтения и записи завершилось неудачно (с неверным дескриптором) после того, как вы выполнили запрос CloseHandle() (предположительно в каком-то другом потоке), прежде чем пытаться снова коснуться устройства. Вы можете использовать это как подсказку, чтобы очистить всю вашу обработку ввода-вывода перед попыткой выпустить другой CreateFile. Я должен сказать, что порты завершения кажутся излишне барочными.
Когда вы открываете COM-порт в потоке, операционная система открывает другой скрытый поток для выполнения операций ввода-вывода. Я также пытаюсь решить проблему закрытия порта. Насколько я могу сказать, один метод, который может работать: 1) приостановить поток, который открыл COM-порт, 2) PurgeComm(), чтобы остановить все операции ввода-вывода и очистить все буферы ввода / вывода для чтения / записи, и 3) затем попытаться закрыть COM-порт. Может быть задействован объект waitforsingle, чтобы определить, когда поток с COM-портом фактически приостанавливается. Если COM-порт не нужно обязательно закрывать, я рассматриваю возможность оставить COM-порт открытым и позволить WinProc обработать команду IDM_Exit для закрытия COM-порта, потоков и приложения. Не то, что я хотел, а вариант, с которым я мог бы жить. Я использую соединение USB с последовательным портом и не уверен, какие проблемы это вызывает у меня. Я знаю, что если вы используете интерфейс USB к последовательному порту, вам нужно установить высокий уровень контакта 20 (DTR). Контакт 20 помогает питать интерфейс, но обеспечивает только около 30 мА или около того. Если все это работает, я сделаю обновление к этому посту и сообщу, как, если я смог закрыть COM-порт. Windows запуталась в драйвере последовательного порта и, кажется, довольна, чтобы оставить вещи в беспорядке!