⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 comm.cpp

📁 串口调试助手的源代码
💻 CPP
📖 第 1 页 / 共 3 页
字号:
		{
			TRACE0("Read thread not exiting. Terminating it.\n");

//			::TerminateThread(m_hReadThread, 0);
			::TerminateThread(m_pReadThread->m_hThread, 0);

			// The ReadThread cleans up these itself if it terminates normally.
//			::CloseHandle(m_hReadThread);
//			m_hReadThread = 0;
//			m_dwReadThreadID = 0;
		}
		delete  m_pReadThread;
		m_pReadThread = NULL;
	}
}

// Closes the write thread by signaling the CloseEvent.
// Purges any outstanding writes on the comm port.
//
// Note that terminating a thread leaks memory (read the docs).
// Besides the normal leak incurred, there is an event object
// that doesn't get closed.  This isn't worth worrying about
// since it shouldn't happen anyway.
void	CCommInfo::CloseWriteThread(void)
{
	// If it exists...
//	if (m_hWriteThread)
	if (m_pWriteThread)
	{
		TRACE0("Closing Write Thread\n");

		// Signal the event to close the worker threads.
		::SetEvent(m_hCloseEvent);
		// Purge all outstanding writes.
		::PurgeComm(m_hCommFile, PURGE_TXABORT | PURGE_TXCLEAR);

		// Wait 10 seconds for it to exit. Shouldn't happen.
//		if (::WaitForSingleObject(m_hWriteThread, 10000) == WAIT_TIMEOUT)
		if (::WaitForSingleObject(m_pWriteThread->m_hThread, 10000) == WAIT_TIMEOUT)
		{
			TRACE0("Write thread not exiting. Terminating it.\n");

//			::TerminateThread(m_hWriteThread, 0);
			::TerminateThread(m_pWriteThread->m_hThread, 0);

			// The WriteThread cleans up these itself if it terminates normally.
//			::CloseHandle(m_hWriteThread);
//			m_hWriteThread = 0;
//			m_dwWriteThreadID = 0;
		}
		delete  m_pWriteThread;
		m_pWriteThread = NULL;
	}
}

