// CommPort.cpp: implementation of the CCommPort class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "CommPort.h"

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

CCommPort::CCommPort()
{
	m_hCommFile = INVALID_HANDLE_VALUE;
	m_strDeviceName = "";
	m_pOverlappedRead = NULL;
}

CCommPort::~CCommPort()
{
	if (m_hCommFile != INVALID_HANDLE_VALUE)
	{
		Close();
	}
	if (m_pOverlappedRead != NULL)
	{
//		OutputDebugString("free overlapped read buffer\n");
		free(m_pOverlappedRead);
	}
}

BOOL CCommPort::Open(HWND hWnd, int iBaudRate, CString strDeviceName, UINT uMsg, HANDLE hCommFile)
{
	m_hCommFile = hCommFile;
	if (m_hCommFile == INVALID_HANDLE_VALUE)
	{
		m_hCommFile = CreateFile(strDeviceName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 
									FILE_FLAG_OVERLAPPED|FILE_FLAG_WRITE_THROUGH|FILE_FLAG_NO_BUFFERING, NULL);
		if (m_hCommFile == INVALID_HANDLE_VALUE) 
		{
			goto Error;
		}
	}
	else
	{
		if (GetFileType(m_hCommFile) != FILE_TYPE_CHAR)		// Is this a valid comm handle?
		{
			m_hCommFile = INVALID_HANDLE_VALUE;
			goto Error;
		}
	}
	m_strDeviceName = strDeviceName;

	if (Prepare(hWnd, iBaudRate, uMsg) == FALSE)
	{
		goto Error;
	}

	if (!SetState())
	{
		goto Error;
	}

	m_hReadThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ReadThreadFunc, this, 0, &m_dwReadThreadId);
	if (m_hReadThread == NULL) 
	{
		OutputDebugString("CreateThread() failed in CCommPort::Open()\n");
		goto Error;
	}
	SetThreadPriority(m_hReadThread, THREAD_PRIORITY_HIGHEST);

	m_hWriteThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)WriteThreadFunc, this, 0, &m_dwWriteThreadId);
	if (m_hWriteThread == NULL) 
	{
		OutputDebugString("CreateThread() failed in CCommPort::Open()\n");
		goto Error;
	}
	SetThreadPriority(m_hWriteThread, THREAD_PRIORITY_ABOVE_NORMAL);

	return TRUE;

Error:
	Close();
	return FALSE;
}

void CCommPort::Close()
{
	if (m_hCommFile != INVALID_HANDLE_VALUE)
	{
		if (m_hReadThread != NULL)
		{
			// Signal the event to close the worker threads.
			SetEvent(m_hQuitEvent);
			PurgeComm(m_hCommFile, PURGE_RXABORT|PURGE_RXCLEAR);
			if (WaitForSingleObject(m_hReadThread, 3000) == WAIT_TIMEOUT)
			{
				OutputDebugString("Force to close CommPort read thread\n");
				TerminateThread(m_hReadThread, 0);
				CloseHandle(m_hReadThread);
			}
			m_hReadThread = NULL;
		}
		if (m_hWriteThread != NULL)
		{
			// Signal the event to close the worker threads.
			SetEvent(m_hQuitEvent);
			PurgeComm(m_hCommFile, PURGE_TXABORT|PURGE_TXCLEAR);
			if (WaitForSingleObject(m_hWriteThread, 3000) == WAIT_TIMEOUT)
			{
				OutputDebugString("Force to close CommPort write thread\n");
				TerminateThread(m_hWriteThread, 0);
				CloseHandle(m_hWriteThread);
			}
			m_hWriteThread = NULL;
		}
		Unprepare();

		if (m_strDeviceName != "")	 
		{	// So this handle is OPENED by us in Open()
			CloseHandle(m_hCommFile);
		}
		m_hCommFile = INVALID_HANDLE_VALUE;
		m_strDeviceName = "";
	}	
}

