Serial Ports, CreateFile and SetCommState - windows

I'm using Windows API's CreateFile and SetCommState functions to open a number of serial ports to read and write from, selecting ports using this notation:
\\?\COM1
I've been logging performance closely, and for some odd reason the CreateFile call takes about as much time as the SetCommState calls do (about 4.1 seconds).
I find this very odd and makes me suspect that both CreateFile and SetCommState perform a set of similar tasks with the windows subsystem that handles serial port communication.
Could there be a way to speed up one of both calls, or eliminate one, for example calling CreateFile in such a way it already uses the DCB I've got prepared to call SetCommState with?

The documentation you posted suggests calling GetCommState to initialize the DCB structure. I wonder if the delay is because you're setting something you don't care about? For what it's worth, I've noticed much bigger delays opening Bluetooth virtual COM ports than regular or USB ports.

Related

How to get the status of a serial COM port

What I mean is that, when I code a project, I need to communicate with the serial port like COM1, COM2... but sometimes there is no device connected and I also can use the function CreateFile to get the COM port handle.
When I use the WriteFile function to send a string to the COM port the software blocks.
After I dig into the problem I find another function GetCommModemStatus which can get status of the COM port but when I use the usb-rs232 transition line, the second parameter always returns 0.
How can I get the COM port status so that I can check if is there some devices connected to the computer?
If I understand correctly, you want to detect if a device is connected to your COM port and ready to accept packets. If that's the case, you need to check control signals (DTR/DSR and CTS/RTS) before sending data, assuming your device is aware of them and sets the appropriate PIN on your DB-9 or DB-25 connector. Some devices rely on software handshaking instead (XON/XOFF) and do not set control signals. Your best bet would be to consult documentation of your device.
I have been using ComPort Library version 4.10 by Dejan Crnila. It does support both hardware and software handshakings, so you can focus on your own code instead of reinventing the wheel.
As several people have already pointed out, it is not a good idea to try to "re-invent the wheel." Except for "quick and dirty" testing, your code will have to handle the com port in a separate thread and the available solutions all make this much easier.
BUT, if you Really want to do it, I'll give you some pointers.
If you are using "WriteFile" then you have probably already figured out the "CreateFile" part of the procedure and how complicated things can get depends upon what kind of IO you specified in that procedure, Overlapped or not. Overlapped IO is more complicated but does let the OS handle some of the burden.
You mentioned that your call to "WriteFile" hangs. You should look up the "SetCommTimeOuts" function. Setting both the WriteTotalTimeoutMultiplier and WriteTotalTimeoutConstant members of TCommTimeouts to zero will tell the OS to return immediately. You may also need to "SetCommMask" if your target uses handshaking.
What happens next really depends on what your target is supposed to do. The GetCommMask function can be used to check the status of the handshake lines.

Who enforces dwShareMode on CreateFile? The OS or the Driver?