// This is the starting point for the Read Thread.
// The Read Thread uses overlapped ReadFile and sends any strings
// read from the comm port to the UI to be printed.  This is
// eventually done through a PostMessage so that the Read Thread
// is never away from the comm port very long.  This also provides
// natural desynchronization between the Read thread and the UI.
//
// If the CloseEvent object is signaled, the Read Thread exits.
//
// Separating the Read and Write threads is natural for a application
// like this sample where there is no need for synchronization between
// reading and writing.  However, if there is such a need (for example,
// most file transfer algorithms synchronize the reading and writing),
// then it would make a lot more sense to have a single thread to handle
// both reading and writing.
DWORD	CCommInfo::ReadThreadProc(LPVOID  lpvParam)
{
	CCommInfo*	pComm = (CCommInfo*)lpvParam;
	if (pComm == NULL ||
		!pComm->IsKindOf(RUNTIME_CLASS(CCommInfo)))
		return  -1;	// illegal parameter

	// Needed for overlapped I/O (ReadFile)
	OVERLAPPED	overlappedRead = {0, 0, 0, 0, NULL};
	// Needed for overlapped Comm Event handling.
	OVERLAPPED	overlappedCommEvent = {0, 0, 0, 0, NULL};

	// Lets put an event in the Read overlapped structure.
	overlappedRead.hEvent = ::CreateEvent(
		NULL,	// no security
		TRUE,	// explicit reset req
		TRUE,	// initial event set
		NULL);	// no name
	if (overlappedRead.hEvent == NULL)
	{
		TRACE0("Unable to CreateEvent. \n");

		pComm->PostCommError();
		goto  LABEL_ENDREADTHREAD;
	}

// ?????
	if (!::SetCommMask(pComm->m_hCommFile, EV_RXCHAR))
	{
		TRACE0("Unable to SetCommMask: EV_ERR\n");
		pComm->PostCommError();
		goto  LABEL_ENDREADTHREAD;
	}
// ?????

	// And an event for the CommEvent overlapped structure.
	overlappedCommEvent.hEvent = CreateEvent(
		NULL,	// no security
		TRUE,	// explicit reset req
		TRUE,	// initial event set
		NULL);	// no name
	if (overlappedCommEvent.hEvent == NULL)
	{
		TRACE0("Unable to CreateEvent. \n");
		pComm->PostCommError();
		goto  LABEL_ENDREADTHREAD;
	}

	// We will be waiting on these objects.
	HANDLE	HandlesToWaitFor[3];
	HandlesToWaitFor[0] = pComm->m_hCloseEvent;
	HandlesToWaitFor[1] = overlappedCommEvent.hEvent;
	HandlesToWaitFor[2] = overlappedRead.hEvent;

	// Setup CommEvent handling.
	// Set the comm mask so we receive error signals.
	if (!::SetCommMask(pComm->m_hCommFile, EV_ERR))
	{
		TRACE0("Unable to SetCommMask: EV_ERR\n");
		pComm->PostCommError();
		goto  LABEL_ENDREADTHREAD;
	}

	// Start waiting for CommEvents (Errors)
	DWORD	fdwEvtMask;
	if (!pComm->SetupWaitCommEvent(&overlappedCommEvent, &fdwEvtMask))
	{
		pComm->PostCommError();
		goto  LABEL_ENDREADTHREAD;
	}

	// Start waiting for Read events.
	DWORD	nNumberOfBytesRead;
	if (!pComm->SetupReadEvent(&overlappedRead,
		&nNumberOfBytesRead))
	{
		pComm->PostCommError();
		goto  LABEL_ENDREADTHREAD;
	}

	// Keep looping until we break out.
	while (TRUE)
	{
		// Wait until some event occurs (data to read; error; stopping).
		DWORD	dwHandleSignaled = 
			::WaitForMultipleObjects(3, HandlesToWaitFor,
				FALSE, INFINITE);

		// Which event occured?
		switch (dwHandleSignaled)
		{
		case WAIT_OBJECT_0:	// Signal to end the thread.
			// Time to exit.
			goto  LABEL_ENDREADTHREAD;

		case WAIT_OBJECT_0 + 1:	// CommEvent signaled.
			// Handle the CommEvent.
			if (!pComm->HandleCommEvent(&overlappedCommEvent, &fdwEvtMask, TRUE))
			{
				pComm->PostCommError();
				goto  LABEL_ENDREADTHREAD;
			}

			// Start waiting for the next CommEvent.
			if (!pComm->SetupWaitCommEvent(&overlappedCommEvent, &fdwEvtMask))
			{
				pComm->PostCommError();
				goto  LABEL_ENDREADTHREAD;
			}
			break;

		case WAIT_OBJECT_0 + 2: // Read Event signaled.
			// Get the new data!
			if (!pComm->HandleReadEvent(&overlappedRead,
				&nNumberOfBytesRead))
			{
				pComm->PostCommError();
				goto  LABEL_ENDREADTHREAD;
			}

			// Wait for more new data.
			if (!pComm->SetupReadEvent(&overlappedRead,
				&nNumberOfBytesRead))
			{
				pComm->PostCommError();
				goto  LABEL_ENDREADTHREAD;
			}
			break;

		case WAIT_FAILED:	// Wait failed. Shouldn't happen.
			TRACE1("Read WAIT_FAILED %lu: \n", ::GetLastError());
			{
				LPVOID	lpMsgBuf;
				::FormatMessage(
					FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
					NULL,
					::GetLastError(),
					MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
					(LPTSTR)&lpMsgBuf,
					0,
					NULL);

				// Display the string.
				TRACE0((LPCTSTR)lpMsgBuf);
				TRACE0("\n");

				// Free the buffer.
				::LocalFree(lpMsgBuf);
			}
 
			pComm->PostCommError();
			break;
//			goto  LABEL_ENDREADTHREAD;

		default:	// This case should never occur.
			TRACE1("Unexpected Wait return value '%lx'", dwHandleSignaled);
			pComm->PostCommError();
			goto  LABEL_ENDREADTHREAD;
		}	// End of switch(dwHandleSignaled).
	}	// End of while(TRUE) loop.

	// Time to clean up Read Thread.
LABEL_ENDREADTHREAD:

	TRACE0("Read thread shutting down\n");
	::PurgeComm(pComm->m_hCommFile, PURGE_RXABORT | PURGE_RXCLEAR);
	if (overlappedRead.hEvent)
		::CloseHandle(overlappedRead.hEvent);
	if (overlappedCommEvent.hEvent)
		::CloseHandle(overlappedCommEvent.hEvent);

//	pComm->m_pReadThread = NULL;
	::AfxEndThread(0);

//	pComm->m_dwReadThreadID = 0;
//	::CloseHandle(pComm->m_hReadThread);
//	pComm->m_hReadThread = 0;
	return  0;
}