BOOL CCommPort::Write(char * pData, int iLength)
{
	if (m_hCommFile == INVALID_HANDLE_VALUE)	return FALSE;
//	if (m_hOutputEvent == NULL)	return FALSE;

	CSingleLock sLock(&m_critical);
	Data * p = (Data *)malloc(sizeof(Data));
	if (p == NULL)
	{
		OutputDebugString("malloc failed in CCommPort::Write\n");
		return FALSE;
	}
	p->pData = (char *)malloc(COMMPORT_DATA_SIZE);
	if (p->pData == NULL)
	{
		free(p);
		OutputDebugString("malloc failed in CCommPort::Write\n");
		return FALSE;
	}
	memcpy(p->pData, pData, iLength);
	p->iLength = iLength;
	sLock.Lock();
	m_sendQueue.AddTail(p);
	sLock.Unlock();
	SetEvent(m_hOutputEvent);
	return TRUE;
}

BOOL CCommPort::Put(char ch)
{
	if (m_hCommFile == INVALID_HANDLE_VALUE)	return FALSE;
//	if (m_hOutputEvent == NULL)	return FALSE;

	CSingleLock sLock(&m_critical);
	Data * p = (Data *)malloc(sizeof(Data));
	if (p == NULL)
	{
		OutputDebugString("malloc failed in CCommPort::Write\n");
		return FALSE;
	}
	p->pData = (char *)malloc(1);
	if (p->pData == NULL)
	{
		free(p);
		OutputDebugString("malloc failed in CCommPort::Write\n");
		return FALSE;
	}
	p->pData[0] = ch;
	p->iLength = 1;
	sLock.Lock();
	m_sendQueue.AddTail(p);
	sLock.Unlock();
	SetEvent(m_hOutputEvent);
	return TRUE;
}

BOOL CCommPort::Busy()
{
	return (m_sendQueue.GetCount() > COMMPORT_QUEUE_SIZE);
}

int CCommPort::GetBaudRate()
{
	return m_iBaudRate;
}

BOOL CCommPort::Prepare(HWND hWnd, int iBaudRate, UINT uMsg)
{
	m_hReadThread = m_hWriteThread = m_hQuitEvent = m_hOutputEvent = NULL;
	m_hWnd = hWnd;
	m_uMsg = uMsg;
	m_iBaudRate = iBaudRate;

	if (!PrepareOverlapped(&m_overlappedRead) || !PrepareOverlapped(&m_overlappedWrite) || !PrepareOverlapped(&m_overlappedCommEvent)) 
	{
		goto Error;
	}

	m_hOutputEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
	m_hQuitEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
	if (!m_hOutputEvent || !m_hQuitEvent)
	{
		OutputDebugString("CreateEvent() failed in CCommPort::prepare()\n");
		goto Error;
	}

	return TRUE;

Error:
	Unprepare();
	return FALSE;
}

void CCommPort::Unprepare()
{
	if (m_hQuitEvent != NULL)
	{
		CloseHandle(m_hQuitEvent);
		m_hQuitEvent = NULL;
	}
	if (m_hOutputEvent != NULL)
	{
		CloseHandle(m_hOutputEvent);
		m_hOutputEvent = NULL;
	}
	UnprepareOverlapped(&m_overlappedWrite);
	UnprepareOverlapped(&m_overlappedRead);
	UnprepareOverlapped(&m_overlappedCommEvent);
	while (!m_sendQueue.IsEmpty())
	{
		Data * p = m_sendQueue.RemoveHead();
		free(p->pData);
		free(p);
	}
}

BOOL CCommPort::PrepareOverlapped(OVERLAPPED * p)
{
	p->Internal = 0;
	p->InternalHigh = 0;
	p->Offset = 0;
	p->OffsetHigh = 0;
	p->hEvent = NULL; 
	p->hEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
	if (p->hEvent == NULL)
	{
		OutputDebugString("Unable to CreateEvent in CCommPort::PrepareOverlapped\n");
		return FALSE;
	}
	return TRUE;
}

void CCommPort::UnprepareOverlapped(OVERLAPPED * p)
{
	if (p->hEvent != NULL)
	{
		CloseHandle(p->hEvent);
		p->hEvent = NULL;
	}
}

