COMMTIMEOUT and Thread not timming out on serial port Read function - c++

I am calling a function that operates an I/o board through a serial port to check that it is communicating in an instance of my main class.
I know that this is risky but unfortunately this is an old section of code that has been used for a while so I am unable to alter the functionality while I have been asked to improve it.
If there is no communication issue the application will start up, use the function and continue with no issue.
The problem arises when there is a communication fault with the I/o board, I have found that the read function is hanging and stopping the app from starting for the majority of the time. On occasion the app will load and will report that there is a communication fault.
What I am trying to achieve is for the application to load successfully every time when there is a communication fault.
The comport is set up with COMMTIMEOUTs originally which I expected would timeout the port when there has been nothing to read. I have attempted to alter the timings but with no avail.
I have also attempted to use a thread for the read function so that it would not block the start up but still it hangs.
Currently the port is set up synchronously.
Has anybody got any suggestions? I can put some code examples up if required.
Main.cpp
extern COMPort comPort;
BOOL Main::InitInstance()
{
int i = comPort.DoorLatchOff();
If(i<0) printf("Error checksum. COM port?\n");
else printf("checksum ok.\n");
}
COMPort.h
class CCOMPort
{
public:
CCOMPort (COM_PORT port = NULL_COM, DCB * state = NULL);
BOOL SetPortNumber (COM_PORT port = NULL_COM, DCB * state = NULL);
void Read(BYTE* buff, int count);
int DoorLatchOff(void);
protected:
HANDLE m_hPort;
};
static HANDLE m_hPortThreaded;
typedef struct readParam{BYTE* readBuff;int readCount;}RP, *PRP;
DWORD WINAPI ThreadedRead( LPVOID lpParam );
COMPort.cpp
CCOMPort::CCOMPort (COM_PORT port, DCB * state) : m_portNum (port), m_hPort(INVALID_HANDLE_VALUE)
{
SetPortNumber (port, state);
}
BOOL CCOMPort::SetPortNumber (COM_PORT port, DCB * state)
{
if (m_hPort != INVALID_HANDLE_VALUE){
::CloseHandle (m_hPort);
m_hPort = INVALID_HANDLE_VALUE;
}
m_portNum = port;
m_currState = m_defState;
m_originState = m_defState;
if (m_portNum != NULL_COM){
stringstream ssPortName;
ssPortName << "COM" << (m_portNum + 1) << ":" << flush;
m_hPort = ::CreateFile (ssPortName.str().c_str(),
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_WRITE_THROUGH,
NULL);
if (m_hPort == INVALID_HANDLE_VALUE)
return FALSE;
else
{
GetState (& m_originState);
if (state)
m_currState = * state;
SetState (& m_currState);
GetCommTimeouts(m_hPort, &timeouts);
timeouts.ReadIntervalTimeout = 75; //15
timeouts.ReadTotalTimeoutMultiplier = 5; //1
timeouts.ReadTotalTimeoutConstant = 1250; //250
timeouts.WriteTotalTimeoutMultiplier = 5; //1
timeouts.WriteTotalTimeoutConstant = 1250; //250
SetCommTimeouts(m_hPort, &timeouts);
FlushOutput ();
FlushInput ();
PurgeOutput ();
PurgeInput ();
}
}
return TRUE;
}
void CCOMPort::Read(BYTE* buff, int count)
{
PRP pReadArray[1];
DWORD dwThreadArray[1];
HANDLE hThreadArray[1];
m_hPortThreaded = m_hPort;
pReadArray[0] = (PRP) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(RP));
if(pReadArray[0] == NULL){
ExitProcess(2);
}
pReadArray[0]->readBuff = buff;
pReadArray[0]->readCount = count;
hThreadArray[0] = CreateThread(NULL,
0,
ThreadedRead,
pReadArray[0],
0,
&dwThreadArray[0]);
if(hThreadArray[0] == NULL){
ExitProcess(3);
}
WaitForSingleObject(hThreadArray[0],500/*INFINITE*/);
CloseHandle(hThreadArray[0]);
if(pReadArray[0] != NULL){
HeapFree(GetProcessHeap(), 0, pReadArray[0]);
pReadArray[0] = NULL;
}
}
DWORD WINAPI ThreadedRead(LPVOID lpParam)
{
BOOL bDone = FALSE, bResult;
//int buff_idx = 0;
DWORD dwCommModemStatus;
DWORD dwBytesTransfered;
PRP pReadArray;
pReadArray = (PRP)lpParam;
SetCommMask(m_hPortThreaded, EV_RXCHAR);
while(!bDone){
WaitCommEvent(m_hPortThreaded, &dwCommModemStatus, 0);
if(dwCommModemStatus == 0){
bDone = TRUE;
break;
}
if(dwCommModemStatus & EV_RXCHAR){
bResult = ReadFile(m_hPortThreaded, pReadArray[0].readBuff, pReadArray[0].readCount, &dwBytesTransfered, 0);
bDone = TRUE;
}
}
return(bResult);
}
int COMPort::DoorLatchOff(void)
{
unsigned char comm_str[10];
int chksum, chksum1;
DWORD count = 6;
WriteComm(21, 7, 0);
comm.Read(comm_str, count);
chksum = comm_str[0] + comm_str[2] + comm_str[3];
chksum1 = comm_str[4];
chksum1 = (chksum1 << 8) | comm_str[5];
if(chksum == chksum1)
return(0);
else
return(-1);
}

Recently I stuck at the same problem, but I have solved it.
There are two ways:
On forums some people recomend to set both ReadIntervalTimeout and ReadTotalTimeoutMultiplier to MAXDWORD, as recomened in MSDN documentation in the REMARKS section. But in this case the funtion returns each time when there is at least one character in the input buffer.
The most robust decision I have found is just to set ReadIntervalTimeout and ReadTotalTimeoutMultiplier to 0, and ReadTotalTimeoutConstant to your timeout value, as below. It works pretty fine for me.
COMMTIMEOUTS commtimeouts;
GetCommTimeouts (hCommFile, &commtimeouts);
commtimeouts.ReadIntervalTimeout = 0;
commtimeouts.ReadTotalTimeoutMultiplier = 0;
commtimeouts.ReadTotalTimeoutConstant = timeout;
commtimeouts.WriteTotalTimeoutMultiplier = 0;
commtimeouts.WriteTotalTimeoutConstant = 0;
SetCommTimeouts (hCommFile, &commtimeouts);

