home *** CD-ROM | disk | FTP | other *** search
/ io Programmo 10 / ioProg_10.iso / soft / platsdk / inetwork.exe / TAPI-S.cab / 23COMMCODE.C < prev    next >
Encoding:
C/C++ Source or Header  |  1997-10-05  |  41.3 KB  |  1,347 lines

  1. // THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
  2. // ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
  3. // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
  4. // PARTICULAR PURPOSE.
  5. //
  6. // Copyright (C) 1995-1997  Microsoft Corporation.  All Rights Reserved.
  7. //
  8. //  MODULE: CommCode.c
  9. //
  10. //  PURPOSE: Handles all the COMM routines for TapiComm.
  11. //
  12. //  EXPORTED FUNCTIONS:  These functions are for use by other modules.
  13. //    StartComm        - Start communications.  
  14. //    StopComm         - Stop Communications.
  15. //    WriteCommString  - Write a string to the Comm port.
  16. //
  17. //  INTERNAL FUNCTION:  These functions are for this module only.
  18. //    CloseReadThread  - Close the Read Thread.
  19. //    CloseWriteThread - Close the Write Thread.
  20. //
  21. //    StartReadThreadProc    - Starting function for the Read Thread.
  22. //    StartWriteThreadProc   - Starting function for the Write Thread.
  23. //
  24. //    - Write Thread helper function
  25. //    HandleWriteData - Actually does the work of writing a string to comm.
  26. //
  27. //    - Read Thread helper functions
  28. //    SetupReadEvent  - Sets up the overlapped ReadFile
  29. //    HandleReadEvent - Gets the results from the overlapped ReadFile
  30. //    HandleReadData  - Handles data returned from the ReadFile
  31. //
  32. //    HandleCommEvent - Sets up the CommEvent event.
  33. //    SetupCommEvent  - Handles CommEvent events (if they occur).
  34. //
  35.  
  36.  
  37. #include <windows.h>
  38. #include <string.h>
  39. #include "TapiCode.h"
  40. #include "CommCode.h"
  41. #include "globals.h"
  42. #include "TapiInfo.h"
  43. #include "EditCtls.h"
  44.  
  45. // This is the message posted to the WriteThread
  46. // When we have something to write.
  47. #define PWM_COMMWRITE   WM_USER+1
  48.  
  49. // Default size of the Input Buffer used by this code.
  50. #define INPUTBUFFERSIZE 2048
  51.  
  52. //*****************************************
  53. // Global variables.
  54. //*****************************************
  55.  
  56. HANDLE g_hCommFile = NULL;
  57.  
  58. DWORD g_dwReadThreadID  = 0;
  59. DWORD g_dwWriteThreadID = 0;
  60. HANDLE g_hReadThread  = NULL;
  61. HANDLE g_hWriteThread = NULL;
  62.  
  63. HANDLE g_hCloseEvent = NULL;
  64.  
  65. //*****************************************
  66. // CommCode internal Function Prototypes
  67. //*****************************************
  68.  
  69. void CloseReadThread();
  70. void CloseWriteThread();
  71.  
  72. DWORD WINAPI StartReadThreadProc(LPVOID lpvParam);
  73. DWORD WINAPI StartWriteThreadProc(LPVOID lpvParam);
  74.  
  75.  
  76. BOOL HandleWriteData(LPOVERLAPPED lpOverlappedWrite,
  77.         LPCSTR lpszStringToWrite, DWORD dwNumberOfBytesToWrite);
  78.  
  79.  
  80. BOOL SetupReadEvent(LPOVERLAPPED lpOverlappedRead,
  81.         LPSTR lpszInputBuffer, DWORD dwSizeofBuffer,
  82.         LPDWORD lpnNumberOfBytesRead);
  83. BOOL HandleReadEvent(LPOVERLAPPED lpOverlappedRead,
  84.         LPSTR lpszInputBuffer, DWORD dwSizeofBuffer,
  85.         LPDWORD lpnNumberOfBytesRead);
  86. BOOL HandleReadData(LPCSTR lpszInputBuffer, DWORD dwSizeofBuffer);
  87.  
  88.  
  89. BOOL HandleCommEvent(LPOVERLAPPED lpOverlappedCommEvent,
  90.         LPDWORD lpfdwEvtMask, BOOL fRetrieveEvent);
  91. BOOL SetupCommEvent(LPOVERLAPPED lpOverlappedCommEvent,
  92.         LPDWORD lpfdwEvtMask);
  93.  
  94.  
  95.  
  96. //*****************************************
  97. // Functions exported for use by other modules
  98. //*****************************************
  99.  
  100.  
  101.  
  102. //
  103. //  FUNCTION: StartComm(HANDLE)
  104. //
  105. //  PURPOSE: Starts communications over the comm port.
  106. //
  107. //  PARAMETERS:
  108. //    hNewCommFile - This is the COMM File handle to communicate with.
  109. //                   This handle is obtained from TAPI.
  110. //
  111. //  RETURN VALUE:
  112. //    TRUE if able to setup the communications.
  113. //
  114. //  COMMENTS:
  115. //
  116. //    StartComm makes sure there isn't communication in progress already,
  117. //    the hNewCommFile is valid, and all the threads can be created.  It
  118. //    also configures the hNewCommFile for the appropriate COMM settings.
  119. //
  120. //    If StartComm fails for any reason, it's up to the calling application
  121. //    to close the Comm file handle.
  122. //
  123. //
  124.  
  125. BOOL StartComm(HANDLE hNewCommFile)
  126. {
  127.     // Is this a valid comm handle?
  128.     if (GetFileType(hNewCommFile) != FILE_TYPE_CHAR)
  129.     {
  130.         OutputDebugString("File handle is not a comm handle.\n");
  131.         return FALSE;
  132.     }
  133.  
  134.     // Are we already doing comm?
  135.     if (g_hCommFile != NULL)
  136.     {
  137.         OutputDebugString("Already have a comm file open\n");
  138.         return FALSE;
  139.     }
  140.  
  141.     // Its ok to continue.
  142.  
  143.     g_hCommFile = hNewCommFile;
  144.  
  145.     // Setting and querying the comm port configurations.
  146.  
  147.     { // Configure the comm settings.
  148.         COMMTIMEOUTS commtimeouts;
  149.         DCB dcb;
  150.         COMMPROP commprop;
  151.         DWORD fdwEvtMask;
  152.  
  153.         // These are here just so you can set a breakpoint
  154.         // and see what the comm settings are.  Most Comm settings
  155.         // are already set through TAPI.
  156.         GetCommState(hNewCommFile, &dcb);
  157.         GetCommProperties(hNewCommFile, &commprop);
  158.         GetCommMask(g_hCommFile, &fdwEvtMask);
  159.         GetCommTimeouts(g_hCommFile, &commtimeouts);
  160.  
  161.  
  162.         // The CommTimeout numbers will very likely change if you are
  163.         // coding to meet some kind of specification where
  164.         // you need to reply within a certain amount of time after
  165.         // recieving the last byte.  However,  If 1/4th of a second
  166.         // goes by between recieving two characters, its a good 
  167.         // indication that the transmitting end has finished, even
  168.         // assuming a 1200 baud modem.
  169.  
  170.         commtimeouts.ReadIntervalTimeout         = 250;
  171.         commtimeouts.ReadTotalTimeoutMultiplier  = 0;
  172.         commtimeouts.ReadTotalTimeoutConstant    = 0;
  173.         commtimeouts.WriteTotalTimeoutMultiplier = 0;
  174.         commtimeouts.WriteTotalTimeoutConstant   = 0;
  175.  
  176.         SetCommTimeouts(g_hCommFile, &commtimeouts);
  177.  
  178.         // fAbortOnError is the only DCB dependancy in TapiComm.
  179.         // Can't guarentee that the SP will set this to what we expect.
  180.         dcb.fAbortOnError = FALSE;
  181.         SetCommState(hNewCommFile, &dcb);
  182.     }
  183.  
  184.     // Create the event that will signal the threads to close.
  185.     g_hCloseEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
  186.  
  187.     if (g_hCloseEvent == NULL)
  188.     {
  189.         OutputDebugLastError(GetLastError(), "Unable to CreateEvent: ");
  190.         g_hCommFile = NULL;
  191.         return FALSE;
  192.     }
  193.  
  194.     // Create the Read thread.
  195.     g_hReadThread =
  196.         CreateThread(NULL, 0, StartReadThreadProc, 0, 0, &g_dwReadThreadID);
  197.         
  198.     if (g_hReadThread == NULL)
  199.     {
  200.         OutputDebugLastError(GetLastError(),"Unable to create Read thread");
  201.  
  202.         g_dwReadThreadID = 0;
  203.         g_hCommFile = 0;
  204.         return FALSE;
  205.     }
  206.     
  207.     // Comm threads should to have a higher base priority than the UI thread.
  208.     // If they don't, then any temporary priority boost the UI thread gains
  209.     // could cause the COMM threads to loose data.
  210.     SetThreadPriority(g_hReadThread, THREAD_PRIORITY_HIGHEST);
  211.  
  212.     // Create the Write thread.
  213.     g_hWriteThread = 
  214.         CreateThread(NULL, 0, StartWriteThreadProc, 0, 0, &g_dwWriteThreadID);
  215.         
  216.     if (g_hWriteThread == NULL)
  217.     {
  218.         OutputDebugLastError(GetLastError(),"Unable to create Write thread");
  219.  
  220.         CloseReadThread();
  221.         g_dwWriteThreadID = 0;
  222.         g_hCommFile = 0;
  223.         return FALSE;
  224.     }
  225.     
  226.     SetThreadPriority(g_hWriteThread, THREAD_PRIORITY_ABOVE_NORMAL);
  227.  
  228.     // Everything was created ok.  Ready to go!
  229.     return TRUE;
  230. }
  231.  
  232.  
  233. //
  234. //  FUNCTION: StopComm
  235. //
  236. //  PURPOSE: Stop and end all communication threads.
  237. //
  238. //  PARAMETERS:
  239. //    none
  240. //
  241. //  RETURN VALUE:
  242. //    none
  243. //
  244. //  COMMENTS:
  245. //
  246. //    Tries to gracefully signal all communication threads to
  247. //    close, but terminates them if it has to.
  248. //
  249. //
  250.  
  251. void StopComm()
  252. {
  253.     // No need to continue if we're not communicating.
  254.     if (g_hCommFile == NULL)
  255.         return;
  256.  
  257.     OutputDebugString("Stopping the Comm\n");
  258.  
  259.     // Close the threads.
  260.     CloseReadThread();
  261.     CloseWriteThread();
  262.  
  263.     // Not needed anymore.
  264.     CloseHandle(g_hCloseEvent);
  265.  
  266.     // Now close the comm port handle.
  267.     CloseHandle(g_hCommFile);
  268.     g_hCommFile = NULL;
  269. }
  270.  
  271.  
  272. //
  273. //  FUNCTION: WriteCommString(LPCSTR, DWORD)
  274. //
  275. //  PURPOSE: Send a String to the Write Thread to be written to the Comm.
  276. //
  277. //  PARAMETERS:
  278. //    pszStringToWrite     - String to Write to Comm port. 
  279. //    nSizeofStringToWrite - length of pszStringToWrite.
  280. //
  281. //  RETURN VALUE:
  282. //    Returns TRUE if the PostMessage is successful.
  283. //    Returns FALSE if PostMessage fails or Write thread doesn't exist.
  284. //
  285. //  COMMENTS:
  286. //
  287. //    This is a wrapper function so that other modules don't care that
  288. //    Comm writing is done via PostMessage to a Write thread.  Note that
  289. //    using PostMessage speeds up response to the UI (very little delay to
  290. //    'write' a string) and provides a natural buffer if the comm is slow
  291. //    (ie:  the messages just pile up in the message queue).
  292. //
  293. //    Note that it is assumed that pszStringToWrite is allocated with
  294. //    LocalAlloc, and that if WriteCommString succeeds, its the job of the
  295. //    Write thread to LocalFree it.  If WriteCommString fails, then its
  296. //    the job of the calling function to free the string.
  297. //
  298. //
  299.  
  300. BOOL WriteCommString(LPCSTR lpszStringToWrite, DWORD dwSizeofStringToWrite)
  301. {
  302.     if (g_hWriteThread)
  303.     {
  304.         if (PostThreadMessage(g_dwWriteThreadID, PWM_COMMWRITE, 
  305.                 (WPARAM) dwSizeofStringToWrite, (LPARAM) lpszStringToWrite))
  306.         {
  307.             return TRUE;
  308.         }
  309.         else
  310.             OutputDebugString("Failed to Post to Write thread.\n");
  311.     }
  312.     else
  313.         OutputDebugString("Write thread not created\n");
  314.  
  315.     return FALSE;
  316. }
  317.  
  318.  
  319.  
  320. //*****************************************
  321. // The rest of the functions are intended for use
  322. // only within the CommCode module.
  323. //*****************************************
  324.  
  325.  
  326.  
  327. //
  328. //  FUNCTION: CloseReadThread
  329. //
  330. //  PURPOSE: Close the Read Thread.
  331. //
  332. //  PARAMETERS:
  333. //    none
  334. //
  335. //  RETURN VALUE:
  336. //    none
  337. //
  338. //  COMMENTS:
  339. //
  340. //    Closes the Read thread by signaling the CloseEvent.
  341. //    Purges any outstanding reads on the comm port.
  342. //
  343. //    Note that terminating a thread leaks memory (read the docs).
  344. //    Besides the normal leak incurred, there is an event object
  345. //    that doesn't get closed.  This isn't worth worrying about 
  346. //    since it shouldn't happen anyway.
  347. //
  348. //
  349.  
  350. void CloseReadThread()
  351. {
  352.     // If it exists...
  353.     if (g_hReadThread)
  354.     {
  355.         OutputDebugString("Closing Read Thread\n");
  356.  
  357.         // Signal the event to close the worker threads.
  358.         SetEvent(g_hCloseEvent);
  359.  
  360.         // Purge all outstanding reads
  361.         PurgeComm(g_hCommFile, PURGE_RXABORT | PURGE_RXCLEAR);
  362.  
  363.         // Wait 10 seconds for it to exit.  Shouldn't happen.
  364.         if (WaitForSingleObject(g_hReadThread, 10000) == WAIT_TIMEOUT)
  365.         {
  366.             OutputDebugString("Read thread not exiting.  Terminating it.\n");
  367.  
  368.             TerminateThread(g_hReadThread, 0);
  369.  
  370.             // The ReadThread cleans up these itself if it terminates
  371.             // normally.
  372.             CloseHandle(g_hReadThread);
  373.             g_hReadThread = 0;
  374.             g_dwReadThreadID = 0;
  375.         }
  376.     }
  377. }
  378.  
  379.  
  380. //
  381. //  FUNCTION: CloseWriteThread
  382. //
  383. //  PURPOSE: Closes the Write Thread.
  384. //
  385. //  PARAMETERS:
  386. //    none
  387. //
  388. //  RETURN VALUE:
  389. //    none
  390. //
  391. //  COMMENTS:
  392. //
  393. //    Closes the write thread by signaling the CloseEvent.
  394. //    Purges any outstanding writes on the comm port.
  395. //
  396. //    Note that terminating a thread leaks memory (read the docs).
  397. //    Besides the normal leak incurred, there is an event object
  398. //    that doesn't get closed.  This isn't worth worrying about 
  399. //    since it shouldn't happen anyway.
  400. //
  401. //
  402.  
  403. void CloseWriteThread()
  404. {
  405.     // If it exists...
  406.     if (g_hWriteThread)
  407.     {
  408.         OutputDebugString("Closing Write Thread\n");
  409.  
  410.         // Signal the event to close the worker threads.
  411.         SetEvent(g_hCloseEvent);
  412.  
  413.         // Purge all outstanding writes.
  414.         PurgeComm(g_hCommFile, PURGE_TXABORT | PURGE_TXCLEAR);
  415.  
  416.         // Wait 10 seconds for it to exit.  Shouldn't happen.
  417.         if (WaitForSingleObject(g_hWriteThread, 10000) == WAIT_TIMEOUT)
  418.         {
  419.             OutputDebugString("Write thread not exiting.  Terminating it.\n");
  420.  
  421.             TerminateThread(g_hWriteThread, 0);
  422.  
  423.             // The WriteThread cleans up these itself if it terminates
  424.             // normally.
  425.             CloseHandle(g_hWriteThread);
  426.             g_hWriteThread = 0;
  427.             g_dwWriteThreadID = 0;
  428.  
  429.         }
  430.     }
  431. }
  432.  
  433.  
  434. //
  435. //  FUNCTION: StartWriteThreadProc(LPVOID)
  436. //
  437. //  PURPOSE: The starting point for the Write thread.
  438. //
  439. //  PARAMETERS:
  440. //    lpvParam - unused.
  441. //
  442. //  RETURN VALUE:
  443. //    DWORD - unused.
  444. //
  445. //  COMMENTS:
  446. //
  447. //    The Write thread uses a PeekMessage loop to wait for a string to write,
  448. //    and when it gets one, it writes it to the Comm port.  If the CloseEvent
  449. //    object is signaled, then it exits.  The use of messages to tell the
  450. //    Write thread what to write provides a natural desynchronization between
  451. //    the UI and the Write thread.
  452. //
  453. //
  454.  
  455. DWORD WINAPI StartWriteThreadProc(LPVOID lpvParam)
  456. {
  457.     MSG msg;
  458.     DWORD dwHandleSignaled;
  459.  
  460.     // Needed for overlapped I/O.
  461.     OVERLAPPED overlappedWrite = {0, 0, 0, 0, NULL};
  462.  
  463.     overlappedWrite.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
  464.     if (overlappedWrite.hEvent == NULL)
  465.     {
  466.         OutputDebugLastError(GetLastError(), "Unable to CreateEvent: ");
  467.         PostHangupCall();
  468.         goto EndWriteThread;
  469.     }
  470.  
  471.     // This is the main loop.  Loop until we break out.
  472.     while (TRUE)
  473.     {
  474.         if (!PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
  475.         {
  476.             // If there are no messages pending, wait for a message or 
  477.             // the CloseEvent.
  478.             dwHandleSignaled = 
  479.                 MsgWaitForMultipleObjects(1, &g_hCloseEvent, FALSE,
  480.                     INFINITE, QS_ALLINPUT);
  481.  
  482.             switch(dwHandleSignaled)
  483.             {
  484.                 case WAIT_OBJECT_0:     // CloseEvent signaled!
  485.                 {
  486.                     // Time to exit.
  487.                     goto EndWriteThread;
  488.                 }
  489.  
  490.                 case WAIT_OBJECT_0 + 1: // New message was received.
  491.                 {
  492.                     // Get the message that woke us up by looping again.
  493.                     continue;
  494.                 }
  495.  
  496.                 case WAIT_FAILED:       // Wait failed.  Shouldn't happen.
  497.                 {
  498.                     OutputDebugLastError(GetLastError(),"Write WAIT_FAILED: ");
  499.                     PostHangupCall();
  500.                     goto EndWriteThread;
  501.                 }
  502.  
  503.                 default:                // This case should never occur.
  504.                 {
  505.                     OutputDebugPrintf("Unexpected Wait return value '%lx'",
  506.                         dwHandleSignaled);
  507.                     PostHangupCall();
  508.                     goto EndWriteThread;
  509.                 }
  510.  
  511.             }
  512.         }
  513.  
  514.         // Make sure the CloseEvent isn't signaled while retrieving messages.
  515.         if (WAIT_TIMEOUT != WaitForSingleObject(g_hCloseEvent,0))
  516.             goto EndWriteThread;
  517.  
  518.         // Process the message.
  519.  
  520.         // This could happen if a dialog is created on this thread.
  521.         // This doesn't occur in this sample, but might if modified.
  522.         if (msg.hwnd != NULL)
  523.         {
  524.             TranslateMessage(&msg);
  525.             DispatchMessage(&msg);
  526.  
  527.             continue;
  528.         }
  529.  
  530.         // Handle the message.
  531.         switch(msg.message)
  532.         {
  533.             case PWM_COMMWRITE:  // New string to write to Comm port.
  534.             {
  535.                 OutputDebugString("Writing to comm port\n");
  536.  
  537.                 // Write the string to the comm port.  HandleWriteData
  538.                 // does not return until the whole string has been written,
  539.                 // an error occurs or until the CloseEvent is signaled.
  540.                 if (!HandleWriteData(&overlappedWrite,
  541.                         (LPSTR) msg.lParam, (DWORD) msg.wParam))
  542.                 {
  543.                     // If it failed, either we got a signal to end or there
  544.                     // really was a failure.
  545.  
  546.                     LocalFree((HLOCAL) msg.lParam); 
  547.                     goto EndWriteThread;
  548.                 }
  549.  
  550.                 // Data was sent in a LocalAlloc()d buffer.  Must free it.
  551.                 LocalFree((HLOCAL) msg.lParam); 
  552.                 break;
  553.             }
  554.     
  555.  
  556.             // What other messages could the thread get?
  557.             default:
  558.             {
  559.                 char Output[256];
  560.     
  561.                 wsprintf(Output,
  562.                     "Unexpected message posted to Write thread: %ui\n",
  563.                     msg.message );
  564.                     
  565.                 OutputDebugString(Output);
  566.                 break;
  567.             }
  568.         } // End of switch(message)
  569.  
  570.     } // End of main loop.
  571.  
  572.     // Thats the end.  Now clean up.
  573.   EndWriteThread:
  574.  
  575.     OutputDebugString("Write thread shutting down\n");
  576.  
  577.     PurgeComm(g_hCommFile, PURGE_TXABORT | PURGE_TXCLEAR);
  578.  
  579.     CloseHandle(overlappedWrite.hEvent);
  580.  
  581.     g_dwWriteThreadID = 0;
  582.     CloseHandle(g_hWriteThread);
  583.     g_hWriteThread = 0;
  584.  
  585.     return 0;
  586. }
  587.  
  588.  
  589. //
  590. //  FUNCTION: HandleWriteData(LPOVERLAPPED, LPCSTR, DWORD)
  591. //
  592. //  PURPOSE: Writes a given string to the comm file handle.
  593. //
  594. //  PARAMETERS:
  595. //    lpOverlappedWrite      - Overlapped structure to use in WriteFile
  596. //    lpszStringToWrite      - String to write.
  597. //    dwNumberOfBytesToWrite - Length of String to write.
  598. //
  599. //  RETURN VALUE:
  600. //    TRUE if all bytes were written.  False if there was a failure to
  601. //    write the whole string.
  602. //
  603. //  COMMENTS:
  604. //
  605. //    This function is a helper function for the Write Thread.  It
  606. //    is this call that actually writes a string to the comm file.
  607. //    Note that this call blocks and waits for the Write to complete
  608. //    or for the CloseEvent object to signal that the thread should end.
  609. //    Another possible reason for returning FALSE is if the comm port
  610. //    is closed by the service provider.
  611. //
  612. //
  613.  
  614. BOOL HandleWriteData(LPOVERLAPPED lpOverlappedWrite,
  615.     LPCSTR lpszStringToWrite, DWORD dwNumberOfBytesToWrite)
  616. {
  617.     DWORD dwLastError;
  618.  
  619.     DWORD dwNumberOfBytesWritten = 0;
  620.     DWORD dwWhereToStartWriting = 0; // Start at the beginning.
  621.  
  622.     DWORD dwHandleSignaled;
  623.     HANDLE HandlesToWaitFor[2];
  624.  
  625.     HandlesToWaitFor[0] = g_hCloseEvent;
  626.     HandlesToWaitFor[1] = lpOverlappedWrite -> hEvent;
  627.  
  628.     // Keep looping until all characters have been written.
  629.     do
  630.     {
  631.         // Start the overlapped I/O.
  632.         if (!WriteFile(g_hCommFile, 
  633.                 &lpszStringToWrite[ dwWhereToStartWriting ], 
  634.                 dwNumberOfBytesToWrite, &dwNumberOfBytesWritten,
  635.                 lpOverlappedWrite))
  636.         {
  637.             // WriteFile failed.  Expected; lets handle it.
  638.             dwLastError = GetLastError();
  639.  
  640.             // Its possible for this error to occur if the 
  641.             // service provider has closed the port.  Time to end.
  642.             if (dwLastError == ERROR_INVALID_HANDLE)
  643.             {
  644.                 OutputDebugString("ERROR_INVALID_HANDLE, "
  645.                     "Likely that the Service Provider has closed the port.\n");
  646.                 return FALSE;
  647.             }
  648.  
  649.             // Unexpected error.  No idea what.
  650.             if (dwLastError != ERROR_IO_PENDING)
  651.             {
  652.                 OutputDebugLastError(dwLastError,
  653.                     "Error to writing to CommFile");
  654.                 
  655.                 OutputDebugString("Closing TAPI\n");
  656.                 PostHangupCall();
  657.                 return FALSE;
  658.             }
  659.  
  660.             // This is the expected ERROR_IO_PENDING case.
  661.  
  662.  
  663.             // Wait for either overlapped I/O completion,
  664.             // or for the CloseEvent to get signaled.
  665.             dwHandleSignaled = 
  666.                 WaitForMultipleObjects(2, HandlesToWaitFor, 
  667.                     FALSE, INFINITE);
  668.  
  669.             switch(dwHandleSignaled)
  670.             {
  671.                 case WAIT_OBJECT_0:     // CloseEvent signaled!
  672.                 {
  673.                     // Time to exit.
  674.                     return FALSE;
  675.                 }
  676.  
  677.                 case WAIT_OBJECT_0 + 1: // Wait finished.
  678.                 {
  679.                     // Time to get the results of the WriteFile
  680.                     break;
  681.                 }
  682.  
  683.                 case WAIT_FAILED: // Wait failed.  Shouldn't happen.
  684.                 {
  685.                     OutputDebugLastError(GetLastError(), 
  686.                         "Write WAIT_FAILED: ");
  687.                     PostHangupCall();
  688.                     return FALSE;
  689.                 }
  690.  
  691.                 default: // This case should never occur.
  692.                 {
  693.                     OutputDebugPrintf(
  694.                         "Unexpected Wait return value '%lx'",
  695.                         dwHandleSignaled);
  696.                     PostHangupCall();
  697.                     return FALSE;
  698.                 }
  699.             }
  700.  
  701.             if (!GetOverlappedResult(g_hCommFile,
  702.                      lpOverlappedWrite,
  703.                      &dwNumberOfBytesWritten, TRUE))
  704.             {
  705.                 dwLastError = GetLastError();
  706.  
  707.                 // Its possible for this error to occur if the 
  708.                 // service provider has closed the port.
  709.                 if (dwLastError == ERROR_INVALID_HANDLE)
  710.                 {
  711.                     OutputDebugString("ERROR_INVALID_HANDLE, "
  712.                         "Likely that the Service Provider has closed the port.\n");
  713.                     return FALSE;
  714.                 }
  715.  
  716.                 // No idea what could cause another error.
  717.                 OutputDebugLastError(dwLastError,
  718.                     "Error writing to CommFile while waiting");
  719.                 OutputDebugString("Closing TAPI\n");
  720.                 PostHangupCall();
  721.                 return FALSE;
  722.             }
  723.         }
  724.  
  725.         // Some data was written.  Make sure it all got written.
  726.  
  727.         dwNumberOfBytesToWrite -= dwNumberOfBytesWritten;
  728.         dwWhereToStartWriting += dwNumberOfBytesWritten;
  729.     }
  730.     while(dwNumberOfBytesToWrite > 0);  // Write the whole thing!
  731.  
  732.     // Wrote the whole string.
  733.     return TRUE;
  734. }
  735.  
  736.  
  737. //
  738. //  FUNCTION: StartReadThreadProc(LPVOID)
  739. //
  740. //  PURPOSE: This is the starting point for the Read Thread.
  741. //
  742. //  PARAMETERS:
  743. //    lpvParam - unused.
  744. //
  745. //  RETURN VALUE:
  746. //    DWORD - unused.
  747. //
  748. //  COMMENTS:
  749. //
  750. //    The Read Thread uses overlapped ReadFile and sends any strings
  751. //    read from the comm port to the UI to be printed.  This is
  752. //    eventually done through a PostMessage so that the Read Thread
  753. //    is never away from the comm port very long.  This also provides
  754. //    natural desynchronization between the Read thread and the UI.
  755. //
  756. //    If the CloseEvent object is signaled, the Read Thread exits.
  757. //
  758. //    Note that there is absolutely *no* interpretation of the data,
  759. //    which means no terminal emulation.  It basically means that this
  760. //    sample is pretty useless as a TTY program.
  761. //
  762. //      Separating the Read and Write threads is natural for a application
  763. //    like this sample where there is no need for synchronization between
  764. //    reading and writing.  However, if there is such a need (for example,
  765. //    most file transfer algorithms synchronize the reading and writing),
  766. //    then it would make a lot more sense to have a single thread to handle
  767. //    both reading and writing.
  768. //
  769. //
  770.  
  771. DWORD WINAPI StartReadThreadProc(LPVOID lpvParam)
  772. {
  773.     char szInputBuffer[INPUTBUFFERSIZE];
  774.     DWORD nNumberOfBytesRead;
  775.  
  776.     HANDLE HandlesToWaitFor[3];
  777.     DWORD dwHandleSignaled;
  778.  
  779.     DWORD fdwEvtMask;
  780.  
  781.     // Needed for overlapped I/O (ReadFile)
  782.     OVERLAPPED overlappedRead  = {0, 0, 0, 0, NULL};
  783.  
  784.     // Needed for overlapped Comm Event handling.
  785.     OVERLAPPED overlappedCommEvent = {0, 0, 0, 0, NULL};
  786.  
  787.     // Lets put an event in the Read overlapped structure.
  788.     overlappedRead.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
  789.     if (overlappedRead.hEvent == NULL)
  790.     {
  791.         OutputDebugLastError(GetLastError(), "Unable to CreateEvent: ");
  792.         PostHangupCall();
  793.         goto EndReadThread;
  794.     }
  795.  
  796.     // And an event for the CommEvent overlapped structure.
  797.     overlappedCommEvent.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
  798.     if (overlappedCommEvent.hEvent == NULL)
  799.     {
  800.         OutputDebugLastError(GetLastError(), "Unable to CreateEvent: ");
  801.         PostHangupCall();
  802.         goto EndReadThread;
  803.     }
  804.  
  805.     // We will be waiting on these objects.
  806.     HandlesToWaitFor[0] = g_hCloseEvent;
  807.     HandlesToWaitFor[1] = overlappedCommEvent.hEvent;
  808.     HandlesToWaitFor[2] = overlappedRead.hEvent;
  809.  
  810.  
  811.     // Setup CommEvent handling.
  812.  
  813.     // Set the comm mask so we receive error signals.
  814.     if (!SetCommMask(g_hCommFile, EV_ERR))
  815.     {
  816.         OutputDebugLastError(GetLastError(),"Unable to SetCommMask: ");
  817.         PostHangupCall();
  818.         goto EndReadThread;
  819.     }
  820.  
  821.     // Start waiting for CommEvents (Errors)
  822.     if (!SetupCommEvent(&overlappedCommEvent, &fdwEvtMask))
  823.     {
  824.         PostHangupCall();
  825.         goto EndReadThread;
  826.     }
  827.  
  828.     // Start waiting for Read events.
  829.     if (!SetupReadEvent(&overlappedRead,
  830.                 szInputBuffer, INPUTBUFFERSIZE,
  831.                 &nNumberOfBytesRead))
  832.     {
  833.         PostHangupCall();
  834.         goto EndReadThread;
  835.     }
  836.  
  837.     // Keep looping until we break out.
  838.     while (TRUE)
  839.     {
  840.         // Wait until some event occurs (data to read; error; stopping).
  841.         dwHandleSignaled = 
  842.             WaitForMultipleObjects(3, HandlesToWaitFor,
  843.                 FALSE, INFINITE);
  844.  
  845.         // Which event occured?
  846.         switch(dwHandleSignaled)
  847.         {
  848.             case WAIT_OBJECT_0:     // Signal to end the thread.
  849.             {
  850.                 // Time to exit.
  851.                 goto EndReadThread;
  852.             }
  853.  
  854.             case WAIT_OBJECT_0 + 1: // CommEvent signaled.
  855.             {
  856.                 // Handle the CommEvent.
  857.                 if (!HandleCommEvent(&overlappedCommEvent, &fdwEvtMask, TRUE))
  858.                 {
  859.                     PostHangupCall();
  860.                     goto EndReadThread;
  861.                 }
  862.  
  863.                 // Start waiting for the next CommEvent.
  864.                 if (!SetupCommEvent(&overlappedCommEvent, &fdwEvtMask))
  865.                 {
  866.                     PostHangupCall();
  867.                     goto EndReadThread;
  868.                 }
  869.                 break;
  870.             }
  871.  
  872.             case WAIT_OBJECT_0 + 2: // Read Event signaled.
  873.             {
  874.                 // Get the new data!
  875.                 if (!HandleReadEvent(&overlappedRead,
  876.                             szInputBuffer, INPUTBUFFERSIZE,
  877.                             &nNumberOfBytesRead))
  878.                 {
  879.                     PostHangupCall();
  880.                     goto EndReadThread;
  881.                 }
  882.  
  883.                 // Wait for more new data.
  884.                 if (!SetupReadEvent(&overlappedRead,
  885.                             szInputBuffer, INPUTBUFFERSIZE,
  886.                             &nNumberOfBytesRead))
  887.                 {
  888.                     PostHangupCall();
  889.                     goto EndReadThread;
  890.                 }
  891.                 break;
  892.             }
  893.  
  894.             case WAIT_FAILED:       // Wait failed.  Shouldn't happen.
  895.             {
  896.                 OutputDebugLastError(GetLastError(),"Read WAIT_FAILED: ");
  897.                 PostHangupCall();
  898.                 goto EndReadThread;
  899.             }
  900.  
  901.             default:    // This case should never occur.
  902.             {
  903.                 OutputDebugPrintf("Unexpected Wait return value '%lx'",
  904.                     dwHandleSignaled);
  905.                 PostHangupCall();
  906.                 goto EndReadThread;
  907.             }
  908.         } // End of switch(dwHandleSignaled).
  909.  
  910.     } // End of while(TRUE) loop.
  911.  
  912.  
  913.     // Time to clean up Read Thread.
  914.   EndReadThread:
  915.  
  916.     OutputDebugString("Read thread shutting down\n");
  917.     PurgeComm(g_hCommFile, PURGE_RXABORT | PURGE_RXCLEAR);
  918.     CloseHandle(overlappedRead.hEvent);
  919.     CloseHandle(overlappedCommEvent.hEvent);
  920.     g_dwReadThreadID = 0;
  921.     CloseHandle(g_hReadThread);
  922.     g_hReadThread = 0;
  923.     return 0;
  924. }
  925.  
  926.  
  927. //
  928. //  FUNCTION: SetupReadEvent(LPOVERLAPPED, LPSTR, DWORD, LPDWORD)
  929. //
  930. //  PURPOSE: Sets up an overlapped ReadFile
  931. //
  932. //  PARAMETERS:
  933. //    lpOverlappedRead      - address of overlapped structure to use.
  934. //    lpszInputBuffer       - Buffer to place incoming bytes.
  935. //    dwSizeofBuffer        - size of lpszInputBuffer.
  936. //    lpnNumberOfBytesRead  - address of DWORD to place the number of read bytes.
  937. //
  938. //  RETURN VALUE:
  939. //    TRUE if able to successfully setup the ReadFile.  FALSE if there
  940. //    was a failure setting up or if the CloseEvent object was signaled.
  941. //
  942. //  COMMENTS:
  943. //
  944. //    This function is a helper function for the Read Thread.  This
  945. //    function sets up the overlapped ReadFile so that it can later
  946. //    be waited on (or more appropriatly, so the event in the overlapped
  947. //    structure can be waited upon).  If there is data waiting, it is
  948. //    handled and the next ReadFile is initiated.
  949. //    Another possible reason for returning FALSE is if the comm port
  950. //    is closed by the service provider.
  951. //    
  952. //
  953. //
  954.  
  955. BOOL SetupReadEvent(LPOVERLAPPED lpOverlappedRead,
  956.     LPSTR lpszInputBuffer, DWORD dwSizeofBuffer,
  957.     LPDWORD lpnNumberOfBytesRead)
  958. {
  959.     DWORD dwLastError;
  960.  
  961.   StartSetupReadEvent:
  962.  
  963.     // Make sure the CloseEvent hasn't been signaled yet.
  964.     // Check is needed because this function is potentially recursive.
  965.     if (WAIT_TIMEOUT != WaitForSingleObject(g_hCloseEvent,0))
  966.         return FALSE;
  967.     
  968.     // Start the overlapped ReadFile.
  969.     if (ReadFile(g_hCommFile, 
  970.             lpszInputBuffer, dwSizeofBuffer,
  971.             lpnNumberOfBytesRead, lpOverlappedRead))
  972.     {
  973.         // This would only happen if there was data waiting to be read.
  974.  
  975.         OutputDebugString("Data waiting for ReadFile.\n");
  976.         
  977.         // Handle the data.
  978.         if (!HandleReadData(lpszInputBuffer, *lpnNumberOfBytesRead))
  979.         {
  980.             return FALSE;
  981.         }
  982.  
  983.         // Start waiting for more data.
  984.         goto StartSetupReadEvent;
  985.     }
  986.  
  987.     // ReadFile failed.  Expected because of overlapped I/O.
  988.     dwLastError = GetLastError();
  989.  
  990.  
  991.     // LastError was ERROR_IO_PENDING, as expected.
  992.     if (dwLastError == ERROR_IO_PENDING)
  993.     {
  994.         OutputDebugString("Waiting for data from comm connection.\n");
  995.         return TRUE;
  996.     }
  997.  
  998.     // Its possible for this error to occur if the 
  999.     // service provider has closed the port.  Time to end.
  1000.     if (dwLastError == ERROR_INVALID_HANDLE)
  1001.     {
  1002.         OutputDebugString("ERROR_INVALID_HANDLE, "
  1003.             "Likely that the Service Provider has closed the port.\n");
  1004.         return FALSE;
  1005.     }
  1006.  
  1007.     // Unexpected error. No idea what could cause this to happen.
  1008.     OutputDebugLastError(dwLastError,"Unexpected ReadFile error: ");
  1009.     
  1010.     PostHangupCall();
  1011.     return FALSE;
  1012. }
  1013.  
  1014.  
  1015. //
  1016. //  FUNCTION: HandleReadEvent(LPOVERLAPPED, LPSTR, DWORD, LPDWORD)
  1017. //
  1018. //  PURPOSE: Retrieves and handles data when there is data ready.
  1019. //
  1020. //  PARAMETERS:
  1021. //    lpOverlappedRead      - address of overlapped structure to use.
  1022. //    lpszInputBuffer       - Buffer to place incoming bytes.
  1023. //    dwSizeofBuffer        - size of lpszInputBuffer.
  1024. //    lpnNumberOfBytesRead  - address of DWORD to place the number of read bytes.
  1025. //
  1026. //  RETURN VALUE:
  1027. //    TRUE if able to successfully retrieve and handle the available data.
  1028. //    FALSE if unable to retrieve or handle the data.
  1029. //
  1030. //  COMMENTS:
  1031. //
  1032. //    This function is another helper function for the Read Thread.  This
  1033. //    is the function that is called when there is data available after
  1034. //    an overlapped ReadFile has been setup.  It retrieves the data and
  1035. //    handles it.
  1036. //
  1037. //
  1038.  
  1039. BOOL HandleReadEvent(LPOVERLAPPED lpOverlappedRead,
  1040.     LPSTR lpszInputBuffer, DWORD dwSizeofBuffer,
  1041.     LPDWORD lpnNumberOfBytesRead)
  1042. {
  1043.     DWORD dwLastError;
  1044.  
  1045.     if (GetOverlappedResult(g_hCommFile,
  1046.             lpOverlappedRead, lpnNumberOfBytesRead, FALSE))
  1047.     {
  1048.         return HandleReadData(lpszInputBuffer, *lpnNumberOfBytesRead);
  1049.     }
  1050.  
  1051.     // Error in GetOverlappedResult; handle it.
  1052.  
  1053.     dwLastError = GetLastError();
  1054.  
  1055.     // Its possible for this error to occur if the 
  1056.     // service provider has closed the port.  Time to end.
  1057.     if (dwLastError == ERROR_INVALID_HANDLE)
  1058.     {
  1059.         OutputDebugString("ERROR_INVALID_HANDLE, "
  1060.             "Likely that the Service Provider has closed the port.\n");
  1061.         return FALSE;
  1062.     }
  1063.  
  1064.     OutputDebugLastError(dwLastError, 
  1065.         "Unexpected GetOverlappedResult Read Error: ");
  1066.  
  1067.     PostHangupCall();
  1068.     return FALSE;
  1069. }
  1070.  
  1071.  
  1072. //
  1073. //  FUNCTION: HandleReadData(LPCSTR, DWORD)
  1074. //
  1075. //  PURPOSE: Deals with data after its been read from the comm file.
  1076. //
  1077. //  PARAMETERS:
  1078. //    lpszInputBuffer  - Buffer to place incoming bytes.
  1079. //    dwSizeofBuffer   - size of lpszInputBuffer.
  1080. //
  1081. //  RETURN VALUE:
  1082. //    TRUE if able to successfully handle the data.
  1083. //    FALSE if unable to allocate memory or handle the data.
  1084. //
  1085. //  COMMENTS:
  1086. //
  1087. //    This function is yet another helper function for the Read Thread.
  1088. //    It LocalAlloc()s a buffer, copies the new data to this buffer and
  1089. //    calls PostWriteToDisplayCtl to let the EditCtls module deal with
  1090. //    the data.  Its assumed that PostWriteToDisplayCtl posts the message
  1091. //    rather than dealing with it right away so that the Read Thread
  1092. //    is free to get right back to waiting for data.  Its also assumed
  1093. //    that the EditCtls module is responsible for LocalFree()ing the
  1094. //    pointer that is passed on.
  1095. //
  1096. //
  1097.  
  1098. BOOL HandleReadData(LPCSTR lpszInputBuffer, DWORD dwSizeofBuffer)
  1099. {
  1100.     // If we got data and didn't just time out empty...
  1101.     if (dwSizeofBuffer)
  1102.     {
  1103.         LPSTR lpszPostedBytes;
  1104.  
  1105.         // Do something with the bytes read.
  1106.         OutputDebugString("Got something from Comm port!!!\n");
  1107.  
  1108.         lpszPostedBytes = LocalAlloc(LPTR,dwSizeofBuffer+1);
  1109.         if (lpszPostedBytes == NULL)
  1110.         {
  1111.             OutputDebugLastError(GetLastError(), "LocalAlloc: ");
  1112.             return FALSE;
  1113.         }
  1114.  
  1115.         memcpy(lpszPostedBytes, lpszInputBuffer, dwSizeofBuffer);
  1116.         lpszPostedBytes[dwSizeofBuffer] = '\0';
  1117.  
  1118.         return PostWriteToDisplayCtl(lpszPostedBytes, dwSizeofBuffer);
  1119.     }
  1120.  
  1121. }
  1122.  
  1123.  
  1124. //
  1125. //  FUNCTION: SetupCommEvent(LPOVERLAPPED, LPDWORD)
  1126. //
  1127. //  PURPOSE: Sets up the overlapped WaitCommEvent call.
  1128. //
  1129. //  PARAMETERS:
  1130. //    lpOverlappedCommEvent - Pointer to the overlapped structure to use.
  1131. //    lpfdwEvtMask          - Pointer to DWORD to received Event data.
  1132. //
  1133. //  RETURN VALUE:
  1134. //    TRUE if able to successfully setup the WaitCommEvent.
  1135. //    FALSE if unable to setup WaitCommEvent, unable to handle
  1136. //    an existing outstanding event or if the CloseEvent has been signaled.
  1137. //
  1138. //  COMMENTS:
  1139. //
  1140. //    This function is a helper function for the Read Thread that sets up
  1141. //    the WaitCommEvent so we can deal with comm events (like Comm errors)
  1142. //    if they occur.
  1143. //
  1144. //
  1145.  
  1146. BOOL SetupCommEvent(LPOVERLAPPED lpOverlappedCommEvent,
  1147.     LPDWORD lpfdwEvtMask)
  1148. {
  1149.     DWORD dwLastError;
  1150.  
  1151.   StartSetupCommEvent:
  1152.  
  1153.     // Make sure the CloseEvent hasn't been signaled yet.
  1154.     // Check is needed because this function is potentially recursive.
  1155.     if (WAIT_TIMEOUT != WaitForSingleObject(g_hCloseEvent,0))
  1156.         return FALSE;
  1157.  
  1158.     // Start waiting for Comm Errors.
  1159.     if (WaitCommEvent(g_hCommFile, lpfdwEvtMask, lpOverlappedCommEvent))
  1160.     {
  1161.         // This could happen if there was an error waiting on the
  1162.         // comm port.  Lets try and handle it.
  1163.  
  1164.         OutputDebugString("Event (Error) waiting before WaitCommEvent.\n");
  1165.  
  1166.         if (!HandleCommEvent(NULL, lpfdwEvtMask, FALSE))
  1167.             return FALSE;
  1168.  
  1169.         // What could cause infinite recursion at this point?
  1170.         goto StartSetupCommEvent;
  1171.     }
  1172.  
  1173.     // We expect ERROR_IO_PENDING returned from WaitCommEvent
  1174.     // because we are waiting with an overlapped structure.
  1175.  
  1176.     dwLastError = GetLastError();
  1177.  
  1178.     // LastError was ERROR_IO_PENDING, as expected.
  1179.     if (dwLastError == ERROR_IO_PENDING)
  1180.     {
  1181.         OutputDebugString("Waiting for a CommEvent (Error) to occur.\n");
  1182.         return TRUE;
  1183.     }
  1184.  
  1185.     // Its possible for this error to occur if the 
  1186.     // service provider has closed the port.  Time to end.
  1187.     if (dwLastError == ERROR_INVALID_HANDLE)
  1188.     {
  1189.         OutputDebugString("ERROR_INVALID_HANDLE, "
  1190.             "Likely that the Service Provider has closed the port.\n");
  1191.         return FALSE;
  1192.     }
  1193.  
  1194.     // Unexpected error. No idea what could cause this to happen.
  1195.     OutputDebugLastError(dwLastError, "Unexpected WaitCommEvent error: ");
  1196.     return FALSE;
  1197. }
  1198.  
  1199.  
  1200. //
  1201. //  FUNCTION: HandleCommEvent(LPOVERLAPPED, LPDWORD, BOOL)
  1202. //
  1203. //  PURPOSE: Handle an outstanding Comm Event.
  1204. //
  1205. //  PARAMETERS:
  1206. //    lpOverlappedCommEvent - Pointer to the overlapped structure to use.
  1207. //    lpfdwEvtMask          - Pointer to DWORD to received Event data.
  1208. //     fRetrieveEvent       - Flag to signal if the event needs to be
  1209. //                            retrieved, or has already been retrieved.
  1210. //
  1211. //  RETURN VALUE:
  1212. //    TRUE if able to handle a Comm Event.
  1213. //    FALSE if unable to setup WaitCommEvent, unable to handle
  1214. //    an existing outstanding event or if the CloseEvent has been signaled.
  1215. //
  1216. //  COMMENTS:
  1217. //
  1218. //    This function is a helper function for the Read Thread that (if
  1219. //    fRetrieveEvent == TRUE) retrieves an outstanding CommEvent and
  1220. //    deals with it.  The only event that should occur is an EV_ERR event,
  1221. //    signalling that there has been an error on the comm port.
  1222. //
  1223. //    Normally, comm errors would not be put into the normal data stream
  1224. //    as this sample is demonstrating.  Putting it in a status bar would
  1225. //    be more appropriate for a real application.
  1226. //
  1227. //
  1228.  
  1229. BOOL HandleCommEvent(LPOVERLAPPED lpOverlappedCommEvent, 
  1230.     LPDWORD lpfdwEvtMask, BOOL fRetrieveEvent)
  1231. {
  1232.     DWORD dwDummy;
  1233.     LPSTR lpszOutput;
  1234.     char szError[128] = "";
  1235.     DWORD dwErrors;
  1236.     DWORD nOutput;
  1237.     DWORD dwLastError;
  1238.  
  1239.  
  1240.     lpszOutput = LocalAlloc(LPTR,256);
  1241.     if (lpszOutput == NULL)
  1242.     {
  1243.         OutputDebugLastError(GetLastError(), "LocalAlloc: ");
  1244.         return FALSE;
  1245.     }
  1246.  
  1247.     // If this fails, it could be because the file was closed (and I/O is
  1248.     // finished) or because the overlapped I/O is still in progress.  In
  1249.     // either case (or any others) its a bug and return FALSE.
  1250.     if (fRetrieveEvent)
  1251.         if (!GetOverlappedResult(g_hCommFile, 
  1252.                 lpOverlappedCommEvent, &dwDummy, FALSE))
  1253.         {
  1254.             dwLastError = GetLastError();
  1255.  
  1256.             // Its possible for this error to occur if the 
  1257.             // service provider has closed the port.  Time to end.
  1258.             if (dwLastError == ERROR_INVALID_HANDLE)
  1259.             {
  1260.                 OutputDebugString("ERROR_INVALID_HANDLE, "
  1261.                     "Likely that the Service Provider has closed the port.\n");
  1262.                 return FALSE;
  1263.             }
  1264.  
  1265.             OutputDebugLastError(dwLastError,
  1266.                 "Unexpected GetOverlappedResult for WaitCommEvent: ");
  1267.             return FALSE;
  1268.         }
  1269.  
  1270.     // Was the event an error?
  1271.     if (*lpfdwEvtMask & EV_ERR)
  1272.     {
  1273.         // Which error was it?
  1274.         if (!ClearCommError(g_hCommFile, &dwErrors, NULL))
  1275.         {
  1276.             dwLastError = GetLastError();
  1277.  
  1278.             // Its possible for this error to occur if the 
  1279.             // service provider has closed the port.  Time to end.
  1280.             if (dwLastError == ERROR_INVALID_HANDLE)
  1281.             {
  1282.                 OutputDebugString("ERROR_INVALID_HANDLE, "
  1283.                     "Likely that the Service Provider has closed the port.\n");
  1284.                 return FALSE;
  1285.             }
  1286.  
  1287.             OutputDebugLastError(GetLastError(),"ClearCommError: ");
  1288.             return FALSE;
  1289.         }
  1290.  
  1291.         // Its possible that multiple errors occured and were handled
  1292.         // in the last ClearCommError.  Because all errors were signaled
  1293.         // individually, but cleared all at once, pending comm events 
  1294.         // can yield EV_ERR while dwErrors equals 0.  Ignore this event.
  1295.         if (dwErrors == 0)
  1296.         {
  1297.             strcat(szError, "NULL Error");
  1298.         }
  1299.        
  1300.         if (dwErrors & CE_FRAME)
  1301.         {
  1302.             if (szError[0])
  1303.                 strcat(szError," and ");
  1304.  
  1305.             strcat(szError,"CE_FRAME");
  1306.         }
  1307.  
  1308.         if (dwErrors & CE_OVERRUN)
  1309.         {
  1310.             if (szError[0])
  1311.                 strcat(szError," and ");
  1312.  
  1313.             strcat(szError,"CE_OVERRUN");
  1314.         }
  1315.  
  1316.         if (dwErrors & CE_RXPARITY)
  1317.         {
  1318.             if (szError[0])
  1319.                 strcat(szError," and ");
  1320.  
  1321.             strcat(szError,"CE_RXPARITY");
  1322.         }
  1323.  
  1324.         if (dwErrors & ~ (CE_FRAME | CE_OVERRUN | CE_RXPARITY))
  1325.         {
  1326.             if (szError[0])
  1327.                 strcat(szError," and ");
  1328.  
  1329.             strcat(szError,"EV_ERR Unknown EvtMask");
  1330.         }
  1331.  
  1332.  
  1333.         nOutput = wsprintf(lpszOutput,
  1334.             "Comm Event: '%s', EvtMask = '%lx'\n",
  1335.             szError, dwErrors);
  1336.  
  1337.         PostWriteToDisplayCtl(lpszOutput, nOutput);
  1338.         return TRUE;
  1339.  
  1340.     }
  1341.  
  1342.     // Should not have gotten here.  Only interested in ERR conditions.
  1343.  
  1344.     OutputDebugPrintf("Unexpected comm event %lx",*lpfdwEvtMask);
  1345.     return FALSE;
  1346. }
  1347.