DWORD WINAPI CCommPort::ReadThreadFunc(LPVOID pParam)
{
	DWORD dwWaitStatus;	// holds return value of the wait
	CCommPort * p = (CCommPort *)pParam;
	HANDLE eventArray[3];	// The array of events which the read thread is currently waiting on.

	eventArray[0] = p->m_hQuitEvent;
	eventArray[1] = p->m_overlappedCommEvent.hEvent;
	eventArray[2] = p->m_overlappedRead.hEvent;

	if (!p->SetupCommEvent() || !p->SetupReadEvent())	// Start waiting for comm events (Errors) and read events
	{
		goto EndThread;
	}

	OutputDebugString("CommPort read thread starting\n");

	// This is the main loop.  Loop until we break out.
	while (TRUE) 
	{
		// Wait for an event 
		dwWaitStatus = WaitForMultipleObjects(3, eventArray, FALSE, INFINITE);
		switch (dwWaitStatus) 
		{
		case WAIT_FAILED:	// A fatal error
			OutputDebugString("WaitForMultipleObjects() failed in CommPort read thread\n");
			PostMessage(p->m_hWnd, WM_COMMPORT_ERROR, 0, 0);
			break;

		case WAIT_OBJECT_0:	// Quit time;
			break;

		case WAIT_OBJECT_0+1:	// Overlapped Comm Event;
			if (p->HandleCommEvent(TRUE))
			{
				if (p->SetupCommEvent())
				{
					continue;
				}
			}
			PostMessage(p->m_hWnd, WM_COMMPORT_ERROR, 0, 0);
			break;

		case WAIT_OBJECT_0+2:	// Overlapped Read;
			if (p->HandleReadEvent())
			{
				if (p->SetupReadEvent())
				{
					continue;
				}
			}
			PostMessage(p->m_hWnd, WM_COMMPORT_ERROR, 0, 0);
			break;

		default:
			OutputDebugString("Unknown error: WaitForMultipleObjects() in CommPort read thread\n");
			PostMessage(p->m_hWnd, WM_COMMPORT_ERROR, 0, 0);
			break;
		} // switch (dwWaitStatus)
		// Break out of the while loop.
		break;
	} // while (1)

	// Thats the end.  Now clean up.
EndThread:
	OutputDebugString("CommPort read thread shutting down\n");
	CloseHandle(p->m_hReadThread);
	return 0;
}

DWORD WINAPI CCommPort::WriteThreadFunc(LPVOID pParam)
{
	DWORD dwWaitStatus;	// holds return value of the wait
	CCommPort * p = (CCommPort *)pParam;
	HANDLE eventArray[2];	// The array of events which the write thread is currently waiting on.

	eventArray[0] = p->m_hQuitEvent;
	eventArray[1] = p->m_hOutputEvent;
//	eventArray[2] = p->m_overlappedWrite.hEvent;

	OutputDebugString("CommPort write thread starting\n");

	// This is the main loop.  Loop until we break out.
	while (TRUE) 
	{
		// Wait for an event 
		dwWaitStatus = WaitForMultipleObjects(2, eventArray, FALSE, INFINITE);
		switch (dwWaitStatus) 
		{
		case WAIT_FAILED:	// A fatal error
			OutputDebugString("WaitForMultipleObjects() failed in CommPort write thread\n");
			PostMessage(p->m_hWnd, WM_COMMPORT_ERROR, 0, 0);
			break;

		case WAIT_OBJECT_0:	// Quit time;
			break;

		case WAIT_OBJECT_0+1:	// The hOutputEvent has been signaled.
			if (p->HandleWriteEvent())
			{
				continue;
			}
			PostMessage(p->m_hWnd, WM_COMMPORT_ERROR, 0, 0);
			break;

		default:
			OutputDebugString("Unknown error: WaitForMultipleObjects() in CommPort write thread\n");
			PostMessage(p->m_hWnd, WM_COMMPORT_ERROR, 0, 0);
			break;
		} // switch (dwWaitStatus)
		// Break out of the while loop.
		break;
	} // while (1)

	// Thats the end.  Now clean up.
//EndThread:
	OutputDebugString("CommPort write thread shutting down\n");
	CloseHandle(p->m_hWriteThread);
	return 0;
}

