I have written C++ code that is able to read from and write to a com port using the Win32 API method ReadFile. The code is for the purpose of sending HPGL instructions to a legacy graphics plotter and retrieving status data from it. I have ported this code to C# and added DllImport definitions for all the Win32 API methods that the code requires and everything is working fine except for ReadFile which never retrieves any data from the port and never returns an error or throws an exception. I have created a pair of small apps that make all the necessary Win32 API calls to communicate with the plotter, one in C++ which works correctly and the other in C# which fails consistently. This is the C++ app, followed by the C# app. I've been looking at this code for a few days trying various changes to the ReadFile call (Unicode vs. Ansi, out vs. ref, etc.) and nothing has changed. I've also done extensive searching online but I haven't been able to find anything relevant. I'd appreciate any suggestions anyone might have.
What I have tried:
int _tmain(int argc, _TCHAR* argv[])
{
HANDLE hSerial = CreateFile (TEXT("\\\\.\\COM3"),
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
DCB dcbSerialParams;
COMMTIMEOUTS timeouts;
ZeroMemory (&dcbSerialParams, sizeof (DCB));
ZeroMemory (&timeouts, sizeof (COMMTIMEOUTS));
dcbSerialParams.DCBlength = sizeof (dcbSerialParams);
if (GetCommState(hSerial, &dcbSerialParams) == 0)
{
CloseHandle (hSerial);
return 1;
}
dcbSerialParams.BaudRate = 9600;
dcbSerialParams.ByteSize = 8;
dcbSerialParams.StopBits = ONESTOPBIT;
dcbSerialParams.Parity = NOPARITY;
if (SetCommState (hSerial, &dcbSerialParams) == 0)
{
CloseHandle (hSerial);
return 2;
}
timeouts.ReadIntervalTimeout = 50;
timeouts.ReadTotalTimeoutConstant = 50;
timeouts.ReadTotalTimeoutMultiplier = 10;
timeouts.WriteTotalTimeoutConstant = 50;
timeouts.WriteTotalTimeoutMultiplier = 10;
if (SetCommTimeouts (hSerial, &timeouts) == 0)
{
CloseHandle (hSerial);
return 3;
}
DWORD dwBytesWritten = 0;
int iResult = WriteFile (hSerial, (LPVOID)"OI;", 3, &dwBytesWritten, NULL);
FlushFileBuffers (hSerial);
char szBuffer[10];
::ZeroMemory (szBuffer, 10);
DWORD dwBytesRead = 0,
dwErrorFlags = 0;
COMSTAT comstat;
::ZeroMemory ((void*)&comstat, sizeof (COMSTAT));
ClearCommError (hSerial, &dwErrorFlags, &comstat);
int iRepeat = 100;
int iLastInQue = 0;
while (iRepeat--)
{
iLastInQue = comstat.cbInQue;
ClearCommError (hSerial, &dwErrorFlags, &comstat);
if (comstat.cbInQue > 0 &&
comstat.cbInQue == iLastInQue)
{
break;
}
Sleep (10);
}
dwBytesRead = (DWORD) comstat.cbInQue;
ReadFile (hSerial, (void*)szBuffer, dwBytesRead, &dwBytesRead, NULL);
puts (szBuffer);
CloseHandle (hSerial);
return 0;
}
class Program
{
#region DllImport statements
[DllImport ("Kernel32.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)]
private static extern UIntPtr CreateFileW (string lpFileName, UInt32 dwDesiredAccess, UInt32 dwShareMode, UIntPtr lpSecurityAttributes,
UInt32 dwCreationDisposition, UInt32 dwFlagsAndAttributes, UIntPtr hTemplateFile);
[StructLayout (LayoutKind.Sequential)]
private struct COMSTAT
{
public uint uiCtsHold;
public uint uiDsrHold;
public uint uiRlsdHold;
public uint uiXoffHold;
public uint uiXoffSent;
public uint uiEof;
public uint uiTxim;
public UInt32 uiFlags;
public UInt32 cbInQue;
public UInt32 cbOutQue;
}
[DllImport ("Kernel32.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)]
private static extern int ClearCommError (UIntPtr hFile, out UInt32 lpErrors, out COMSTAT lpStat);
[DllImport ("Kernel32.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)]
private static extern int CloseHandle (UIntPtr hObject);
[DllImport ("Kernel32.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)]
private static extern int FlushFileBuffers (UIntPtr hFile);
[StructLayout (LayoutKind.Sequential)]
public struct DCB
{
public UInt32 DCBlength;
public UInt32 BaudRate;
public UInt32 uiFlagBits;
public UInt16 wReserved;
public UInt16 XonLim;
public UInt16 XoffLim;
public byte ByteSize;
public byte Parity;
public byte StopBits;
public char XonChar;
public char XoffChar;
public char ErrorChar;
public char EofChar;
public char EvtChar;
public UInt16 wReserved1;
}
[DllImport ("Kernel32.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)]
private static extern int GetCommState (UIntPtr hFile, out DCB lpDCB);
[DllImport ("Kernel32.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)]
private static extern int ReadFile (UIntPtr hFile, out string lpBuffer, UInt32 nNumberOfBytesToRead, out UInt32 lpNumberOfBytesRead, UIntPtr lpOverlapped);
[DllImport ("Kernel32.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)]
private static extern int SetCommState (UIntPtr hFile, ref DCB lpDCB);
private class COMMTIMEOUTS
{
public UInt32 ReadIntervalTimeout = 0;
public UInt32 ReadTotalTimeoutMultiplier = 0;
public UInt32 ReadTotalTimeoutConstant = 0;
public UInt32 WriteTotalTimeoutMultiplier = 0;
public UInt32 WriteTotalTimeoutConstant = 0;
}
[DllImport ("Kernel32.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)]
private static extern int SetCommTimeouts (UIntPtr hFile, ref COMMTIMEOUTS lpCommTimeouts);
[DllImport ("Kernel32.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)]
private static extern int WriteFile (UIntPtr hFile, string lpBuffer, UInt32 nNumberOfBytesToWrite, out UInt32 lpNumberOfBytesWritten, UIntPtr lpOverlapped);
#endregion
static void Main (string[] args)
{
const uint OPEN_EXISTING = 3;
const uint GENERIC_READ = 0x80000000;
const uint GENERIC_WRITE = 0x40000000;
const uint FILE_ATTRIBUTE_NORMAL = 0x00000080;
const int NOPARITY = 0;
const int ONESTOPBIT = 0;
UIntPtr hSerial = CreateFileW ("COM3",
GENERIC_READ | GENERIC_WRITE,
0,
(UIntPtr)null,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
(UIntPtr)null);
DCB dcbSerialParams = new DCB ();
COMMTIMEOUTS timeouts = new COMMTIMEOUTS ();
dcbSerialParams.DCBlength = (uint)Marshal.SizeOf (dcbSerialParams);
if (GetCommState (hSerial, out dcbSerialParams) == 0)
{
CloseHandle (hSerial);
return;
}
dcbSerialParams.BaudRate = 9600;
dcbSerialParams.ByteSize = 8;
dcbSerialParams.StopBits = ONESTOPBIT;
dcbSerialParams.Parity = NOPARITY;
if (SetCommState (hSerial, ref dcbSerialParams) == 0)
{
CloseHandle (hSerial);
return;
}
timeouts.ReadIntervalTimeout = 50;
timeouts.ReadTotalTimeoutConstant = 50;
timeouts.ReadTotalTimeoutMultiplier = 10;
timeouts.WriteTotalTimeoutConstant = 50;
timeouts.WriteTotalTimeoutMultiplier = 10;
if (SetCommTimeouts (hSerial, ref timeouts) == 0)
{
CloseHandle (hSerial);
return;
}
UInt32 dwBytesWritten = 0;
int iResult = WriteFile (hSerial, "OI;", 3, out dwBytesWritten, (UIntPtr)null);
FlushFileBuffers (hSerial);
string strBuffer;
UInt32 dwBytesRead = 0,
dwErrorFlags = 0;
COMSTAT comstat;
ClearCommError (hSerial, out dwErrorFlags, out comstat);
int iRepeat = 100;
int iLastInQue = 0;
while (iRepeat-- > 0)
{
iLastInQue = (int)comstat.cbInQue;
ClearCommError (hSerial, out dwErrorFlags, out comstat);
if (comstat.cbInQue > 0 &&
comstat.cbInQue == iLastInQue)
{
break;
}
Thread.Sleep (10);
}
dwBytesRead = (UInt32)comstat.cbInQue;
ReadFile (hSerial, out strBuffer, dwBytesRead, out dwBytesRead, (UIntPtr)null);
Console.WriteLine (strBuffer);
CloseHandle (hSerial);
}
}