CommCode.C
上传用户:woweijixie
上传日期:2018-12-11
资源大小:131k
文件大小:41k
源码类别:

TAPI编程

开发平台:

Visual C++

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