BOOL CCommPort::SetState()
{
	COMMPROP commprop;
	COMMTIMEOUTS commtimeouts;
	DCB dcb;

	if (!PurgeComm(m_hCommFile, PURGE_TXABORT|PURGE_RXABORT|PURGE_TXCLEAR|PURGE_RXCLEAR))
	{
		OutputDebugString("Purge Comm fail\n");
		return FALSE;
	}

	if (!GetCommProperties(m_hCommFile, &commprop))
	{
		OutputDebugString("Get Comm Properties fail\n");
		return FALSE;
	}

	if (!SetupComm(m_hCommFile, COMMPORT_DATA_SIZE, COMMPORT_DATA_SIZE))	
	{
		OutputDebugString("Setup Comm fail\n");
		return FALSE;
	}

	commtimeouts.ReadIntervalTimeout         = 0;
	commtimeouts.ReadTotalTimeoutMultiplier  = 0;
	commtimeouts.ReadTotalTimeoutConstant    = 1;
	commtimeouts.WriteTotalTimeoutMultiplier = 0;
	commtimeouts.WriteTotalTimeoutConstant   = 1;
	if (!SetCommTimeouts(m_hCommFile, &commtimeouts))
	{
		OutputDebugString("Set Comm Timeout fail\n");
		return FALSE;
	}

	if (!GetCommState(m_hCommFile, &dcb))
	{
		OutputDebugString("Get Comm State fail\n");
		return FALSE;
	}
	dcb.fAbortOnError	= FALSE;
	dcb.BaudRate		= m_iBaudRate;
	dcb.fBinary			= TRUE;
	dcb.StopBits		= ONESTOPBIT;
	dcb.Parity			= NOPARITY;
	dcb.fParity			= FALSE;
	dcb.fNull			= FALSE;
	dcb.fOutxCtsFlow	= FALSE;
	dcb.fOutxDsrFlow	= FALSE;
	dcb.fDtrControl		= DTR_CONTROL_DISABLE;
	dcb.fDsrSensitivity	= FALSE;
	dcb.fTXContinueOnXoff = FALSE;
	dcb.fOutX			= FALSE;
	dcb.fInX			= FALSE;
	dcb.fErrorChar		= FALSE;
	dcb.fRtsControl		= RTS_CONTROL_DISABLE;
	if (!SetCommState(m_hCommFile, &dcb))
	{
		OutputDebugString("Set Comm State fail\n");
		return FALSE;
	}

	if (!SetCommMask(m_hCommFile, EV_ERR))	// Set the comm mask so we receive error signals.
	{
		OutputDebugString("Unable to SetCommMask: EV_ERR\n");
		return FALSE;
	}

	return TRUE;
}

BOOL CCommPort::SetupReadEvent()
{
	DWORD dwLastError, dwRead;
	char * pData;

	while (TRUE)
	{
		// Make sure the CloseEvent hasn't been signaled yet. Check is needed because this function is potentially recursive.
		if (WAIT_TIMEOUT != WaitForSingleObject(m_hQuitEvent, 0))
		{
			return FALSE;
		}
  
		// Start the overlapped ReadFile.
		pData = (char *)malloc(COMMPORT_DATA_SIZE);
		if (pData == NULL)	return FALSE;
		if (ReadFile(m_hCommFile, pData, COMMPORT_DATA_SIZE, &dwRead, &m_overlappedRead))
		{	// This would only happen if there was data waiting to be read.
			OutputDebugString("Data waiting for ReadFile.\n");
			// Handle the data.
			if (dwRead > 0)
			{
//				CString temp;
//				temp.Format("Read %d bytes\n", dwRead);
//				OutputDebugString(temp);
				PostMessage(m_hWnd, m_uMsg, (WPARAM)dwRead, (LPARAM)pData);
			}
			else
			{
				free(pData);
			}
		}
		else
			break;
	}

	// ReadFile failed.  Expected because of overlapped I/O.
	dwLastError = GetLastError();
	if (dwLastError == ERROR_IO_PENDING)    // LastError was ERROR_IO_PENDING, as expected.
	{
		if (m_pOverlappedRead != NULL)
			free(m_pOverlappedRead);
		m_pOverlappedRead = pData;
		return TRUE;
	}
	else if (dwLastError == ERROR_INVALID_HANDLE)
	{	// Its possible for this error to occur if the service provider has closed the port.  Time to end.
		OutputDebugString("ERROR_INVALID_HANDLE: Likely that the Service Provider has closed the port.\n");
	}

	// Unexpected error. No idea what could cause this to happen.
	OutputDebugString("Unexpected ReadFile error in CommPort read thread.\n");
	free(pData);
	return FALSE;
}