Please, could you try to remove the WaitCommEvent function from ThreadedRead and see if it still hangs?
DWORD WINAPI ThreadedRead(LPVOID lpParam)
{
BOOL bResult;
DWORD dwBytesTransfered = 0;
PRP pReadArray;
pReadArray = (PRP)lpParam;
while (dwBytesTransfered == 0) {
bResult = ReadFile(m_hPortThreaded, pReadArray[0].readBuff, pReadArray[0].readCount, &dwBytesTransfered, 0);
Sleep(250);
}
return(bResult);
}

When dealing with hw I/O it is a best practice to decouple the Application (GUI) thread from the command-execution thread.
If you are developing a C++ Win32 app you could use SerialLib. It is an old but stable Win32 event-driven serial library.

Related

How to find all connected removable USB storage devices in Windows?

I'm writing a program that processes USB drives, to get information about connecting a new device, I signed up for a window message WM_DEVICECHANGE. But I, of course, do not receive messages about the devices that were connected before my program was launched. To process such devices, I wrote a search function, but I get a strange result. It finds my flash drive, but does not recognize it to be removable. Why?
Function
bool FindConnectedRemovableUsbstorDevices(std::list<std::wstring>& UsbList)
{
std::wstring ClassGuidString(L"{53F56307-B6BF-11D0-94F2-00A0C91EFB8B}");
GUID ClassGuid;
BYTE buf[1024];
PSP_DEVICE_INTERFACE_DETAIL_DATA_W pspdidd = reinterpret_cast<PSP_DEVICE_INTERFACE_DETAIL_DATA_W>(buf);
SP_DEVICE_INTERFACE_DATA spdid;
SP_DEVINFO_DATA spdd;
DWORD size;
SP_DEVINFO_DATA dev_data;
DWORD properties;
if(NOERROR != CLSIDFromString(ClassGuidString.c_str(), &ClassGuid))
return false;
HDEVINFO dev_info = INVALID_HANDLE_VALUE;
dev_info = SetupDiGetClassDevs(&ClassGuid, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
if (dev_info == INVALID_HANDLE_VALUE)
return false;
DWORD index = 0;
BOOL ret = FALSE;
spdid.cbSize = sizeof(spdid);
while (true)
{
ret = SetupDiEnumDeviceInterfaces(dev_info, NULL, &ClassGuid, index, &spdid);
if (!ret)
break;
size = 0;
SetupDiGetDeviceInterfaceDetail(dev_info, &spdid, NULL, 0, &size, NULL);
//Get required size
if (size == 0 || size >= sizeof(buf))
continue;
//prepare structs
ZeroMemory(reinterpret_cast<PVOID>(pspdidd), 1024);
pspdidd->cbSize = sizeof(*pspdidd); // 5 Bytes!
ZeroMemory(reinterpret_cast<PVOID>(&spdd), sizeof(spdd));
spdd.cbSize = sizeof(spdd);
BOOL res = SetupDiGetDeviceInterfaceDetail(dev_info, &spdid, pspdidd, size, &size, &spdd);
//Get info
if (!res)
continue;
HANDLE drive = CreateFileW(pspdidd->DevicePath, FILE_READ_ATTRIBUTES,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL, OPEN_EXISTING, 0, NULL);//open device
if (drive == INVALID_HANDLE_VALUE)
continue;
printf("\n%S\r\n", pspdidd->DevicePath);
DWORD bytes_returned = 0;
BOOL b = DeviceIoControl(drive, IOCTL_STORAGE_CHECK_VERIFY2, NULL, 0, NULL, 0, &bytes_returned, NULL);
if (!b) //check is card reader?
{
printf("IOCTL_STORAGE_CHECK_VERIFY2 error = %d\r\n", GetLastError());
goto stop_process_device;
}
bytes_returned = 0;
STORAGE_DEVICE_NUMBER sdn;
//Get Drive number
b = DeviceIoControl(drive, IOCTL_STORAGE_GET_DEVICE_NUMBER, NULL, 0, &sdn, sizeof(sdn), &bytes_returned, NULL);
if (!b)
goto stop_process_device;
RtlZeroMemory(&dev_data, sizeof(SP_DEVINFO_DATA));
dev_data.cbSize = sizeof(dev_data);
if (SetupDiEnumDeviceInfo(dev_info, sdn.DeviceNumber, &dev_data))
{
//check property
b = SetupDiGetDeviceRegistryProperty(dev_info, &dev_data, SPDRP_REMOVAL_POLICY, NULL,
reinterpret_cast<PBYTE>(&properties), sizeof(properties), NULL);
if (b && properties != CM_REMOVAL_POLICY_EXPECT_NO_REMOVAL)
{
UsbList.push_back(pspdidd->DevicePath);
printf("REMOVAL\r\n");
}
}
stop_process_device:
CloseHandle(drive);
index++;
}
SetupDiDestroyDeviceInfoList(dev_info);
return true;
}
And output
\\?\usbstor#disk&ven_generic-&prod_ms#ms-pro#hg&rev_1.00#20090703819900000&1#{53f56307-b6bf-11d0-94f2-00a0c91efb8b}
IOCTL_STORAGE_CHECK_VERIFY2 error = 21
\\?\ide#diskst3500418as_____________________________cc38____#5&5c6cfd6&0&1.0.0#{53f56307-b6bf-11d0-94f2-00a0c91efb8b}
REMOVAL
\\?\usbstor#disk&ven_generic-&prod_sd#mmc&rev_1.00#20090703819900000&0#{53f56307-b6bf-11d0-94f2-00a0c91efb8b}
IOCTL_STORAGE_CHECK_VERIFY2 error = 21
\\?\scsi#disk&ven_ocz&prod_revodrive3_x2#5&19ad1f72&0&000000#{53f56307-b6bf-11d0-94f2-00a0c91efb8b}
\\?\ide#diskst1000lm014-1ej164______________________sm30____#5&2ea7e938&0&0.1.0#{53f56307-b6bf-11d0-94f2-00a0c91efb8b}
\\?\usbstor#disk&ven_sandisk&prod_extreme&rev_0001#aa010823150434152862&0#{53f56307-b6bf-11d0-94f2-00a0c91efb8b}
\\?\ide#diskwdc_wd1002fbys-02a6b0___________________03.00c06#5&2ea7e938&0&0.0.0#{53f56307-b6bf-11d0-94f2-00a0c91efb8b}
REMOVAL
Error 21 is empty card reader.
Sandisk is my flash. In Debug i saw that SetupDiGetDeviceRegistryProperty return property CM_REMOVAL_POLICY_EXPECT_NO_REMOVAL, but i really don't know why...
If you just want to find the connected removable devices, there's a much simpler alternative using GetLogicalDriveStrings() and GetDriveType():
#define MAX_DRIVES 256
bool FindConnectedRemovableUsbstorDevices(std::list<std::wstring>& UsbList)
{
wchar_t drives[MAX_DRIVES];
wchar_t* temp = drives;
if (GetLogicalDriveStringsW(MAX_DRIVES, drives) == 0)
return false;
while (*temp != NULL)
{
if (GetDriveTypeW(temp) == 2 /* DRIVE_REMOVABLE */)
UsbList.push_back(temp);
// Go to the next drive
temp += lstrlenW(temp) + 1;
}
return true;
}
I don't know why my fisrt method of detection removable media works so strange, but method of RbMm works great. I send IOCTL query to every found device with IOCTL_STORAGE_QUERY_PROPERTY with StorageDeviceProperty and look for STORAGE_DEVICE_DESCRIPTOR.RemovableMedia field. All my devices recognized successfully and right.

correctly reading or writing serial port Windows API

I have read alot of issues with serial port reading and writing. None so far have helped me figure out what my code is missing. The msdn example for c++ has undefined variables and missing brackets so although i can add brackets it still does not function. Here's what I've got at this point. It appears I can open the port and do the configuration but I cannot read a byte/char of data. I really just want a simple asyncronous serial read/write for aprogram to read from an Arduino.
class MY_SERIAL
{
HANDLE serialinstance;
DWORD dwStoredFlags;
DWORD dwRes;
DWORD dwCommEvent;
OVERLAPPED osStatus = {0};
BOOL fWaitingOnStat;
//dwStoredFlags = EV_BREAK | EV_CTS | EV_DSR | EV_ERR | EV_RING | EV_RLSD | EV_RXCHAR | EV_RXFLAG | EV_TXEMPTY ;
DCB dcb;
COMMTIMEOUTS timeouts;
COMMCONFIG serialconfig;
public:
char inBuffer[1000];
char outBuffer[100];
PDWORD noBytes;
void close_serial()
{
CloseHandle(serialinstance);
}
//----------------------------------------------------
bool open_serial(LPCSTR portNumber) // serial port name use this format "\\\\.\\COM10"
{
serialinstance = CreateFile(portNumber, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
if(serialinstance == INVALID_HANDLE_VALUE)
{
int error = GetLastError();
printf("ERROR opening serial port %s\r\n", portNumber);
if(error == 0x2){printf("ERROR_FILE_NOT_FOUND\r\n");}
if(error == 0x5){printf("ERROR_ACCESS_DENIED\r\n");}
if(error == 0xC){printf("ERROR_INVALID_ACCESS\r\n");}
if(error == 0x6){printf("ERROR_INVALID_HANDLE\r\n");}
printf("error code %d\r\n", error);
return false;
}
if(GetCommState(serialinstance, &dcb)!= true)
{
printf("ERROR getting current state of COM %d \r\n", GetLastError());
return false;
}
else{printf("debug read current comstate\r\n");}
FillMemory(&dcb, sizeof(dcb), 0); //zero initialize the structure
dcb.DCBlength = sizeof(dcb); //fill in length
dcb.BaudRate = CBR_115200; // baud rate
dcb.ByteSize = 8; // data size, xmit and rcv
dcb.Parity = NOPARITY; // parity bit
dcb.StopBits = ONESTOPBIT;
if(SetCommState(serialinstance, &dcb) != true)
{
printf("ERROR setting new state of COM %d \r\n", GetLastError());
return false;
}
else{printf("debug set new comstate\r\n");}
/*
if (!BuildCommDCB("115200,n,8,1", &dcb)) //fills in basic async details
{
printf("ERROR getting port comstate\r\n");
return FALSE;
}
*/
if (!SetCommMask(serialinstance, EV_RXCHAR))
{
printf("ERROR setting new COM MASK %d \r\n", GetLastError());
return false;
}
else{printf("debug commmask set\r\n");}
timeouts.ReadIntervalTimeout = MAXDWORD;
timeouts.ReadTotalTimeoutMultiplier = 20;
timeouts.ReadTotalTimeoutConstant = 0;
timeouts.WriteTotalTimeoutMultiplier = 0;
timeouts.WriteTotalTimeoutConstant = 0;
if (!SetCommTimeouts(serialinstance, &timeouts))
{
printf("ERROR setting timeout parameters\r\n");
return false;
}
else{printf("debug timeouts set\r\n");}
osStatus.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (osStatus.hEvent == NULL)
{// error creating event; abort
printf("ERROR creating Serial EVENT\r\n");
return false;
}
else{printf("debug event created set\r\n");}
osStatus.Internal = 0;
osStatus.InternalHigh = 0;
osStatus.Offset = 0;
osStatus.OffsetHigh = 0;
assert(osStatus.hEvent);
printf("debug com port setting complete\r\n");
return true;
}
//---------------------------------------------------------
bool read_serial_simple()
{
char m[1000];
LPDWORD bytesRead;
if (WaitCommEvent(serialinstance, &dwCommEvent, &osStatus))
{
if(dwCommEvent & EV_RXCHAR)
{
ReadFile(serialinstance, &m, 1, bytesRead, &osStatus);
printf("data read = %d, number bytes read = %d \r\n", m, bytesRead);
return true;
}
else
{
int error = GetLastError();
if(error == ERROR_IO_PENDING){printf(" waiting on incomplete IO\r\n");}
else{printf("ERROR %d\r\n", error);}
return false;
}
}
return false;
}
};
So I stripped the read function down. I now get a char and it reports reading 1 byte but the value of the char is incorrect. I get a series of 48, 13, 10, and occasionally a 50 value for the byte. However the Arduino is sending a series a 0's then a 128 as verified with TerraTerm. What else do I need here?
bool read_serial_simple()
{
unsigned char m = 0;
DWORD bytesRead;
if(ReadFile(serialinstance, &m, 1, &bytesRead, &osStatus) == true)
{
printf("data read = %d, number bytes read = %d \r\n", m, bytesRead);
return true;
}
else{
int error = GetLastError();
if(error == ERROR_IO_PENDING){printf(" waiting on incomplete IO\r\n");}
else{printf("ERROR %d\r\n", error);}
return false;
}
}
So now I can read a byte of data but I cannot write a byte or more to the port. I just get ERROR_IO_PENDING. Can someone help out with this as well. Write function of my class below.
bool write(DWORD noBytesToWrite)
{
if(WriteFile(serialinstance, outBuffer, noBytesToWrite, NULL, &osStatus) == true)
{
printf("message sent\r\n");
return true;
}
else
{
int error = GetLastError();
if(error != ERROR_IO_PENDING){LastError();}
return false;
}
}
I'm calling both functions from main as follows
myserial.open_serial(COM12);
myserial.outBuffer[0] = 'H';
myserial.outBuffer[1] = 'e';
myserial.outBuffer[2] = 'L';
myserial.outBuffer[3] = 'l';
myserial.outBuffer[4] = 'O';
for(int n=0; n<5; n++){printf("%c", myserial.outBuffer[n]);}
printf("\r\n");
while(1)
{
myserial.read();
myserial.write(5);
//system("PAUSE");
}
Currently the arduino is set up to read bytes in and repeat them back to the pc. It is doing this fine on the arduino IDE serial monitor so now I just need to get this pc program to write out.
Your bytesRead variable is an uninitialized pointer. You are passing an invalid address to ReadFile() to write to.
Replace LPDWORD bytesRead with DWORD bytesRead, then pass it to ReadFile() using &bytesRead.
Edit:
Also eliminate the FILE_FLAG_OVERLAPPED. You are not handling it properly, and there is no point in using it if you WaitForSingleObject() before reading.
Sorry my answer is a bit late, but as I was checking up on another serial port detail I found this.
There is a bit flaw in the original code. You are calling CreateFile with the FILE_FLAG_OVERLAPPED flag. This means you want to use non-blocking calls. You either need to remove this flag, or change your ReadFile and WriteFile calls so that they include a pointer to an OVERLAPPED structure WriteFile.
Your code works with ReadFile as it will complete syncrhronously as there is a character already waiting. The WriteFile will return IO_PENDING result to indicate that the write has been queued.

ReadDirectoryChangesW issues

I'am using ReadDirectoryChangesW to watch a directory changes asynchronously, based on this question I implement a function that watch a given directory, but I still get the error message GetQueuedCompletionStatus(): Timeout
void Filewatcher::OpenWatchDir(QString PathToOpen)
{
QString path=QDir::fromNativeSeparators(PathToOpen);
LPCTSTR Dirname=(LPCTSTR)path.utf16();//.toStdWString().c_str();
dirinfo_t* d =(dirinfo_t*) malloc(1*sizeof(dirinfo_t));
d->CompletionKey = (ULONG_PTR)&somekey;
dirinfo_init(d);
/* set up */
runthread = TRUE;
d->hDirFH = CreateFile(Dirname,
FILE_LIST_DIRECTORY,
FILE_SHARE_READ|FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED,
NULL);
d->hDirOPPort = CreateIoCompletionPort(d->hDirFH, NULL,
(ULONG_PTR)d->CompletionKey, 1);
DWORD errorcode = 0; // an error code
BOOL bResultQ = FALSE; // obvios=us
BOOL bResultR = FALSE;
DWORD NumBytes = 0;
FILE_NOTIFY_INFORMATION* pInfo = NULL; // the data incoming is a pointer
// to this struct.
int i = 0;
while ( runthread )
{
bResultR = ReadDirectoryChangesW(d->hDirFH, (void*)d->buffer,
16777216, TRUE,
FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_CREATION ,
NULL,
&d->o->overlapped,
NULL );
bResultQ = GetQueuedCompletionStatus(d->hDirOPPort,
&NumBytes, &(d->CompletionKey),
(LPOVERLAPPED*)(d->o), 1000);
if ( bResultQ && bResultR )
{
wprintf(L"\n");
pInfo = (FILE_NOTIFY_INFORMATION*) d->buffer;
wprintf(L"File %s", pInfo->FileName);
wprintf(L" changes %d\n", pInfo->Action);
qDebug()<<"file "<<pInfo->FileName<<" was"<<pInfo->Action;
memset(d->buffer, 0, 16777216);
}
else
{
errorcode = GetLastError();
if ( errorcode == WAIT_TIMEOUT )
{
qDebug()<<"GetQueuedCompletionStatus(): Timeout\n";
}
else
{
qDebug()<<"GetQueuedCompletionStatus(): Failed\n";
qDebug()<<"Error Code "<<errorcode;
}
Sleep(500);
}
}
}
I need to know how use ReadDirectoryChangesW asynchronously with IoCompletionPort.
Any help please.
There's no reason to use a completion port here, simple overlapped I/O with an event will work fabulously.
The key is to wait for this operation (whether event or completion port) at the same time as all other events (possibly including GUI messages), and only check the status when the event becomes signaled. For that, use (Msg)WaitForMultipleObjects(Ex).
In Qt, you can add Win32 events (used by OVERLAPPED structure for async I/O) using QWinEventNotifier as described here:
http://www.downtowndougbrown.com/2010/07/adding-windows-event-objects-to-a-qt-event-loop/
thank you guys for your answers, after a deep research and retesting code I solve my problem based on this , I really appreciate your help.

Terrible Serial Port / USB code (C++) - suggestions for fixes?

I don't have much experience with Serial I/O, but have recently been tasked with fixing some highly flawed serial code, because the original programmer has left the company.
The application is a Windows program that talks to a scientific instrument serially via a virtual COMM port running on USB. Virtual COMM port USB drivers are provided by FTDI, since they manufacture the USB chip we use on the instrument.
The serial code is in an unmanaged C++ DLL, which is shared by both our old C++ software, and our new C# / .Net (WinForms) software.
There are two main problems:
Fails on many XP systems
When the first command is sent to the instrument, there's no response. When you issue the next command, you get the response from the first one.
Here's a typical usage scenario (full source for methods called is included below):
char szBuf [256];
CloseConnection ();
if (OpenConnection ())
{
ClearBuffer ();
// try to get a firmware version number
WriteChar ((char) 'V');
BOOL versionReadStatus1 = ReadString (szBuf, 100);
...
}
On a failing system, the ReadString call will never receive any serial data, and times out. But if we issue another, different command, and call ReadString again, it will return the response from the first command, not the new one!
But this only happens on a large subset of Windows XP systems - and never on Windows 7. As luck would have it, our XP dev machines worked OK, so we did not see the problem until we started beta testing. But I can also reproduce the problem by running an XP VM (VirtualBox) on my XP dev machine. Also, the problem only occurs when using the DLL with the new C# version - works fine with the old C++ app.
This seemed to be resolved when I added a Sleep(21) to the low level BytesInQue method before calling ClearCommError, but this exacerbated the other problem - CPU usage. Sleeping for less than 21 ms would make the failure mode reappear.
High CPU usage
When doing serial I/O CPU use is excessive - often above 90%. This happens with both the new C# app and the old C++ app, but is much worse in the new app. Often makes the UI very non-responsive, but not always.
Here's the code for our Port.cpp class, in all it's terrible glory. Sorry for the length, but this is what I'm working with. Most important methods are probably OpenConnection, ReadString, ReadChar, and BytesInQue.
//
// Port.cpp: Implements the CPort class, which is
// the class that controls the serial port.
//
// Copyright (C) 1997-1998 Microsoft Corporation
// All rights reserved.
//
// This source code is only intended as a supplement to the
// Broadcast Architecture Programmer's Reference.
// For detailed information regarding Broadcast
// Architecture, see the reference.
//
#include <windows.h>
#include <stdio.h>
#include <assert.h>
#include "port.h"
// Construction code to initialize the port handle to null.
CPort::CPort()
{
m_hDevice = (HANDLE)0;
// default parameters
m_uPort = 1;
m_uBaud = 9600;
m_uDataBits = 8;
m_uParity = 0;
m_uStopBits = 0; // = 1 stop bit
m_chTerminator = '\n';
m_bCommportOpen = FALSE;
m_nTimeOut = 50;
m_nBlockSizeMax = 2048;
}
// Destruction code to close the connection if the port
// handle was valid.
CPort::~CPort()
{
if (m_hDevice)
CloseConnection();
}
// Open a serial communication port for writing short
// one-byte commands, that is, overlapped data transfer
// is not necessary.
BOOL CPort::OpenConnection()
{
char szPort[64];
m_bCommportOpen = FALSE;
// Build the COM port string as "COMx" where x is the port.
if (m_uPort > 9)
wsprintf(szPort, "\\\\.\\COM%d", m_uPort);
else
wsprintf(szPort, "COM%d", m_uPort);
// Open the serial port device.
m_hDevice = CreateFile(szPort,
GENERIC_WRITE | GENERIC_READ,
0,
NULL, // No security attributes
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (m_hDevice == INVALID_HANDLE_VALUE)
{
SaveLastError ();
m_hDevice = (HANDLE)0;
return FALSE;
}
return SetupConnection(); // After the port is open, set it up.
} // end of OpenConnection()
// Configure the serial port with the given settings.
// The given settings enable the port to communicate
// with the remote control.
BOOL CPort::SetupConnection(void)
{
DCB dcb; // The DCB structure differs betwwen Win16 and Win32.
dcb.DCBlength = sizeof(DCB);
// Retrieve the DCB of the serial port.
BOOL bStatus = GetCommState(m_hDevice, (LPDCB)&dcb);
if (bStatus == 0)
{
SaveLastError ();
return FALSE;
}
// Assign the values that enable the port to communicate.
dcb.BaudRate = m_uBaud; // Baud rate
dcb.ByteSize = m_uDataBits; // Data bits per byte, 4-8
dcb.Parity = m_uParity; // Parity: 0-4 = no, odd, even, mark, space
dcb.StopBits = m_uStopBits; // 0,1,2 = 1, 1.5, 2
dcb.fBinary = TRUE; // Binary mode, no EOF check : Must use binary mode in NT
dcb.fParity = dcb.Parity == 0 ? FALSE : TRUE; // Enable parity checking
dcb.fOutX = FALSE; // XON/XOFF flow control used
dcb.fInX = FALSE; // XON/XOFF flow control used
dcb.fNull = FALSE; // Disable null stripping - want nulls
dcb.fOutxCtsFlow = FALSE;
dcb.fOutxDsrFlow = FALSE;
dcb.fDsrSensitivity = FALSE;
dcb.fDtrControl = DTR_CONTROL_ENABLE;
dcb.fRtsControl = RTS_CONTROL_DISABLE ;
// Configure the serial port with the assigned settings.
// Return TRUE if the SetCommState call was not equal to zero.
bStatus = SetCommState(m_hDevice, &dcb);
if (bStatus == 0)
{
SaveLastError ();
return FALSE;
}
DWORD dwSize;
COMMPROP *commprop;
DWORD dwError;
dwSize = sizeof(COMMPROP) + sizeof(MODEMDEVCAPS) ;
commprop = (COMMPROP *)malloc(dwSize);
memset(commprop, 0, dwSize);
if (!GetCommProperties(m_hDevice, commprop))
{
dwError = GetLastError();
}
m_bCommportOpen = TRUE;
return TRUE;
}
void CPort::SaveLastError ()
{
DWORD dwLastError = GetLastError ();
LPVOID lpMsgBuf;
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
dwLastError,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
(LPTSTR) &lpMsgBuf,
0,
NULL);
strcpy (m_szLastError,(LPTSTR)lpMsgBuf);
// Free the buffer.
LocalFree( lpMsgBuf );
}
void CPort::SetTimeOut (int nTimeOut)
{
m_nTimeOut = nTimeOut;
}
// Close the opened serial communication port.
void CPort::CloseConnection(void)
{
if (m_hDevice != NULL &&
m_hDevice != INVALID_HANDLE_VALUE)
{
FlushFileBuffers(m_hDevice);
CloseHandle(m_hDevice); ///that the port has been closed.
}
m_hDevice = (HANDLE)0;
// Set the device handle to NULL to confirm
m_bCommportOpen = FALSE;
}
int CPort::WriteChars(char * psz)
{
int nCharWritten = 0;
while (*psz)
{
nCharWritten +=WriteChar(*psz);
psz++;
}
return nCharWritten;
}
// Write a one-byte value (char) to the serial port.
int CPort::WriteChar(char c)
{
DWORD dwBytesInOutQue = BytesInOutQue ();
if (dwBytesInOutQue > m_dwLargestBytesInOutQue)
m_dwLargestBytesInOutQue = dwBytesInOutQue;
static char szBuf[2];
szBuf[0] = c;
szBuf[1] = '\0';
DWORD dwBytesWritten;
DWORD dwTimeOut = m_nTimeOut; // 500 milli seconds
DWORD start, now;
start = GetTickCount();
do
{
now = GetTickCount();
if ((now - start) > dwTimeOut )
{
strcpy (m_szLastError, "Timed Out");
return 0;
}
WriteFile(m_hDevice, szBuf, 1, &dwBytesWritten, NULL);
}
while (dwBytesWritten == 0);
OutputDebugString(TEXT(strcat(szBuf, "\r\n")));
return dwBytesWritten;
}
int CPort::WriteChars(char * psz, int n)
{
DWORD dwBytesWritten;
WriteFile(m_hDevice, psz, n, &dwBytesWritten, NULL);
return dwBytesWritten;
}
// Return number of bytes in RX queue
DWORD CPort::BytesInQue ()
{
COMSTAT ComStat ;
DWORD dwErrorFlags;
DWORD dwLength;
// check number of bytes in queue
ClearCommError(m_hDevice, &dwErrorFlags, &ComStat ) ;
dwLength = ComStat.cbInQue;
return dwLength;
}
DWORD CPort::BytesInOutQue ()
{
COMSTAT ComStat ;
DWORD dwErrorFlags;
DWORD dwLength;
// check number of bytes in queue
ClearCommError(m_hDevice, &dwErrorFlags, &ComStat );
dwLength = ComStat.cbOutQue ;
return dwLength;
}
int CPort::ReadChars (char* szBuf, int nMaxChars)
{
if (BytesInQue () == 0)
return 0;
DWORD dwBytesRead;
ReadFile(m_hDevice, szBuf, nMaxChars, &dwBytesRead, NULL);
return (dwBytesRead);
}
// Read a one-byte value (char) from the serial port.
int CPort::ReadChar (char& c)
{
static char szBuf[2];
szBuf[0] = '\0';
szBuf[1] = '\0';
if (BytesInQue () == 0)
return 0;
DWORD dwBytesRead;
ReadFile(m_hDevice, szBuf, 1, &dwBytesRead, NULL);
c = *szBuf;
if (dwBytesRead == 0)
return 0;
return dwBytesRead;
}
BOOL CPort::ReadString (char *szStrBuf , int nMaxLength)
{
char str [256];
char str2 [256];
DWORD dwTimeOut = m_nTimeOut;
DWORD start, now;
int nBytesRead;
int nTotalBytesRead = 0;
char c = ' ';
static char szCharBuf [2];
szCharBuf [0]= '\0';
szCharBuf [1]= '\0';
szStrBuf [0] = '\0';
start = GetTickCount();
while (c != m_chTerminator)
{
nBytesRead = ReadChar (c);
nTotalBytesRead += nBytesRead;
if (nBytesRead == 1 && c != '\r' && c != '\n')
{
*szCharBuf = c;
strncat (szStrBuf,szCharBuf,1);
if (strlen (szStrBuf) == nMaxLength)
return TRUE;
// restart timer for next char
start = GetTickCount();
}
// check for time out
now = GetTickCount();
if ((now - start) > dwTimeOut )
{
strcpy (m_szLastError, "Timed Out");
return FALSE;
}
}
return TRUE;
}
int CPort::WaitForQueToFill (int nBytesToWaitFor)
{
DWORD start = GetTickCount();
do
{
if (BytesInQue () >= nBytesToWaitFor)
break;
if (GetTickCount() - start > m_nTimeOut)
return 0;
} while (1);
return BytesInQue ();
}
int CPort::BlockRead (char * pcInputBuffer, int nBytesToRead)
{
int nBytesRead = 0;
int charactersRead;
while (nBytesToRead >= m_nBlockSizeMax)
{
if (WaitForQueToFill (m_nBlockSizeMax) < m_nBlockSizeMax)
return nBytesRead;
charactersRead = ReadChars (pcInputBuffer, m_nBlockSizeMax);
pcInputBuffer += charactersRead;
nBytesRead += charactersRead;
nBytesToRead -= charactersRead;
}
if (nBytesToRead > 0)
{
if (WaitForQueToFill (nBytesToRead) < nBytesToRead)
return nBytesRead;
charactersRead = ReadChars (pcInputBuffer, nBytesToRead);
nBytesRead += charactersRead;
nBytesToRead -= charactersRead;
}
return nBytesRead;
}
Based on my testing and reading, I see several suspicious things in this code:
COMMTIMEOUTS is never set. MS docs say "Unpredictable results can occur if you fail to set the time-out values". But I tried setting this, and it didn't help.
Many methods (e.g. ReadString) will go into a tight loop and hammer the port with repeated reads if they don't get data immediately . This seems to explain the high CPU usage.
Many methods have their own timeout handling, using GetTickCount(). Isn't that what COMMTIMEOUTS is for?
In the new C# (WinForms) program, all these serial routines are called directly from the main thread, from a MultiMediaTimer event. Maybe should be run in a different thread?
BytesInQue method seems to be a bottleneck. If I break to debugger when CPU usage is high, that's usually where the program stops. Also, adding a Sleep(21) to this method before calling ClearCommError seems to resolve the XP problem, but exacerbates the CPU usage problem.
Code just seems unnecessarily complicated.
My Questions
Can anyone explain why this only works with a C# program on a small number of XP systems?
Any suggestions on how to rewrite this? Pointers to good sample code would be most welcome.
There are some serious problems with that class and it makes things even worse that there is a Microsoft copyright on it.
There is nothing special about this class. And it makes me wonder why it even exists except as an Adapter over Create/Read/WriteFile. You wouldnt even need this class if you used the SerialPort class in the .NET Framework.
Your CPU usage is because the code goes into an infinite loop while waiting for the device to have enough available data. The code might as well say while(1); If you must stick with Win32 and C++ you can look into Completion Ports and setting the OVERLAPPED flag when invoking CreateFile. This way you can wait for data in a separate worker thread.
You need to be careful when communicating to multiple COM ports. It has been a long time since I've done C++ but I believe the static buffer szBuff in the Read and Write methods is static for ALL instances of that class. It means if you invoke Read against two different COM ports "at the same time" you will have unexpected results.
As for the problems on some of the XP machines, you will most certainly figure out the problem if you check GetLastError after each Read/Write and log the results. It should be checking GetLastError anyways as it sometimes isn't always an "error" but a request from the subsystem to do something else in order to get the result you want.
You can get rid of the the whole while loop for blocking if you set COMMTIMEOUTS correctly. If there is a specific timeout for a Read operation use SetCommTimeouts before you perform the read.
I set ReadIntervalTimeout to the max timeout to ensure that the Read won't return quicker than m_nTimeOut. This value will cause Read to return if the time elapses between any two bytes. If it was set to 2 milliseconds and the first byte came in at t, and the second came in at t+1, the third at t+4, ReadFile would of only returned the first two bytes since the interval between the bytes was surpassed. ReadTotalTimeoutConstant ensures that you will never wait longer than m_nTimeOut no matter what.
maxWait = BytesToRead * ReadTotalTimeoutMultiplier + ReadTotalTimeoutConstant. Thus (BytesToRead * 0) + m_nTimeout = m_nTimeout
BOOL CPort::SetupConnection(void)
{
// Snip...
COMMTIMEOUTS comTimeOut;
comTimeOut.ReadIntervalTimeout = m_nTimeOut; // Ensure's we wait the max timeout
comTimeOut.ReadTotalTimeoutMultiplier = 0;
comTimeOut.ReadTotalTimeoutConstant = m_nTimeOut;
comTimeOut.WriteTotalTimeoutMultiplier = 0;
comTimeOut.WriteTotalTimeoutConstant = m_nTimeOut;
SetCommTimeouts(m_hDevice,&comTimeOut);
}
// If return value != nBytesToRead check check GetLastError()
// Most likely Read timed out.
int CPort::BlockRead (char * pcInputBuffer, int nBytesToRead)
{
DWORD dwBytesRead;
if (FALSE == ReadFile(
m_hDevice,
pcInputBuffer,
nBytesToRead,
&dwBytesRead,
NULL))
{
// Check GetLastError
return dwBytesRead;
}
return dwBytesRead;
}
I have no idea if this is completely correct but it should give you an idea. Remove the ReadChar and ReadString methods and use this if your program relies on things being synchronous. Be careful about setting high time outs also. Communications are fast, in the milliseconds.
Here's a terminal program I wrote years ago (probably at least 15 years ago, now that I think about it). I just did a quick check, and under Windows 7 x64, it still seems to work reasonably well -- connects to my GPS, read, and displays the data coming from it.
If you look at the code, you can see that I didn't spend much time selecting the comm timeout values. I set them all to 1, intending to experiment with longer timeouts until the CPU usage was tolerable. To make a long story short, it uses so little CPU time I've never bothered. For example, on the Task Manager's CPU usage graph, I can't see any difference between it running and not. I've left it running collecting data from the GPS for a few hours at a time, and the Task Manager still says its total CPU usage is 0:00:00.
Bottom line: I'm pretty sure it could be more efficient -- but sometimes good enough is good enough. Given how heavily I don't use it any more, and the chances of ever adding anything like file transfer protocols, making it more efficient probably won't ever get to the top of the pile of things to do.
#include <stdio.h>
#include <conio.h>
#include <string.h>
#define STRICT
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
void system_error(char *name) {
// Retrieve, format, and print out a message from the last error. The
// `name' that's passed should be in the form of a present tense noun
// (phrase) such as "opening file".
//
char *ptr = NULL;
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM,
0,
GetLastError(),
0,
(char *)&ptr,
1024,
NULL);
fprintf(stderr, "\nError %s: %s\n", name, ptr);
LocalFree(ptr);
}
int main(int argc, char **argv) {
int ch;
char buffer[64];
HANDLE file;
COMMTIMEOUTS timeouts;
DWORD read, written;
DCB port;
HANDLE keyboard = GetStdHandle(STD_INPUT_HANDLE);
HANDLE screen = GetStdHandle(STD_OUTPUT_HANDLE);
DWORD mode;
char port_name[128] = "\\\\.\\COM3";
char init[] = "";
if ( argc > 2 )
sprintf(port_name, "\\\\.\\COM%s", argv[1]);
// open the comm port.
file = CreateFile(port_name,
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
0,
NULL);
if ( INVALID_HANDLE_VALUE == file) {
system_error("opening file");
return 1;
}
// get the current DCB, and adjust a few bits to our liking.
memset(&port, 0, sizeof(port));
port.DCBlength = sizeof(port);
if (!GetCommState(file, &port))
system_error("getting comm state");
if (!BuildCommDCB("baud=19200 parity=n data=8 stop=1", &port))
system_error("building comm DCB");
if (!SetCommState(file, &port))
system_error("adjusting port settings");
// set short timeouts on the comm port.
timeouts.ReadIntervalTimeout = 1;
timeouts.ReadTotalTimeoutMultiplier = 1;
timeouts.ReadTotalTimeoutConstant = 1;
timeouts.WriteTotalTimeoutMultiplier = 1;
timeouts.WriteTotalTimeoutConstant = 1;
if (!SetCommTimeouts(file, &timeouts))
system_error("setting port time-outs.");
// set keyboard to raw reading.
if (!GetConsoleMode(keyboard, &mode))
system_error("getting keyboard mode");
mode &= ~ ENABLE_PROCESSED_INPUT;
if (!SetConsoleMode(keyboard, mode))
system_error("setting keyboard mode");
if (!EscapeCommFunction(file, CLRDTR))
system_error("clearing DTR");
Sleep(200);
if (!EscapeCommFunction(file, SETDTR))
system_error("setting DTR");
if (!WriteFile(file, init, sizeof(init), &written, NULL))
system_error("writing data to port");
if (written != sizeof(init))
system_error("not all data written to port");
// basic terminal loop:
do {
// check for data on port and display it on screen.
ReadFile(file, buffer, sizeof(buffer), &read, NULL);
if (read)
WriteFile(screen, buffer, read, &written, NULL);
// check for keypress, and write any out the port.
if ( kbhit() ) {
ch = getch();
WriteFile(file, &ch, 1, &written, NULL);
}
// until user hits ctrl-backspace.
} while ( ch != 127);
// close up and go home.
CloseHandle(keyboard);
CloseHandle(file);
return 0;
}
I would add
Sleep(2);
to the while loop in CPort::WaitForQueToFill()
This will give the OS a chance to actually place some bytes in the queue.

Serial Comm using WriteFile/ReadFile

//#include "StdAfx.h"
#include <stdio.h>
#include <windows.h>
#include <winbase.h>
#include <iostream>
#include <tchar.h>
using namespace std;
int main()
{
int com = 'COM2';
string data = "\n 010400 \n";
char output[32];
//unsigned int length = 0;
DCB config = {0};
bool abContinue = true;
DWORD dwBytesWritten;
DWORD dwBytesRead;
int isRead = false;
HANDLE m_hCommPort = ::CreateFile(L"COM2",
GENERIC_READ|GENERIC_WRITE,//access ( read and write)
0, //(share) 0:cannot share the COM port
0, //security (None)
OPEN_EXISTING,// creation : open_existing
0, // we dont want overlapped operation
0// no templates file for COM port...
);
config.DCBlength = sizeof(config);
if((GetCommState(m_hCommPort, &config) == 0))
{
printf("Get configuration port has a problem.");
return FALSE;
}
config.BaudRate = 9600;
config.StopBits = ONESTOPBIT;
config.Parity = PARITY_NONE;
config.ByteSize = DATABITS_8;
config.fDtrControl = 0;
config.fRtsControl = 0;
if (!SetCommState(m_hCommPort, &config))
{
printf( "Failed to Set Comm State Reason: %d\n",GetLastError());
//return E_FAIL;
}
printf("Current Settings\n Baud Rate %d\n Parity %d\n Byte Size %d\n Stop Bits %d", config.BaudRate,
config.Parity, config.ByteSize, config.StopBits);
int isWritten = WriteFile(m_hCommPort, &data,(DWORD) sizeof(data), &dwBytesWritten, NULL);
//memset(output, 0, sizeof(output));
while (abContinue)
{
isRead = ReadFile(m_hCommPort, output, sizeof(output), &dwBytesRead, NULL);
if(!isRead)
{
abContinue = false;
break;
}
}
cin.get();
}
I am having trouble reading from the com port. If I step through the code, it goes into "isRead = ReadFile(m_hCommPort, output, sizeof(output), &dwBytesRead, NULL);" and doesn't come back out.... This is my first try at this with no success.
You might try some code something like this after you've opened the file, but before you try to use it:
COMMTIMEOUTS timeouts;
timeouts.ReadIntervalTimeout = 1;
timeouts.ReadTotalTimeoutMultiplier = 1;
timeouts.ReadTotalTimeoutConstant = 1;
timeouts.WriteTotalTimeoutMultiplier = 1;
timeouts.WriteTotalTimeoutConstant = 1;
if (!SetCommTimeouts(m_hCommPort, &timeouts))
// setting timeouts failed.
Edit: perhaps it's easier to start with some code that works, and make it do what you want rather than trying to get your code to work. Here's a simple terminal program. It's minimalist in the extreme, but does work (at least well enough to let me see output from my GPS, for one example). It's a long ways from what anybody (least of all me) would call sophisticated, but should give at least some idea of how to get started.
#include <stdio.h>
#include <conio.h>
#include <string.h>
#define STRICT
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
void system_error(char *name) {
// Retrieve, format, and print out a message from the last error. The
// `name' that's passed should be in the form of a present tense noun
// (phrase) such as "opening file".
//
char *ptr = NULL;
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM,
0,
GetLastError(),
0,
(char *)&ptr,
1024,
NULL);
fprintf(stderr, "\nError %s: %s\n", name, ptr);
LocalFree(ptr);
}
int main(int argc, char **argv) {
int ch;
char buffer[1];
HANDLE file;
COMMTIMEOUTS timeouts;
DWORD read, written;
DCB port;
HANDLE keyboard = GetStdHandle(STD_INPUT_HANDLE);
HANDLE screen = GetStdHandle(STD_OUTPUT_HANDLE);
DWORD mode;
char port_name[128] = "\\\\.\\COM3";
char init[] = ""; // e.g., "ATZ" to completely reset a modem.
if ( argc > 2 )
sprintf(port_name, "\\\\.\\COM%c", argv[1][0]);
// open the comm port.
file = CreateFile(port_name,
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
0,
NULL);
if ( INVALID_HANDLE_VALUE == file) {
system_error("opening file");
return 1;
}
// get the current DCB, and adjust a few bits to our liking.
memset(&port, 0, sizeof(port));
port.DCBlength = sizeof(port);
if ( !GetCommState(file, &port))
system_error("getting comm state");
if (!BuildCommDCB("baud=19200 parity=n data=8 stop=1", &port))
system_error("building comm DCB");
if (!SetCommState(file, &port))
system_error("adjusting port settings");
// set short timeouts on the comm port.
timeouts.ReadIntervalTimeout = 1;
timeouts.ReadTotalTimeoutMultiplier = 1;
timeouts.ReadTotalTimeoutConstant = 1;
timeouts.WriteTotalTimeoutMultiplier = 1;
timeouts.WriteTotalTimeoutConstant = 1;
if (!SetCommTimeouts(file, &timeouts))
system_error("setting port time-outs.");
// set keyboard to raw reading.
if (!GetConsoleMode(keyboard, &mode))
system_error("getting keyboard mode");
mode &= ~ ENABLE_PROCESSED_INPUT;
if (!SetConsoleMode(keyboard, mode))
system_error("setting keyboard mode");
if (!EscapeCommFunction(file, CLRDTR))
system_error("clearing DTR");
Sleep(200);
if (!EscapeCommFunction(file, SETDTR))
system_error("setting DTR");
if ( !WriteFile(file, init, sizeof(init), &written, NULL))
system_error("writing data to port");
if (written != sizeof(init))
system_error("not all data written to port");
// basic terminal loop:
do {
// check for data on port and display it on screen.
ReadFile(file, buffer, sizeof(buffer), &read, NULL);
if ( read )
WriteFile(screen, buffer, read, &written, NULL);
// check for keypress, and write any out the port.
if ( kbhit() ) {
ch = getch();
WriteFile(file, &ch, 1, &written, NULL);
}
// until user hits ctrl-backspace.
} while ( ch != 127);
// close up and go home.
CloseHandle(keyboard);
CloseHandle(file);
return 0;
}
If you do not explicitly set the timeouts, then ReadFile will indefinitely block until data becomes available.
ReadFile function may be blocking your thread,if so, it will remain blocked until some data can be read from Serial port. Here is a link see if its help. Good luck.
I had this problem on a readfile, with the timeouts set. This was driving me crackers so I ended up getting some code from the web which did work and then changing line by line to see where the error was.
Turns out he readfile was fine. My problem was a WaitCommEvent which was hanging when the port was disconnected as no com event is ever received...