I have a Windows application that interacts with some hardware. A handle to the hardware is opened with CreateFile, and we control the hardware using DeviceIoControl.
I'm attempting to update an application which uses this hardware to open the hardware in an exclusive mode, so that other programs can't access the hardware at the same time (the hardware has mutable state that I can't have changed out from under me). I do this by passing 0 as the dwShareMode parameter to CreateFile. After making this change, I am still able to run two separate instances of my application. Both calls to CreateFile in both processes are successful. Neither returns INVALID_HANDLE_VALUE.
I believe one of several things is happening, and I'm asking for help narrowing the problem down.
I badly misunderstand the dwShareMode parameter
dwShareMode doesn't have any effect on DeviceIoControl - only ReadFile or WriteFile
The driver itself is somehow responsible for respecting the dwShareMode parameter and our driver is written badly. This, sadly, isn't totally unheard of.
Edit Option 2 is nonsense. dwShareMode should prevent the 2nd CreateFile from happening, DeviceIoControl has nothing to do with it. It must be option #1 or option #3
The Question:
Is the device driver responsible for looking at the dwShareMode parameter, and rejecting requests if someone has already opened a handle without sharing, or is the OS responsible?
If the device driver is responsible, then I'm going to assume #3 is happening. If the OS is responsible, then it must be #1.
Some additional Clues:
IRP_MJ_CREATE documentation suggests that the sharing mode does indeed get passed down to the device driver
I believe that sharing rules are only enforced on some devices. In many (most?) cases enforcing sharing rules on the device object itself (as opposed to on objects within the device namespace) would make no sense.
Therefore, it must be the responsibility of the device driver to enforce these rules in those rare cases where they are required. (Either that or the device driver sets a flag to instruct the operating system to do so, but there doesn't seem to be a flag of this sort.)
In the case of a volume device, for example, you can open the device with a sharing mode of 0 even though the volume is mounted. [The documentation for CreateFile says you must use FILE_SHARE_WRITE but this does not appear to be true.]
In order to gain exclusive access to the volume, you use the FSCTL_LOCK_VOLUME control code.
[That's a file system driver, so it might not be a typical case. But I don't think it makes a difference in this context.]
Serial port and LPT drivers would be an example of a device that should probably enforce sharing rules. I think there may be some applicable sample code, perhaps this would shed light on things?
Edited to add:
I've had a look through the Windows Research Kernel source (this is essentially the same as the Windows Server 2003 kernel) and:
1) The code that opens a device object (by sending IRP_MJ_CREATE to the driver) does not appear to make any attempt to enforce the sharing mode parameter, though it does check access permissions and enforces the Exclusive flag for the driver;
2) I've also searched the code for references to the structure member that holds the requested dwShareMode. As far as I can see it is written into the relevant structure by the internal function that implements CreateFile, and later passed to the device driver, but otherwise ignored.
So, my conclusion remains the same: enforcing the sharing mode rules, or providing an alternative mechanism if appropriate, is the responsibility of the device driver.
(The kernel does, however, provide functions such as IoCheckShareAccess to assist file system drivers in enforcing sharing rules.)
In cases where we open a COM port with :
CreateFile(devname,GENERIC_READ | GENERIC_WRITE,
0,
0,
OPEN_EXISTING,
0,
0);
It doesnt allow another application to open the same COM port until the previous handle is closed. I would suggest walking through serenum.sys to check if it has a role here.

Two-way communication between kernel-mode driver and user-mode application?

I need a two-way communication between a kernel-mode WFP driver and a user-mode application. The driver initiates the communication by passing a URL to the application which then does a categorization of that URL (Entertainment, News, Adult, etc.) and passes that category back to the driver. The driver needs to know the category in the filter function because it may block certain web pages based on that information. I had a thread in the application that was making an I/O request that the driver would complete with the URL and a GUID, and then the application would write the category into the registry under that GUID where the driver would pick it up. Unfortunately, as the driver verifier pointed out, this is unstable because the Zw registry functions have to run at PASSIVE_LEVEL. I was thinking about trying the same thing with mapped memory buffers, but I’m not sure what the interrupt requirements are for that. Also, I thought about lowering the interrupt level before the registry function calls, but I don't know what the side effects of that are.
You just need to have two different kinds of I/O request.
If you're using DeviceIoControl to retrieve the URLs (I think this would be the most suitable method) this is as simple as adding a second I/O control code.
If you're using ReadFile or equivalent, things would normally get a bit messier, but as it happens in this specific case you only have two kinds of operations, one of which is a read (driver->application) and the other of which is a write (application->driver). So you could just use WriteFile to send the reply, including of course the GUID so that the driver can match up your reply to the right query.
Another approach (more similar to your original one) would be to use a shared memory buffer. See this answer for more details. The problem with that idea is that you would either need to use a spinlock (at the cost of system performance and power consumption, and of course not being able to work on a single-core system) or to poll (which is both inefficient and not really suitable for time-sensitive operations).
There is nothing unstable about PASSIVE_LEVEL. Access to registry must be at PASSIVE_LEVEL so it's not possible directly if driver is running at higher IRQL. You can do it by offloading to work item, though. Lowering the IRQL is usually not recommended as it contradicts the OS intentions.
Your protocol indeed sounds somewhat cumbersome and doing a direct app-driver communication is probably preferable. You can find useful information about this here: http://msdn.microsoft.com/en-us/library/windows/hardware/ff554436(v=vs.85).aspx
Since the callouts are at DISPATCH, your processing has to be done either in a worker thread or a DPC, which will allow you to use ZwXXX. You should into inverted callbacks for communication purposes, there's a good document on OSR.
I've just started poking around WFP but it looks like even in the samples that they provide, Microsoft reinject the packets. I haven't looked into it that closely but it seems that they drop the packet and re-inject whenever processed. That would be enough for your use mode engine to make the decision. You should also limit the packet capture to a specific port (80 in your case) so that you don't do extra processing that you don't need.