BOOL CCommPort::HandleReadEvent()
{
	DWORD dwLastError, dwRead;

	if (GetOverlappedResult(m_hCommFile, &m_overlappedRead, &dwRead, FALSE))
	{
		if (dwRead > 0)
		{
			PostMessage(m_hWnd, m_uMsg, (WPARAM)dwRead, (LPARAM)m_pOverlappedRead);
		}
		else
		{
			free(m_pOverlappedRead);
		}
		m_pOverlappedRead = NULL;
		return TRUE;
	}
	free(m_pOverlappedRead);
	m_pOverlappedRead = NULL;

	// Error in GetOverlappedResult; handle it.
	dwLastError = GetLastError();
	if (dwLastError == ERROR_INVALID_HANDLE)
	{	// Its possible for this error to occur if the service provider has closed the port.  Time to end.
		OutputDebugString("ERROR_INVALID_HANDLE: Likely that the Service Provider has closed the port.\n");
	}
	OutputDebugString("Unexpected GetOverlappedResult Read Error in CommPort read thread.\n");
	return FALSE;
}

BOOL CCommPort::SetupCommEvent()
{
	DWORD dwLastError;

	while (TRUE)
	{
		// Make sure the CloseEvent hasn't been signaled yet. Check is needed because this function is potentially recursive.
		if (WAIT_TIMEOUT != WaitForSingleObject(m_hQuitEvent, 0))
		{
			return FALSE;
		}
		
		// Start waiting for Comm Errors.
		if (WaitCommEvent(m_hCommFile, &m_dwEventMask, &m_overlappedCommEvent))
		{
			// This could happen if there was an error waiting on the comm port.  Lets try and handle it.
			OutputDebugString("Event (Error) waiting before WaitCommEvent.\n");
			if (!HandleCommEvent(FALSE))
			{
				return FALSE;
			}
			// What could cause infinite recursion at this point?
			continue;
		}
		break;
	}

	// We expect ERROR_IO_PENDING returned from WaitCommEvent because we are waiting with an overlapped structure.
	dwLastError = GetLastError();
	if (dwLastError == ERROR_IO_PENDING)
	{	// LastError was ERROR_IO_PENDING, as expected.
		OutputDebugString("Waiting for a CommEvent (Error) to occur.\n");
		return TRUE;
	}
	else if (dwLastError == ERROR_INVALID_HANDLE)
	{	// Its possible for this error to occur if the service provider has closed the port.  Time to end.
		OutputDebugString("ERROR_INVALID_HANDLE: Likely that the Service Provider has closed the port.\n");
		return FALSE;
	}
	// Unexpected error. No idea what could cause this to happen.
	OutputDebugString("Unexpected WaitCommEvent error in CommPort read thread.\n");
	return FALSE;
}

BOOL CCommPort::HandleCommEvent(BOOL bRetrieveEvent)
{
	DWORD dwDummy, dwErrors, dwLastError;
	CString strError = "Comm Event: ";

	// If this fails, it could be because the file was closed (and I/O is finished) or because the overlapped I/O
	// is still in progress. In either case (or any others) its a bug and return FALSE.
	if (bRetrieveEvent)
	{
		if (!GetOverlappedResult(m_hCommFile, &m_overlappedCommEvent, &dwDummy, FALSE))
		{
			dwLastError = GetLastError();
			if (dwLastError == ERROR_INVALID_HANDLE)
			{	// Its possible for this error to occur if the service provider has closed the port.  Time to end.
				OutputDebugString("ERROR_INVALID_HANDLE: Likely that the Service Provider has closed the port.\n");
				return FALSE;
			}
			OutputDebugString("Unexpected GetOverlappedResult for WaitCommEvent in CommPort IO threaed.\n");
			return FALSE;
		}
	}

	// Was the event an error?
	if (m_dwEventMask & EV_ERR)
	{
		// Which error was it?
		if (!ClearCommError(m_hCommFile, &dwErrors, NULL))
		{
			dwLastError = GetLastError();
			if (dwLastError == ERROR_INVALID_HANDLE)
			{	// Its possible for this error to occur if the service provider has closed the port.  Time to end.
				OutputDebugString("ERROR_INVALID_HANDLE: Likely that the Service Provider has closed the port.\n");
				return FALSE;
			}
			OutputDebugString("ClearCommError in CommPort read thread.\n");
			return FALSE;
		}

		// Its possible that multiple errors occured and were handled in the last ClearCommError.  Because all errors
		// were signaled individually, but cleared all at once, pending comm events can yield EV_ERR while dwErrors equals 0.  Ignore this event.
		if (dwErrors == 0)
		{
			strError += "NULL Error, ";
		}
		if (dwErrors & CE_FRAME)
		{
			strError += "CE_FRAME, ";
		}
		if (dwErrors & CE_OVERRUN)
		{
			strError += "CE_OVERRUN, ";
		}
		if (dwErrors & CE_RXPARITY)
		{
			strError += "CE_RXPARITY, ";
		}
		if (dwErrors & ~ (CE_FRAME | CE_OVERRUN | CE_RXPARITY))
        {
			strError += "EV_ERR Unknown EvtMask";
        }
		strError += "\n";
		OutputDebugString(strError);
		return TRUE;
	}

	// Should not have gotten here.  Only interested in ERR conditions.
	OutputDebugString("Unexpected comm event in CommPort read thread.\n");
	return FALSE;
}

