I'm pulling my hair trying to figure out when a serial port finishes closing so I can reopen it. It turns out that CloseHandle()
returns before the port is actually unlocked.
I am opening a serial port using CreateFile(FILE_FLAG_OVERLAPPED)
, associating it with a CompletionPort using CreateIoCompletionPort()
, reading/writing to it using ReadFile()
, WriteFile()
and closing it using CloseHandle()
.
I've noticed that if I close and reopen a serial port quickly enough, I get an ERROR_ACCESS_DENIED
back from CreateFile()
. This is happening in spite of the fact that I'm waiting for CloseHandle()
to return, then waiting for all outstanding read/write operations associated with that handle to come back from the completion port. Surely there is a better way :)
How do I close a serial port synchronously? Please no retry loops, sleep() or some other cheap hacks.
EDIT: Maybe this has something to do with my use of completion ports and FILE_FLAG_OVERLAPPED. I get a callback when the read/write operations complete. Is there some sort of callback for the port closing?
I believe the problem is with the driver that serves the COM port. Hence - there'll be no API to "actually close" the COM port.
BTW, after you close the file handle, there's no need to wait for all oustanding I/Os to complete with errors. At the time
CloseHandle
returns all outstanding I/Os are already completed/cancelled, you just receive the callbacks asynchronously (be it via completion port or APC queue, no matter).Specifically FTDI drivers (those that emulate COM->USB) are known to be very glitchy.
I may only recommend to try flushing the data before closing the handle. You may wait for all I/Os to complete before closing the COM port (if this is applicable for your case). Alternatively you may call
SetCommMask
andWaitCommEvent
to ensure there's no send data pending. Hopefully this may help.EDIT:
Does
CloseHandle
immediately (before it returns) cancel all the pending I/Os on the file handle?Strictly speaking - no.
There may be other references left to the file object. For example, a user-mode code may call
DuplicateHandle
, or a kernel-mode driver may callObReferenceObjectByXXXX
. In such a case the object the handle refers to is not necessarily released.When the last handle is closed the driver's
DispatchCleanup
is called. It must cancel all the outstanding I/Os according to this.However while one thread is within a
CloseHandle
- you may theoretically issue another I/O from another thread (if you're lucky - the handle will still be valid). While you're in the call to an I/O function (such asWriteFile
or etc.) the OS temporarily increases the reference counter to the object. This in turn may start another I/O, which won't be cancelled directly by theCloseHandle
invocation.However in this case the handle will be closed by the O/S immediately after the new I/O is issued, because the reference count to the object reached 0 again.
So that in such a perverted scenario there may happen a situation where immediately after a call to
CloseHandle
you're unable to reopen the file again.