CloseHandle() returns before the serial port is actually closed

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 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.
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.
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!

How can I send null characters to the serial port using the Windows API?

I am working on a Windows utility program which communicates with some custom hardware using a standard COM port. The communication protocol (which is out of my control) requires me to transmit and receive raw 8-bit data bytes.
I am currently using the following Windows API function to send data to the COM port:
WriteFile(hFile, lpBuffer, numberOfBytesToWrite, ...)
where hFile is a reference to the properly opened COM port, and lpBuffer is an array of bytes that I have stored in memory. The code works perfectly until a null character (ASCII zero) needs to be sent to the device. WriteFile stops sending as soon as it detects the null character because it assumes that it has reached the end of the string. This happens even though I set numberOfBytesToWrite properly.
How can I send raw data to the COM port using the Windows API? I would prefer to use a standard API call similar to WriteFile, but I am open to suggestions.
I am currently using RapidQ to build the utility, but all it does is call the Windows API function directly.
Edit: My setup consists of a Windows PC connected via serial port to the custom hardware module. The module has a small screen on which I am able to view the transmitted characters. I have tested this setup with another third-party utility program. I am able to communicate with the module using this third party program, and null characters show up properly. In my own program, when I use WriteFile, a null character anywhere in the transmit stream stops the rests of the stream from being sent.
I've done Windows serial port programming before, and am sure that I have been able to send null characters through the serial ports (various file transfer protocols wouldn't have worked otherwise). I can think of two possible explanations:
The receiving device is printing the data it received using a method that stops at the first null character. How are you determining that the transmitting device is terminating at the first null? Could the problem be somewhere further down the line?
The serial driver is broken. This is rather unlikely, but is a possible explanation. If you are using some dodgy proprietary third party serial port and its drivers, then they might be suspect. If you are using a standard built-in serial port with the normal Windows drivers, then this is probably not a problem.
I just had a quick look at RapidQ and there's a third possible explanation:
The marshaling that RapidQ may do in the process of calling Win32 API functions may have trouble with embedded null characters in the data to send. I don't know anything about RapidQ, but this is yet another link in the chain that must be considered.
Serial Communications in Win32
Greg is probably correct, but I would also check on your com port settings. Make sure your DCB settings are set properly. Look up SetCommState, and GetCommState for information.
Normally, Serial is 8N1, but your device might be using some other parity, or stop bit configuration, etc.
Like Greg, I've done a lot of work over serial myself, and this can often be the source of problems... Try talking with RS485 and an incorrect DCB structure... heh..
Larry
PS: If you don't already have one, I'd recommend getting yourself a good serial break-out box. I have one somewhere around the house, and it will show you what all the signals are, on all the lines during all of your communications, and is a very powerful debugging tool.
I have used WriteFile in the past and it worked fine with null bytes. I would dig into RapidQ, it could be the problem.
If none of this helps, you can always do what I do. Use Greg's COMM code to test the device with. Remember Qmodem? :) Very nice tool for testing/debugging serial routines. You ccould even use Qmodem and a simple Qmodem script to read the COM port on another box (or another port in the same box), and print out the hex value of every char sent. Oh wait. Qmodem has that built in. :) Use the Debug ASCII emulation and it will show the hex values of every char that comes across the serial port. Then, you could at least verify if your code is working correctly or not.
Now, Question. Does WriteFile return when it hits the NULL, or does it just sit and wait, and never return?
If it never returns, then it may not be your code. WriteFile only returns when it has sent all dwBytesToWrite, or if there is an error. Anything else, and it's TRYING to write, but there is a failure on the other end.
To send a NULL character through serial you can use the setEndOfFile() function, giving the port handler as file.
If you have too many NULL data to send it's probably not ideal, but it's a workaround.
Function details

Resources