BOOL CCommPort::HandleWriteEvent()
{
	DWORD dwHandleSignaled, dwLastError, dwNumberWritten = 0, dwStartWriting = 0;
	int iLength; // Start at the beginning.
	char * pData;
	HANDLE eventArray[2];	// The array of events which the write operation is currently waiting on.
	CSingleLock sLock(&m_critical);
	Data * p = NULL;

	sLock.Lock();
	if (!m_sendQueue.IsEmpty())
	{
		p = m_sendQueue.RemoveHead();
	}
	sLock.Unlock();

	if (p == NULL)
		return TRUE;

	iLength = p->iLength;
	pData = p->pData;

	eventArray[0] = m_hQuitEvent;
	eventArray[1] = m_overlappedWrite.hEvent;

	// Keep looping until all bytes have been written.
	do
	{
		// Start the overlapped I/O.
		if (!WriteFile(m_hCommFile, &pData[dwStartWriting], iLength, &dwNumberWritten, &m_overlappedWrite))
		{
			// WriteFile failed.  Expected; lets handle it.
			dwLastError = GetLastError();
			if (dwLastError == ERROR_INVALID_HANDLE)
			{	// Its possible for this error to occur if the service provider has closed the port.  Time to end.
				OutputDebugString("ERROR_INVALID_HANDLE: Likely that the Service Provider has closed the port.\n");
				goto Error;
			}
			if (dwLastError != ERROR_IO_PENDING)
			{	// Unexpected error.  No idea what.
				OutputDebugString("Error to writing to CommFile in CommPort write thread.\n");
				goto Error;
			}

			// This is the expected ERROR_IO_PENDING case.
			// Wait for either overlapped I/O completion, or for the CloseEvent to get signaled.
			dwHandleSignaled = WaitForMultipleObjects(2, eventArray, FALSE, INFINITE);
			switch (dwHandleSignaled)
			{
			case WAIT_OBJECT_0:     // CloseEvent signaled! Time to exit.
				goto Error;

			case WAIT_OBJECT_0 + 1: // Wait finished.  Time to get the results of the WriteFile
				break;

			case WAIT_FAILED: // Wait failed.  Shouldn't happen.
				OutputDebugString("Write WAIT_FAILED in CommPort write thread.\n");
				goto Error;

			default: // This case should never occur.
				OutputDebugString("Unexpected Wait return value in CommPort write thread.\n");
				goto Error;
			}

			if (!GetOverlappedResult(m_hCommFile, &m_overlappedWrite, &dwNumberWritten, TRUE))
			{
				dwLastError = GetLastError();
				if (dwLastError == ERROR_INVALID_HANDLE)
				{	// Its possible for this error to occur if the  service provider has closed the port.
					OutputDebugString("ERROR_INVALID_HANDLE: Likely that the Service Provider has closed the port.\n");
					goto Error;
				}
				// No idea what could cause another error.
				OutputDebugString("Error writing to CommFile while waiting in CommPort write thread.\n");
				goto Error;
			}
		}

		// Some data was written.  Make sure it all got written.
		iLength -= dwNumberWritten;
		dwStartWriting += dwNumberWritten;
	} while(iLength > 0);  // Write the whole thing!

	free(pData);
	free(p);

	sLock.Lock();
	if (!m_sendQueue.IsEmpty())
	{
		SetEvent(m_hOutputEvent);
	}
	sLock.Unlock();

	return TRUE;

Error:
	free(pData);
	free(p);
	return FALSE;
}