// Handle an outstanding Comm Event.
// (if bRetrieveEvent == TRUE) retrieves an outstanding CommEvent and
// deals with it.  The only event that should occur is an EV_ERR event,
// signalling that there has been an error on the comm port.
//
// Normally, comm errors would not be put into the normal data stream
// as this sample is demonstrating. Putting it in a status bar would
// be more appropriate for a real application.
BOOL	CCommInfo::HandleCommEvent(LPOVERLAPPED  lpOverlappedCommEvent,
	LPDWORD  lpfdwEvtMask, BOOL  bRetrieveEvent)
{
	// 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)
	{
		DWORD	dwDummy;
		if (!::GetOverlappedResult(m_hCommFile, lpOverlappedCommEvent, &dwDummy, FALSE))
		{
			switch (::GetLastError())
			{
			// Its possible for this error to occur if the 
			// service provider has closed the port.  Time to end.
			case  ERROR_INVALID_HANDLE:
				TRACE0("ERROR_INVALID_HANDLE, Likely that the Service Provider has closed the port.\n");
				return  FALSE;

			default:
				TRACE0("Unexpected GetOverlappedResult for WaitCommEvent: \n");
				return  FALSE;
			}
		}
	}

	// Was the event an error?
	if (*lpfdwEvtMask & EV_ERR)
	{
		// Which error was it?
		DWORD	dwErrors;
		if (!::ClearCommError(m_hCommFile, &dwErrors, NULL))
		{
			switch (::GetLastError())
			{
			// Its possible for this error to occur if the 
			// service provider has closed the port.  Time to end.
			case  ERROR_INVALID_HANDLE:
				TRACE0("ERROR_INVALID_HANDLE, Likely that the Service Provider has closed the port.\n");
				return  FALSE;
			
			default:
				TRACE0("ClearCommError: \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.
		TCHAR	szError[128] = "";
		if (dwErrors != 0)
		{
			if (dwErrors & CE_FRAME)
			{
				if (szError[0])
					strcat(szError," and ");

				strcat(szError,"CE_FRAME");
			}

			if (dwErrors & CE_OVERRUN)
			{
				if (szError[0])
					strcat(szError," and ");

				strcat(szError,"CE_OVERRUN");
			}

			if (dwErrors & CE_RXPARITY)
			{
				if (szError[0])
					strcat(szError," and ");

				strcat(szError,"CE_RXPARITY");
			}

			if (dwErrors & ~(CE_FRAME | CE_OVERRUN | CE_RXPARITY))
			{
				if (szError[0])
					strcat(szError," and ");

				strcat(szError,"EV_ERR Unknown EvtMask");
			}
		}
		else
		{
			strcat(szError, "NULL Error");
		}
		TRACE2("Comm Event: '%s', EvtMask = '%lx'\n",  szError, dwErrors);
		return  TRUE;
	}

	// Should not have gotten here.  Only interested in ERR conditions.
	TRACE1("Unexpected comm event %lx\n", *lpfdwEvtMask);
	return  FALSE;
}

// Sets up the overlapped WaitCommEvent call.
// This function is a helper function for the Read Thread that sets up
// the WaitCommEvent so we can deal with comm events (like Comm errors)
// if they occur.
BOOL	CCommInfo::SetupWaitCommEvent(LPOVERLAPPED  lpOverlappedCommEvent,
	LPDWORD  lpfdwEvtMask)
{
//LABEL_STARTSETUPCOMMEVENT:
	while (TRUE)
	{
		// Make sure the CloseEvent hasn't been signaled yet.
		// Check is needed because this function is potentially recursive.
		if (::WaitForSingleObject(m_hCloseEvent, 0) != WAIT_TIMEOUT)
			return  FALSE;

		// Start waiting for Comm Errors.
		if (::WaitCommEvent(m_hCommFile, lpfdwEvtMask, lpOverlappedCommEvent))
		{
			// This could happen if there was an error waiting on the
			// comm port.  Lets try and handle it.
			TRACE0("Event (Error) waiting before WaitCommEvent.\n");

			if (!HandleCommEvent(NULL, lpfdwEvtMask, FALSE))
				return  FALSE;

			// What could cause infinite recursion at this point?
//			goto  LABEL_STARTSETUPCOMMEVENT;
		}
		else
			break;
	}

	// We expect ERROR_IO_PENDING returned from WaitCommEvent
	// because we are waiting with an overlapped structure.
	switch (::GetLastError())
	{
	// LastError was ERROR_IO_PENDING, as expected.
	case ERROR_IO_PENDING:
		TRACE0("Waiting for a CommEvent (Error) to occur.\n");
		return  TRUE;

	// Its possible for this error to occur if the 
	// service provider has closed the port. Time to end.
	case ERROR_INVALID_HANDLE:
		TRACE0("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.
	default:
		TRACE0("Unexpected WaitCommEvent error !");
		return  FALSE;
	}
}

// The starting point for the Write thread.
// The Write thread uses a PeekMessage loop to wait for a string to write,
// and when it gets one, it writes it to the Comm port.  If the CloseEvent

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -