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

📄 tapiconn._cp

📁 串口调试助手的源代码
💻 _CP
📖 第 1 页 / 共 5 页
字号:
			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 + -