?? commcode.c
字號:
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright 1995 - 1998 Microsoft Corporation. All Rights Reserved.
//
// MODULE: CommCode.c
//
// PURPOSE: Handles all the COMM routines for TapiComm.
//
// EXPORTED FUNCTIONS: These functions are for use by other modules.
// StartComm - Start communications.
// StopComm - Stop Communications.
// WriteCommString - Write a string to the Comm port.
//
// INTERNAL FUNCTION: These functions are for this module only.
// CloseReadThread - Close the Read Thread.
// CloseWriteThread - Close the Write Thread.
//
// StartReadThreadProc - Starting function for the Read Thread.
// StartWriteThreadProc - Starting function for the Write Thread.
//
// - Write Thread helper function
// HandleWriteData - Actually does the work of writing a string to comm.
//
// - Read Thread helper functions
// SetupReadEvent - Sets up the overlapped ReadFile
// HandleReadEvent - Gets the results from the overlapped ReadFile
// HandleReadData - Handles data returned from the ReadFile
//
// HandleCommEvent - Sets up the CommEvent event.
// SetupCommEvent - Handles CommEvent events (if they occur).
//
#include <windows.h>
#include <string.h>
#include "TapiCode.h"
#include "CommCode.h"
#include "globals.h"
#include "TapiInfo.h"
#include "EditCtls.h"
// This is the message posted to the WriteThread
// When we have something to write.
#define PWM_COMMWRITE WM_USER+1
// Default size of the Input Buffer used by this code.
#define INPUTBUFFERSIZE 2048
//*****************************************
// Global variables.
//*****************************************
HANDLE g_hCommFile = NULL;
DWORD g_dwReadThreadID = 0;
DWORD g_dwWriteThreadID = 0;
HANDLE g_hReadThread = NULL;
HANDLE g_hWriteThread = NULL;
HANDLE g_hCloseEvent = NULL;
//*****************************************
// CommCode internal Function Prototypes
//*****************************************
void CloseReadThread();
void CloseWriteThread();
DWORD WINAPI StartReadThreadProc(LPVOID lpvParam);
DWORD WINAPI StartWriteThreadProc(LPVOID lpvParam);
BOOL HandleWriteData(LPOVERLAPPED lpOverlappedWrite,
LPCSTR lpszStringToWrite, DWORD dwNumberOfBytesToWrite);
BOOL SetupReadEvent(LPOVERLAPPED lpOverlappedRead,
LPSTR lpszInputBuffer, DWORD dwSizeofBuffer,
LPDWORD lpnNumberOfBytesRead);
BOOL HandleReadEvent(LPOVERLAPPED lpOverlappedRead,
LPSTR lpszInputBuffer, DWORD dwSizeofBuffer,
LPDWORD lpnNumberOfBytesRead);
BOOL HandleReadData(LPCSTR lpszInputBuffer, DWORD dwSizeofBuffer);
BOOL HandleCommEvent(LPOVERLAPPED lpOverlappedCommEvent,
LPDWORD lpfdwEvtMask, BOOL fRetrieveEvent);
BOOL SetupCommEvent(LPOVERLAPPED lpOverlappedCommEvent,
LPDWORD lpfdwEvtMask);
//*****************************************
// Functions exported for use by other modules
//*****************************************
//
// 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 StartComm(HANDLE hNewCommFile)
{
// Is this a valid comm handle?
if (GetFileType(hNewCommFile) != FILE_TYPE_CHAR)
{
OutputDebugString("File handle is not a comm handle.\n");
return FALSE;
}
// Are we already doing comm?
if (g_hCommFile != NULL)
{
OutputDebugString("Already have a comm file open\n");
return FALSE;
}
// Its ok to continue.
g_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(g_hCommFile, &fdwEvtMask);
GetCommTimeouts(g_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(g_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.
g_hCloseEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (g_hCloseEvent == NULL)
{
OutputDebugLastError(GetLastError(), "Unable to CreateEvent: ");
g_hCommFile = NULL;
return FALSE;
}
// Create the Read thread.
g_hReadThread =
CreateThread(NULL, 0, StartReadThreadProc, 0, 0, &g_dwReadThreadID);
if (g_hReadThread == NULL)
{
OutputDebugLastError(GetLastError(),"Unable to create Read thread");
g_dwReadThreadID = 0;
g_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(g_hReadThread, THREAD_PRIORITY_HIGHEST);
// Create the Write thread.
g_hWriteThread =
CreateThread(NULL, 0, StartWriteThreadProc, 0, 0, &g_dwWriteThreadID);
if (g_hWriteThread == NULL)
{
OutputDebugLastError(GetLastError(),"Unable to create Write thread");
CloseReadThread();
g_dwWriteThreadID = 0;
g_hCommFile = 0;
return FALSE;
}
SetThreadPriority(g_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 StopComm()
{
// No need to continue if we're not communicating.
if (g_hCommFile == NULL)
return;
OutputDebugString("Stopping the Comm\n");
// Close the threads.
CloseReadThread();
CloseWriteThread();
// Not needed anymore.
CloseHandle(g_hCloseEvent);
// Now close the comm port handle.
CloseHandle(g_hCommFile);
g_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 WriteCommString(LPCSTR lpszStringToWrite, DWORD dwSizeofStringToWrite)
{
if (g_hWriteThread)
{
if (PostThreadMessage(g_dwWriteThreadID, PWM_COMMWRITE,
(WPARAM) dwSizeofStringToWrite, (LPARAM) lpszStringToWrite))
{
return TRUE;
}
else
OutputDebugString("Failed to Post to Write thread.\n");
}
else
OutputDebugString("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 CloseReadThread()
{
// If it exists...
if (g_hReadThread)
{
OutputDebugString("Closing Read Thread\n");
// Signal the event to close the worker threads.
SetEvent(g_hCloseEvent);
// Purge all outstanding reads
PurgeComm(g_hCommFile, PURGE_RXABORT | PURGE_RXCLEAR);
// Wait 10 seconds for it to exit. Shouldn't happen.
if (WaitForSingleObject(g_hReadThread, 10000) == WAIT_TIMEOUT)
{
OutputDebugString("Read thread not exiting. Terminating it.\n");
TerminateThread(g_hReadThread, 0);
// The ReadThread cleans up these itself if it terminates
// normally.
CloseHandle(g_hReadThread);
g_hReadThread = 0;
g_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 CloseWriteThread()
{
// If it exists...
if (g_hWriteThread)
{
OutputDebugString("Closing Write Thread\n");
// Signal the event to close the worker threads.
SetEvent(g_hCloseEvent);
// Purge all outstanding writes.
PurgeComm(g_hCommFile, PURGE_TXABORT | PURGE_TXCLEAR);
// Wait 10 seconds for it to exit. Shouldn't happen.
if (WaitForSingleObject(g_hWriteThread, 10000) == WAIT_TIMEOUT)
{
OutputDebugString("Write thread not exiting. Terminating it.\n");
TerminateThread(g_hWriteThread, 0);
// The WriteThread cleans up these itself if it terminates
// normally.
CloseHandle(g_hWriteThread);
g_hWriteThread = 0;
g_dwWriteThreadID = 0;
}
}
}
//
// FUNCTION: StartWriteThreadProc(LPVOID)
//
// PURPOSE: The starting point for the Write thread.
//
// PARAMETERS:
// lpvParam - unused.
//
// 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
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -