www.pudn.com > TapiComm.rar > 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#include #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 // Write thread what to write provides a natural desynchronization between // the UI and the Write thread. // // DWORD WINAPI StartWriteThreadProc(LPVOID 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: "); PostHangupCall(); goto 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, &g_hCloseEvent, FALSE, INFINITE, QS_ALLINPUT); switch(dwHandleSignaled) { case WAIT_OBJECT_0: // CloseEvent signaled! { // Time to exit. goto 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: "); PostHangupCall(); goto EndWriteThread; } default: // This case should never occur. { OutputDebugPrintf("Unexpected Wait return value '%lx'", dwHandleSignaled); PostHangupCall(); goto EndWriteThread; } } } // Make sure the CloseEvent isn't signaled while retrieving messages. if (WAIT_TIMEOUT != WaitForSingleObject(g_hCloseEvent,0)) goto 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. { OutputDebugString("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 (!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 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 ); OutputDebugString(Output); break; } } // End of switch(message) } // End of main loop. // Thats the end. Now clean up. EndWriteThread: OutputDebugString("Write thread shutting down\n"); PurgeComm(g_hCommFile, PURGE_TXABORT | PURGE_TXCLEAR); CloseHandle(overlappedWrite.hEvent); g_dwWriteThreadID = 0; CloseHandle(g_hWriteThread); g_hWriteThread = 0; return 0; } // // FUNCTION: HandleWriteData(LPOVERLAPPED, LPCSTR, DWORD) // // PURPOSE: Writes a given string to the comm file handle. // // PARAMETERS: // lpOverlappedWrite - Overlapped structure to use in WriteFile // lpszStringToWrite - String to write. // dwNumberOfBytesToWrite - Length of String to write. // // RETURN VALUE: // TRUE if all bytes were written. False if there was a failure to // write the whole string. // // COMMENTS: // // This function is a helper function for the Write Thread. 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 HandleWriteData(LPOVERLAPPED lpOverlappedWrite, LPCSTR lpszStringToWrite, DWORD dwNumberOfBytesToWrite) { DWORD dwLastError; DWORD dwNumberOfBytesWritten = 0; DWORD dwWhereToStartWriting = 0; // Start at the beginning. DWORD dwHandleSignaled; HANDLE HandlesToWaitFor[2]; HandlesToWaitFor[0] = g_hCloseEvent; HandlesToWaitFor[1] = lpOverlappedWrite -> hEvent; // Keep looping until all characters have been written. do { // Start the overlapped I/O. if (!WriteFile(g_hCommFile, &lpszStringToWrite[ dwWhereToStartWriting ], dwNumberOfBytesToWrite, &dwNumberOfBytesWritten, lpOverlappedWrite)) { // WriteFile failed. Expected; lets handle it. dwLastError = GetLastError(); // Its possible for this error to occur if the // service provider has closed the port. Time to end. if (dwLastError == ERROR_INVALID_HANDLE) { OutputDebugString("ERROR_INVALID_HANDLE, " "Likely that the Service Provider has closed the port.\n"); return FALSE; } // Unexpected error. No idea what. if (dwLastError != ERROR_IO_PENDING) { OutputDebugLastError(dwLastError, "Error to writing to CommFile"); OutputDebugString("Closing TAPI\n"); PostHangupCall(); return FALSE; } // This is the expected ERROR_IO_PENDING case. // Wait for either overlapped I/O completion, // or for the CloseEvent to get signaled. 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. { OutputDebugLastError(GetLastError(), "Write WAIT_FAILED: "); PostHangupCall(); return FALSE; } default: // This case should never occur. { OutputDebugPrintf( "Unexpected Wait return value '%lx'", dwHandleSignaled); PostHangupCall(); return FALSE; } } if (!GetOverlappedResult(g_hCommFile, lpOverlappedWrite, &dwNumberOfBytesWritten, TRUE)) { dwLastError = GetLastError(); // Its possible for this error to occur if the // service provider has closed the port. if (dwLastError == ERROR_INVALID_HANDLE) { OutputDebugString("ERROR_INVALID_HANDLE, " "Likely that the Service Provider has closed the port.\n"); return FALSE; } // No idea what could cause another error. OutputDebugLastError(dwLastError, "Error writing to CommFile while waiting"); OutputDebugString("Closing TAPI\n"); PostHangupCall(); return FALSE; } } // Some data was written. Make sure it all got written. dwNumberOfBytesToWrite -= dwNumberOfBytesWritten; dwWhereToStartWriting += dwNumberOfBytesWritten; } while(dwNumberOfBytesToWrite > 0); // Write the whole thing! // Wrote the whole string. return TRUE; } // // FUNCTION: StartReadThreadProc(LPVOID) // // PURPOSE: This is the starting point for the Read Thread. // // PARAMETERS: // lpvParam - unused. // // RETURN VALUE: // DWORD - unused. // // COMMENTS: // // 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. // // Note that there is absolutely *no* interpretation of the data, // which means no terminal emulation. It basically means that this // sample is pretty useless as a TTY program. // // 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 WINAPI StartReadThreadProc(LPVOID lpvParam) { char szInputBuffer[INPUTBUFFERSIZE]; DWORD nNumberOfBytesRead; HANDLE HandlesToWaitFor[3]; DWORD dwHandleSignaled; DWORD fdwEvtMask; // 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, TRUE, TRUE, NULL); if (overlappedRead.hEvent == NULL) { OutputDebugLastError(GetLastError(), "Unable to CreateEvent: "); PostHangupCall(); goto EndReadThread; } // And an event for the CommEvent overlapped structure. overlappedCommEvent.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL); if (overlappedCommEvent.hEvent == NULL) { OutputDebugLastError(GetLastError(), "Unable to CreateEvent: "); PostHangupCall(); goto EndReadThread; } // We will be waiting on these objects. HandlesToWaitFor[0] = g_hCloseEvent; HandlesToWaitFor[1] = overlappedCommEvent.hEvent; HandlesToWaitFor[2] = overlappedRead.hEvent; // Setup CommEvent handling. // Set the comm mask so we receive error signals. if (!SetCommMask(g_hCommFile, EV_ERR)) { OutputDebugLastError(GetLastError(),"Unable to SetCommMask: "); PostHangupCall(); goto EndReadThread; } // Start waiting for CommEvents (Errors) if (!SetupCommEvent(&overlappedCommEvent, &fdwEvtMask)) { PostHangupCall(); goto EndReadThread; } // Start waiting for Read events. if (!SetupReadEvent(&overlappedRead, szInputBuffer, INPUTBUFFERSIZE, &nNumberOfBytesRead)) { PostHangupCall(); goto EndReadThread; } // Keep looping until we break out. while (TRUE) { // Wait until some event occurs (data to read; error; stopping). dwHandleSignaled = WaitForMultipleObjects(3, HandlesToWaitFor, FALSE, INFINITE); // Which event occured? switch(dwHandleSignaled) { case WAIT_OBJECT_0: // Signal to end the thread. { // Time to exit. goto EndReadThread; } case WAIT_OBJECT_0 + 1: // CommEvent signaled. { // Handle the CommEvent. if (!HandleCommEvent(&overlappedCommEvent, &fdwEvtMask, TRUE)) { PostHangupCall(); goto EndReadThread; } // Start waiting for the next CommEvent. if (!SetupCommEvent(&overlappedCommEvent, &fdwEvtMask)) { PostHangupCall(); goto EndReadThread; } break; } case WAIT_OBJECT_0 + 2: // Read Event signaled. { // Get the new data! if (!HandleReadEvent(&overlappedRead, szInputBuffer, INPUTBUFFERSIZE, &nNumberOfBytesRead)) { PostHangupCall(); goto EndReadThread; } // Wait for more new data. if (!SetupReadEvent(&overlappedRead, szInputBuffer, INPUTBUFFERSIZE, &nNumberOfBytesRead)) { PostHangupCall(); goto EndReadThread; } break; } case WAIT_FAILED: // Wait failed. Shouldn't happen. { OutputDebugLastError(GetLastError(),"Read WAIT_FAILED: "); PostHangupCall(); goto EndReadThread; } default: // This case should never occur. { OutputDebugPrintf("Unexpected Wait return value '%lx'", dwHandleSignaled); PostHangupCall(); goto EndReadThread; } } // End of switch(dwHandleSignaled). } // End of while(TRUE) loop. // Time to clean up Read Thread. EndReadThread: OutputDebugString("Read thread shutting down\n"); PurgeComm(g_hCommFile, PURGE_RXABORT | PURGE_RXCLEAR); CloseHandle(overlappedRead.hEvent); CloseHandle(overlappedCommEvent.hEvent); g_dwReadThreadID = 0; CloseHandle(g_hReadThread); g_hReadThread = 0; return 0; } // // FUNCTION: SetupReadEvent(LPOVERLAPPED, LPSTR, DWORD, LPDWORD) // // PURPOSE: Sets up an overlapped ReadFile // // PARAMETERS: // lpOverlappedRead - address of overlapped structure to use. // lpszInputBuffer - Buffer to place incoming bytes. // dwSizeofBuffer - size of lpszInputBuffer. // lpnNumberOfBytesRead - address of DWORD to place the number of read bytes. // // RETURN VALUE: // TRUE if able to successfully setup the ReadFile. FALSE if there // was a failure setting up or if the CloseEvent object was signaled. // // COMMENTS: // // This function is a helper function for the Read Thread. 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 SetupReadEvent(LPOVERLAPPED lpOverlappedRead, LPSTR lpszInputBuffer, DWORD dwSizeofBuffer, LPDWORD lpnNumberOfBytesRead) { DWORD dwLastError; StartSetupReadEvent: // Make sure the CloseEvent hasn't been signaled yet. // Check is needed because this function is potentially recursive. if (WAIT_TIMEOUT != WaitForSingleObject(g_hCloseEvent,0)) return FALSE; // Start the overlapped ReadFile. if (ReadFile(g_hCommFile, lpszInputBuffer, dwSizeofBuffer, lpnNumberOfBytesRead, lpOverlappedRead)) { // This would only happen if there was data waiting to be read. OutputDebugString("Data waiting for ReadFile.\n"); // Handle the data. if (!HandleReadData(lpszInputBuffer, *lpnNumberOfBytesRead)) { return FALSE; } // Start waiting for more data. goto StartSetupReadEvent; } // ReadFile failed. Expected because of overlapped I/O. dwLastError = GetLastError(); // LastError was ERROR_IO_PENDING, as expected. if (dwLastError == ERROR_IO_PENDING) { OutputDebugString("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. if (dwLastError == ERROR_INVALID_HANDLE) { OutputDebugString("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. OutputDebugLastError(dwLastError,"Unexpected ReadFile error: "); PostHangupCall(); return FALSE; } // // FUNCTION: HandleReadEvent(LPOVERLAPPED, LPSTR, DWORD, LPDWORD) // // PURPOSE: Retrieves and handles data when there is data ready. // // PARAMETERS: // lpOverlappedRead - address of overlapped structure to use. // lpszInputBuffer - Buffer to place incoming bytes. // dwSizeofBuffer - size of lpszInputBuffer. // lpnNumberOfBytesRead - address of DWORD to place the number of read bytes. // // RETURN VALUE: // TRUE if able to successfully retrieve and handle the available data. // FALSE if unable to retrieve or handle the data. // // COMMENTS: // // This function is another helper function for the Read Thread. 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 HandleReadEvent(LPOVERLAPPED lpOverlappedRead, LPSTR lpszInputBuffer, DWORD dwSizeofBuffer, LPDWORD lpnNumberOfBytesRead) { DWORD dwLastError; if (GetOverlappedResult(g_hCommFile, lpOverlappedRead, lpnNumberOfBytesRead, FALSE)) { return HandleReadData(lpszInputBuffer, *lpnNumberOfBytesRead); } // Error in GetOverlappedResult; handle it. dwLastError = GetLastError(); // Its possible for this error to occur if the // service provider has closed the port. Time to end. if (dwLastError == ERROR_INVALID_HANDLE) { OutputDebugString("ERROR_INVALID_HANDLE, " "Likely that the Service Provider has closed the port.\n"); return FALSE; } OutputDebugLastError(dwLastError, "Unexpected GetOverlappedResult Read Error: "); PostHangupCall(); return FALSE; } // // FUNCTION: HandleReadData(LPCSTR, DWORD) // // PURPOSE: Deals with data after its been read from the comm file. // // PARAMETERS: // lpszInputBuffer - Buffer to place incoming bytes. // dwSizeofBuffer - size of lpszInputBuffer. // // RETURN VALUE: // TRUE if able to successfully handle the data. // FALSE if unable to allocate memory or handle the data. // // COMMENTS: // // This function is yet another helper function for the Read Thread. // It LocalAlloc()s a buffer, copies the new data to this buffer and // calls PostWriteToDisplayCtl to let the EditCtls module deal with // the data. Its assumed that PostWriteToDisplayCtl posts the message // rather than dealing with it right away so that the Read Thread // is free to get right back to waiting for data. Its also assumed // that the EditCtls module is responsible for LocalFree()ing the // pointer that is passed on. // // BOOL HandleReadData(LPCSTR lpszInputBuffer, DWORD dwSizeofBuffer) { // If we got data and didn't just time out empty... if (dwSizeofBuffer) { LPSTR lpszPostedBytes; // Do something with the bytes read. OutputDebugString("Got something from Comm port!!!\n"); lpszPostedBytes = LocalAlloc(LPTR,dwSizeofBuffer+1); if (lpszPostedBytes == NULL) { OutputDebugLastError(GetLastError(), "LocalAlloc: "); return FALSE; } memcpy(lpszPostedBytes, lpszInputBuffer, dwSizeofBuffer); lpszPostedBytes[dwSizeofBuffer] = '\0'; return PostWriteToDisplayCtl(lpszPostedBytes, dwSizeofBuffer); } } // // FUNCTION: SetupCommEvent(LPOVERLAPPED, LPDWORD) // // PURPOSE: Sets up the overlapped WaitCommEvent call. // // PARAMETERS: // lpOverlappedCommEvent - Pointer to the overlapped structure to use. // lpfdwEvtMask - Pointer to DWORD to received Event data. // // RETURN VALUE: // TRUE if able to successfully setup the WaitCommEvent. // FALSE if unable to setup WaitCommEvent, unable to handle // an existing outstanding event or if the CloseEvent has been signaled. // // COMMENTS: // // 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 SetupCommEvent(LPOVERLAPPED lpOverlappedCommEvent, LPDWORD lpfdwEvtMask) { DWORD dwLastError; StartSetupCommEvent: // Make sure the CloseEvent hasn't been signaled yet. // Check is needed because this function is potentially recursive. if (WAIT_TIMEOUT != WaitForSingleObject(g_hCloseEvent,0)) return FALSE; // Start waiting for Comm Errors. if (WaitCommEvent(g_hCommFile, lpfdwEvtMask, lpOverlappedCommEvent)) { // This could happen if there was an error waiting on the // comm port. Lets try and handle it. OutputDebugString("Event (Error) waiting before WaitCommEvent.\n"); if (!HandleCommEvent(NULL, lpfdwEvtMask, FALSE)) return FALSE; // What could cause infinite recursion at this point? goto StartSetupCommEvent; } // We expect ERROR_IO_PENDING returned from WaitCommEvent // because we are waiting with an overlapped structure. dwLastError = GetLastError(); // LastError was ERROR_IO_PENDING, as expected. if (dwLastError == ERROR_IO_PENDING) { OutputDebugString("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. if (dwLastError == ERROR_INVALID_HANDLE) { OutputDebugString("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. OutputDebugLastError(dwLastError, "Unexpected WaitCommEvent error: "); return FALSE; } // // FUNCTION: HandleCommEvent(LPOVERLAPPED, LPDWORD, BOOL) // // PURPOSE: Handle an outstanding Comm Event. // // PARAMETERS: // lpOverlappedCommEvent - Pointer to the overlapped structure to use. // lpfdwEvtMask - Pointer to DWORD to received Event data. // fRetrieveEvent - Flag to signal if the event needs to be // retrieved, or has already been retrieved. // // RETURN VALUE: // TRUE if able to handle a Comm Event. // FALSE if unable to setup WaitCommEvent, unable to handle // an existing outstanding event or if the CloseEvent has been signaled. // // COMMENTS: // // This function is a helper function for the Read Thread that (if // fRetrieveEvent == 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 HandleCommEvent(LPOVERLAPPED lpOverlappedCommEvent, LPDWORD lpfdwEvtMask, BOOL fRetrieveEvent) { DWORD dwDummy; LPSTR lpszOutput; char szError[128] = ""; DWORD dwErrors; DWORD nOutput; DWORD dwLastError; lpszOutput = LocalAlloc(LPTR,256); if (lpszOutput == NULL) { OutputDebugLastError(GetLastError(), "LocalAlloc: "); return FALSE; } // 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 (fRetrieveEvent) if (!GetOverlappedResult(g_hCommFile, lpOverlappedCommEvent, &dwDummy, FALSE)) { dwLastError = GetLastError(); // Its possible for this error to occur if the // service provider has closed the port. Time to end. if (dwLastError == ERROR_INVALID_HANDLE) { OutputDebugString("ERROR_INVALID_HANDLE, " "Likely that the Service Provider has closed the port.\n"); return FALSE; } OutputDebugLastError(dwLastError, "Unexpected GetOverlappedResult for WaitCommEvent: "); return FALSE; } // Was the event an error? if (*lpfdwEvtMask & EV_ERR) { // Which error was it? if (!ClearCommError(g_hCommFile, &dwErrors, NULL)) { dwLastError = GetLastError(); // Its possible for this error to occur if the // service provider has closed the port. Time to end. if (dwLastError == ERROR_INVALID_HANDLE) { OutputDebugString("ERROR_INVALID_HANDLE, " "Likely that the Service Provider has closed the port.\n"); return FALSE; } OutputDebugLastError(GetLastError(),"ClearCommError: "); 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. if (dwErrors == 0) { strcat(szError, "NULL Error"); } 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"); } nOutput = wsprintf(lpszOutput, "Comm Event: '%s', EvtMask = '%lx'\n", szError, dwErrors); PostWriteToDisplayCtl(lpszOutput, nOutput); return TRUE; } // Should not have gotten here. Only interested in ERR conditions. OutputDebugPrintf("Unexpected comm event %lx",*lpfdwEvtMask); return FALSE; }