📄 tapiconn._cp
字号:
TRACE0("lineShutdown unhandled error\n");
break;
}
}
while (lReturn != TAPISUCCESS);
m_bTapiInUse = FALSE;
m_bConnected = FALSE;
m_hLineApp = NULL;
m_hCall = NULL;
m_hLine = NULL;
m_bShuttingDown = FALSE;
TRACE0("TAPI uninitialized.\n");
return TRUE;
}
//
// FUNCTION: StartComm(HANDLE)
//
// PURPOSE: Starts communications over the comm port.
//
// PARAMETERS:
// hNewCommFile - This is the COMM File handle to communicate with.
// This handle is obtained from TAPI.
//
// RETURN VALUE:
// TRUE if able to setup the communications.
//
// COMMENTS:
//
// StartComm makes sure there isn't communication in progress already,
// the hNewCommFile is valid, and all the threads can be created. It
// also configures the hNewCommFile for the appropriate COMM settings.
//
// If StartComm fails for any reason, it's up to the calling application
// to close the Comm file handle.
//
BOOL CTapiConnection::StartComm(HANDLE hNewCommFile)
{
// Is this a valid comm handle?
if (GetFileType(hNewCommFile) != FILE_TYPE_CHAR)
{
TRACE0("File handle is not a comm handle.\n");
return FALSE;
}
// Are we already doing comm?
if (m_hCommFile != NULL)
{
TRACE0("Already have a comm file open\n");
return FALSE;
}
// Its ok to continue.
m_hCommFile = hNewCommFile;
// Setting and querying the comm port configurations.
{
// Configure the comm settings.
COMMTIMEOUTS commtimeouts;
DCB dcb;
COMMPROP commprop;
DWORD fdwEvtMask;
// These are here just so you can set a breakpoint
// and see what the comm settings are. Most Comm settings
// are already set through TAPI.
GetCommState(hNewCommFile, &dcb);
GetCommProperties(hNewCommFile, &commprop);
GetCommMask(m_hCommFile, &fdwEvtMask);
GetCommTimeouts(m_hCommFile, &commtimeouts);
// The CommTimeout numbers will very likely change if you are
// coding to meet some kind of specification where
// you need to reply within a certain amount of time after
// recieving the last byte. However, If 1/4th of a second
// goes by between recieving two characters, its a good
// indication that the transmitting end has finished, even
// assuming a 1200 baud modem.
commtimeouts.ReadIntervalTimeout = 250;
commtimeouts.ReadTotalTimeoutMultiplier = 0;
commtimeouts.ReadTotalTimeoutConstant = 0;
commtimeouts.WriteTotalTimeoutMultiplier = 0;
commtimeouts.WriteTotalTimeoutConstant = 0;
SetCommTimeouts(m_hCommFile, &commtimeouts);
// fAbortOnError is the only DCB dependancy in TapiComm.
// Can't guarentee that the SP will set this to what we expect.
dcb.fAbortOnError = FALSE;
SetCommState(hNewCommFile, &dcb);
}
// Create the event that will signal the threads to close.
m_hCloseEvent = ::CreateEvent(NULL, TRUE, FALSE, NULL);
if (m_hCloseEvent == NULL)
{
OutputDebugLastError(GetLastError(), "Unable to CreateEvent: ");
m_hCommFile = NULL;
return FALSE;
}
// Create the Read thread.
m_hReadThread =
::CreateThread(NULL,
0,
(LPTHREAD_START_ROUTINE)CTapiConnection::StartReadThreadProc,
(LPVOID)this,
0,
&m_dwReadThreadID);
if (m_hReadThread == NULL)
{
OutputDebugLastError(GetLastError(),"Unable to create Read thread");
m_dwReadThreadID = 0;
m_hCommFile = 0;
return FALSE;
}
// Comm threads should to have a higher base priority than the UI thread.
// If they don't, then any temporary priority boost the UI thread gains
// could cause the COMM threads to loose data.
::SetThreadPriority(m_hReadThread, THREAD_PRIORITY_HIGHEST);
// Create the Write thread.
m_hWriteThread =
::CreateThread(
NULL,
0,
(LPTHREAD_START_ROUTINE)CTapiConnection::StartWriteThreadProc,
(LPVOID)this,
0,
&m_dwWriteThreadID);
if (m_hWriteThread == NULL)
{
OutputDebugLastError(GetLastError(),"Unable to create Write thread");
CloseReadThread();
m_dwWriteThreadID = 0;
m_hCommFile = 0;
return FALSE;
}
::SetThreadPriority(m_hWriteThread, THREAD_PRIORITY_ABOVE_NORMAL);
// Everything was created ok. Ready to go!
return TRUE;
}
//
// FUNCTION: StopComm
//
// PURPOSE: Stop and end all communication threads.
//
// PARAMETERS:
// none
//
// RETURN VALUE:
// none
//
// COMMENTS:
//
// Tries to gracefully signal all communication threads to
// close, but terminates them if it has to.
//
void CTapiConnection::StopComm()
{
// No need to continue if we're not communicating.
if (m_hCommFile == NULL)
return;
TRACE0("Stopping the Comm\n");
// Close the threads.
CloseReadThread();
CloseWriteThread();
// Not needed anymore.
CloseHandle(m_hCloseEvent);
// Now close the comm port handle.
CloseHandle(m_hCommFile);
m_hCommFile = NULL;
}
//
// FUNCTION: WriteCommString(LPCSTR, DWORD)
//
// PURPOSE: Send a String to the Write Thread to be written to the Comm.
//
// PARAMETERS:
// pszStringToWrite - String to Write to Comm port.
// nSizeofStringToWrite - length of pszStringToWrite.
//
// RETURN VALUE:
// Returns TRUE if the PostMessage is successful.
// Returns FALSE if PostMessage fails or Write thread doesn't exist.
//
// COMMENTS:
//
// This is a wrapper function so that other modules don't care that
// Comm writing is done via PostMessage to a Write thread. Note that
// using PostMessage speeds up response to the UI (very little delay to
// 'write' a string) and provides a natural buffer if the comm is slow
// (ie: the messages just pile up in the message queue).
//
// Note that it is assumed that pszStringToWrite is allocated with
// LocalAlloc, and that if WriteCommString succeeds, its the job of the
// Write thread to LocalFree it. If WriteCommString fails, then its
// the job of the calling function to free the string.
//
//
BOOL CTapiConnection::WriteCommString(LPCSTR lpszStringToWrite, DWORD dwSizeofStringToWrite)
{
if (m_hWriteThread)
{
if (::PostThreadMessage(m_dwWriteThreadID, PWM_COMMWRITE,
(WPARAM)dwSizeofStringToWrite, (LPARAM) lpszStringToWrite))
{
return TRUE;
}
else
TRACE0("Failed to Post to Write thread.\n");
}
else
TRACE0("Write thread not created\n");
return FALSE;
}
//*****************************************
// The rest of the functions are intended for use
// only within the CommCode module.
//*****************************************
//
// FUNCTION: CloseReadThread
//
// PURPOSE: Close the Read Thread.
//
// PARAMETERS:
// none
//
// RETURN VALUE:
// none
//
// COMMENTS:
//
// Closes the Read thread by signaling the CloseEvent.
// Purges any outstanding reads 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 CTapiConnection::CloseReadThread()
{
// If it exists...
if (m_hReadThread)
{
TRACE0("Closing Read Thread\n");
// Signal the event to close the worker threads.
::SetEvent(m_hCloseEvent);
// Purge all outstanding reads
::PurgeComm(m_hCommFile, PURGE_RXABORT | PURGE_RXCLEAR);
// Wait 10 seconds for it to exit. Shouldn't happen.
if (::WaitForSingleObject(m_hReadThread, 10000) == WAIT_TIMEOUT)
{
TRACE0("Read thread not exiting. Terminating it.\n");
::TerminateThread(m_hReadThread, 0);
// The ReadThread cleans up these itself if it terminates normally.
::CloseHandle(m_hReadThread);
m_hReadThread = 0;
m_dwReadThreadID = 0;
}
}
}
//
// FUNCTION: CloseWriteThread
//
// PURPOSE: Closes the Write Thread.
//
// PARAMETERS:
// none
//
// RETURN VALUE:
// none
//
// COMMENTS:
//
// 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 CTapiConnection::CloseWriteThread()
{
// If it exists...
if (m_hWriteThread)
{
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)
{
TRACE0("Write thread not exiting. Terminating it.\n");
::TerminateThread(m_hWriteThread, 0);
// The WriteThread cleans up these itself if it terminates normally.
::CloseHandle(m_hWriteThread);
m_hWriteThread = 0;
m_dwWriteThreadID = 0;
}
}
}
//
// FUNCTION: StartWriteThreadProc(LPVOID)
//
// PURPOSE: The starting point for the Write thread.
//
//
// RETURN VALUE:
// DWORD - unused.
//
// COMMENTS:
//
// 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
// 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 CTapiConnection::StartWriteThreadProc(LPVOID lpvParam)
{
CTapiConnection* pConn = (CTapiConnection*)lpvParam;
MSG msg;
DWORD dwHandleSignaled;
// Needed for overlapped I/O.
OVERLAPPED overlappedWrite = {0, 0, 0, 0, NULL};
overlappedWrite.hEvent = ::CreateEvent(NULL, TRUE, TRUE, NULL);
if (overlappedWrite.hEvent == NULL)
{
OutputDebugLastError(GetLastError(), "Unable to CreateEvent: ");
pConn->PostHangupCall();
goto LABEL_ENDWRITETHREAD;
}
// This is the main loop. Loop until we break out.
while (TRUE)
{
if (!::PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
// If there are no messages pending, wait for a message or the CloseEvent.
dwHandleSignaled =
::MsgWaitForMultipleObjects(1, &pConn->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.
{
OutputDebugLastError(GetLastError(),"Write WAIT_FAILED: ");
pConn->PostHangupCall();
goto LABEL_ENDWRITETHREAD;
}
default: // This case should never occur.
{
TRACE1("Unexpected Wait return value '%lx'",
dwHandleSignaled);
pConn->PostHangupCall();
goto LABEL_ENDWRITETHREAD;
}
}
}
// Make sure the CloseEvent isn't signaled while retrieving messages.
if (WAIT_TIMEOUT != ::WaitForSingleObject(pConn->m_hCloseEvent, 0))
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.
switch (msg.message)
{
case PWM_COMMWRITE: // New string to write to Comm port.
{
TRACE0("Writing to comm port\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 (!pConn->HandleWriteData(&overlappedWrite,
(LPSTR)msg.lParam, (DWORD)msg.wParam))
{
// If it failed, either we got a signal to end or there
// really was a failure.
::LocalFree((HLOCAL)msg.lParam);
goto LABEL_ENDWRITETHREAD;
}
// Data was sent in a LocalAlloc()d buffer. Must free it.
::LocalFree((HLOCAL) msg.lParam);
break;
}
// What other messages could the thread get?
default:
{
char Output[256];
wsprintf(Output,
"Unexpected message posted to Write thread: %ui\n",
msg.message);
TRACE0(Output);
break;
}
} // End of switch(message)
} // End of main loop.
// Thats the end. Now clean up.
LABEL_ENDWRITETHREAD:
TRACE0("Write thread shutting down\n");
::PurgeComm(pConn->m_hCommFile, PURGE_TXABORT | PURGE_TXCLEAR);
::CloseHandle(overlappedWrite.hEvent);
pConn->m_dwWriteThreadID = 0;
::CloseHandle(pConn->m_hWriteThread);
pConn->m_hWriteThread = 0;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -