📄 comm.cpp
字号:
// object is signaled, then it exits. The use of messages to tell the
// Write thread what to write provides a natural desynchronization between
// the UI and the Write thread.
DWORD CCommInfo::WriteThreadProc(LPVOID lpvParam)
{
CCommInfo* pComm = (CCommInfo*)lpvParam;
if (pComm == NULL ||
!pComm->IsKindOf(RUNTIME_CLASS(CCommInfo)))
return -1; // illegal parameter
// Needed for overlapped I/O.
OVERLAPPED overlappedWrite = {0, 0, 0, 0, NULL};
overlappedWrite.hEvent = ::CreateEvent(NULL, TRUE, TRUE, NULL);
if (overlappedWrite.hEvent == NULL)
{
TRACE0("Unable to CreateEvent: overlappedWrite\n");
pComm->PostCommError();
goto LABEL_ENDWRITETHREAD;
}
// This is the main loop. Loop until we break out.
while (TRUE)
{
MSG msg;
if (!::PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
// If there are no messages pending, wait for a message or the CloseEvent.
DWORD dwHandleSignaled =
::MsgWaitForMultipleObjects(1, &pComm->m_hCloseEvent, FALSE,
INFINITE, QS_ALLINPUT);
switch (dwHandleSignaled)
{
case WAIT_OBJECT_0: // CloseEvent signaled!
// Time to exit.
goto LABEL_ENDWRITETHREAD;
case WAIT_OBJECT_0 + 1: // New message was received.
// Get the message that woke us up by looping again.
continue;
case WAIT_FAILED: // Wait failed. Shouldn't happen.
TRACE0("Write WAIT_FAILED \n");
pComm->PostCommError();
goto LABEL_ENDWRITETHREAD;
default: // This case should never occur.
TRACE1("Unexpected Wait return value '%lx'", dwHandleSignaled);
pComm->PostCommError();
goto LABEL_ENDWRITETHREAD;
}
}
// Make sure the CloseEvent isn't signaled while retrieving messages.
if (::WaitForSingleObject(pComm->m_hCloseEvent, 0) != WAIT_TIMEOUT)
goto LABEL_ENDWRITETHREAD;
// Process the message.
// This could happen if a dialog is created on this thread.
// This doesn't occur in this sample, but might if modified.
if (msg.hwnd != NULL)
{
::TranslateMessage(&msg);
::DispatchMessage(&msg);
continue;
}
// Handle the message.
CString strWriteValue;
switch (msg.message)
{
case PWM_COMMWRITE: // New string to write to Comm port.
TRACE0("Writing to comm port\n");
// TRACE0((LPSTR)msg.lParam);
TRACE0("....\n");
// Write the string to the comm port. HandleWriteData
// does not return until the whole string has been written,
// an error occurs or until the CloseEvent is signaled.
if (!pComm->HandleWriteData(&overlappedWrite,
(LPSTR)msg.lParam, (DWORD)msg.wParam))
{
// If it failed, either we got a signal to end or there
// really was a failure.
goto LABEL_ENDWRITETHREAD;
}
break;
// What other messages could the thread get?
default:
TRACE1("Unexpected message posted to Write thread: %ui\n",
msg.message);
break;
} // End of switch(message)
} // End of main loop.
// Thats the end. Now clean up.
LABEL_ENDWRITETHREAD:
TRACE0("Write thread shutting down\n");
::PurgeComm(pComm->m_hCommFile, PURGE_TXABORT | PURGE_TXCLEAR);
::CloseHandle(overlappedWrite.hEvent);
// pComm->m_pWriteThread = NULL;
::AfxEndThread(0);
// pComm->m_dwWriteThreadID = 0;
// ::CloseHandle(pComm->m_hWriteThread);
// pComm->m_hWriteThread = 0;
return 0;
}
// Writes a given string to the comm file handle.
// It is this call that actually writes a string to the comm file.
// Note that this call blocks and waits for the Write to complete
// or for the CloseEvent object to signal that the thread should end.
// Another possible reason for returning FALSE is if the comm port
// is closed by the service provider.
BOOL CCommInfo::HandleWriteData(LPOVERLAPPED lpOverlappedWrite,
LPCSTR lpszStringToWrite, DWORD dwNumberOfBytesToWrite)
{
if (m_bRTS)
{
// special for connection with DIANTAI ?
// assert RTS
::EscapeCommFunction(m_hCommFile, SETRTS);
::Sleep(500);
}
HANDLE HandlesToWaitFor[2];
HandlesToWaitFor[0] = m_hCloseEvent;
HandlesToWaitFor[1] = lpOverlappedWrite->hEvent;
// Keep looping until all characters have been written.
DWORD dwNumberOfBytesWritten = 0;
DWORD dwWhereToStartWriting = 0; // Start at the beginning.
do
{
// Start the overlapped I/O.
if (!::WriteFile(m_hCommFile,
&lpszStringToWrite[dwWhereToStartWriting],
dwNumberOfBytesToWrite, &dwNumberOfBytesWritten,
lpOverlappedWrite))
{
// WriteFile failed. Expected; lets handle it.
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;
// This is the expected ERROR_IO_PENDING case.
case ERROR_IO_PENDING:
{
// Wait for either overlapped I/O completion,
// or for the CloseEvent to get signaled.
DWORD dwHandleSignaled = ::WaitForMultipleObjects(2, HandlesToWaitFor,
FALSE, INFINITE);
switch (dwHandleSignaled)
{
case WAIT_OBJECT_0: // CloseEvent signaled!
// Time to exit.
return FALSE;
case WAIT_OBJECT_0 + 1: // Wait finished.
// Time to get the results of the WriteFile
break;
case WAIT_FAILED: // Wait failed. Shouldn't happen.
TRACE0("Write WAIT_FAILED !\n");
PostCommError();
return FALSE;
default: // This case should never occur.
TRACE1("Unexpected Wait return value '%lx'",
dwHandleSignaled);
PostCommError();
return FALSE;
}
if (!::GetOverlappedResult(m_hCommFile,
lpOverlappedWrite,
&dwNumberOfBytesWritten, TRUE))
{
switch (::GetLastError())
{
// Its possible for this error to occur if the
// service provider has closed the port.
case ERROR_INVALID_HANDLE:
TRACE0("ERROR_INVALID_HANDLE, Likely that the Service Provider has closed the port.\n");
return FALSE;
default:
// No idea what could cause another error.
TRACE0("Error writing to CommFile while waiting\n");
TRACE0("Closing TAPI\n");
PostCommError();
return FALSE;
}
}
break;
}
// Unexpected error. No idea what.
default:
TRACE0("Error to writing to CommFile, Closing TAPI\n");
PostCommError();
return FALSE;
}
}
// Some data was written. Make sure it all got written.
dwNumberOfBytesToWrite -= dwNumberOfBytesWritten;
dwWhereToStartWriting += dwNumberOfBytesWritten;
}
while (dwNumberOfBytesToWrite > 0); // Write the whole thing!
// 1997.03.27
if (m_bRTS)
{
// special for connection with DIANTAI ?
::Sleep(500);
// drop RTS
::EscapeCommFunction(m_hCommFile, CLRRTS);
::Sleep(200);
}
// Wrote the whole string.
return TRUE;
}
// Sets up an overlapped ReadFile
// This function sets up the overlapped ReadFile so that it can later
// be waited on (or more appropriatly, so the event in the overlapped
// structure can be waited upon). If there is data waiting, it is
// handled and the next ReadFile is initiated.
// Another possible reason for returning FALSE is if the comm port
// is closed by the service provider.
BOOL CCommInfo::SetupReadEvent(LPOVERLAPPED lpOverlappedRead,
LPDWORD lpnNumberOfBytesRead)
{
// while (*((WORD*)m_pReadBuf) != 0)
// ;
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;
::WaitForSingleObject(m_hPostEvent, INFINITE);
// Start the overlapped ReadFile.
if (::ReadFile(m_hCommFile,
m_pReadBuf + sizeof(WORD), m_nReadBufLen,
lpnNumberOfBytesRead, lpOverlappedRead))
{
// This would only happen if there was data waiting to be read.
TRACE0("Data waiting for ReadFile.\n");
// Handle the data.
if (!HandleReadData(*lpnNumberOfBytesRead))
{
return FALSE;
}
}
else
break;
}
// ReadFile failed. Expected because of overlapped I/O.
switch (::GetLastError())
{
// LastError was ERROR_IO_PENDING, as expected.
case ERROR_IO_PENDING:
// TRACE0("Waiting for data from comm connection.\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 ReadFile error");
PostCommError();
return FALSE;
}
}
// Retrieves and handles data when there is data ready.
// This is the function that is called when there is
// data available after an overlapped ReadFile has been
// setup. It retrieves the data and handles it.
BOOL CCommInfo::HandleReadEvent(LPOVERLAPPED lpOverlappedRead,
LPDWORD lpnNumberOfBytesRead)
{
if (::GetOverlappedResult(m_hCommFile,
lpOverlappedRead, lpnNumberOfBytesRead, FALSE))
{
// *((WORD*)m_pReadBuf) = (WORD)*lpnNumberOfBytesRead;
return HandleReadData(*lpnNumberOfBytesRead);
}
// Error in GetOverlappedResult; handle it.
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 Read Error !\n");
PostCommError();
return FALSE;
}
}
// Deals with data after its been read from the comm file.
// This function is yet another helper function for the Read Thread.
BOOL CCommInfo::HandleReadData(DWORD dwSizeofBuffer)
{
// If we got data and didn't just time out empty...
if (dwSizeofBuffer)
{
// Do something with the bytes read.
TRACE0("Got something from Comm port!!!\n");
::ResetEvent(m_hPostEvent);
*((WORD*)m_pReadBuf) = (WORD)dwSizeofBuffer;
if (m_pNotifyWnd != NULL)
{
if (m_pNotifyWnd->PostMessage(WM_COMMAND,
m_dwCommBaseCommandID + ID_COMM_READ,
(LPARAM)m_pNotifyWnd->GetSafeHwnd()) == FALSE)
{
::SetEvent(m_hPostEvent);
return FALSE;
}
else
return TRUE;
}
else
{
// avoid MainWnd is NULL
if (::AfxGetApp()->m_pMainWnd != NULL)
{
if (::AfxGetApp()->m_pMainWnd->PostMessage(WM_COMMAND,
m_dwCommBaseCommandID + ID_COMM_READ,
0) == FALSE)
{
::SetEvent(m_hPostEvent);
return FALSE;
}
else
return TRUE;
}
else
{
::SetEvent(m_hPostEvent);
return TRUE; // only
}
}
}
return TRUE;
}
BOOL CCommInfo::PostCommError(void)
{
if (m_pNotifyWnd != NULL)
{
return m_pNotifyWnd->PostMessage(WM_COMMAND,
m_dwCommBaseCommandID + ID_COMM_ERROR,
(LPARAM)m_pNotifyWnd->GetSafeHwnd());
}
else
{
// avoid MainWnd is NULL
if (::AfxGetApp()->m_pMainWnd != NULL)
{
return ::AfxGetApp()->m_pMainWnd->PostMessage(WM_COMMAND,
m_dwCommBaseCommandID + ID_COMM_ERROR,
0);
}
else
return FALSE;
}
}
void CCommInfo::SuspendReadThread(void)
{
// ::SuspendThread(m_hReadThread);
m_pReadThread->SuspendThread();
}
void CCommInfo::ResumeReadThread(void)
{
// ::ResumeThread(m_hReadThread);
m_pReadThread->ResumeThread();
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -