CloseHandle() returns before the serial port is actually closed

6.2k views Asked by At

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?

3

There are 3 answers

3
valdo On BEST ANSWER

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 and WaitCommEvent 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 call ObReferenceObjectByXXXX. 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 as WriteFile 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 the CloseHandle 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.

2
AudioBubble On

Ensure that your reader and writer fail (with invalid handle) after you have issued the CloseHandle() request (presumably on some other thread) before attempting to touch the device again. You can use this as a cue to clean up all your io handling before attempting to issue another CreateFile. I must say completion ports do seem needlessly baroque.

0
Chuck L. On

When you open a COM Port in a thread, the operating system opens another hidden thread to do the I/O. I am trying to resolve closing a port as well. Near as I can tell one method which may work is: 1) Suspend the thread which opened the COM Port, 2) PurgeComm() to stop all I/O and empty all read/write I/O buffers, and 3) then attempt to close the COM Port. There may be a waitforsingleobject involved to determine when the thread with the COM Port actually suspends. Unless the COM Port absolutely needs to be closed, I am considering leaving the COM Port open and letting the WinProc process the IDM_Exit command to close the COM Port, threads, and the application. Not what I wanted but an option I could live with. I am using a USB to Serial Port connection and not sure what problems this is causing me. I know if you are using an USB to Serial Port interface you need to set pin 20 high (DTR). Pin 20 helps power the interface, but is only providing around 30ma or so. If all this works I will make an update to this post and let you know how if I was able to close the COM Port. Windows has made a mess of the Serial Port driver and seem to be content to leave things in disarray!