Bug in Win32file WaitCommEvent ???

Grant Edwards grante at visi.com
Tue Nov 19 19:18:39 EST 2002


In article <zLyC9.17461$nK4.48560 at news-server.bigpond.net.au>, Mark Hammond wrote:

>> I'm trying to figure out how WaitCommEvent() works in Python, and it looks
>> like there's a problem when it's used in overlapped mode.
>> 
>> My understanding is that (in C) you:
>> 
>>  1) Call WaitCommEvent() and give it three parameters:
>>      * File handle
>>      * a pointer to where the output mask value is to be stored
>>      * a pointer to an overlapped struct
>>     
>>  2) WaitCommEvent() returns immediately.
>> 
>>  3) Call one of the WaitXXX() functions to wait on the event in
>>     the overloapped struct.
>> 
>>  4) When the event is triggered WaitXXX() returns. Check the
>>     value stored via the output mask pointer passed in 1)
>>     above.
>> 
>> [I've gleaned most of this from
>> http://msdn.microsoft.com/library/en-us/devio/base/waitcommevent.asp]
> 
> I don't think this last step is correct.

Perhaps.  If it isn't correct, where does the event mask get returned? [I'd
try to generate a test case in C, but I don't have a Win32 C compiler.]
Remember that in overlapped mode, the call to WaitCommEvent() can return
_before_ the event actually happens, just as a call to ReadFile() or
WriteFile() can return before data has been transferred.

> Certainly the link you posted doesn't say this is true.

Here's the part I was reading:

    Remarks

    The WaitCommEvent function monitors a set of events for a specified
    communications resource. To set and query the current event mask of a
    communications resource, use the SetCommMask and GetCommMask functions.

    If the overlapped operation cannot be completed immediately, the
    function returns FALSE and the GetLastError function returns
    ERROR_IO_PENDING, indicating that the operation is executing in the
    background.

This means to me that WaitCommEvent can return immediately (before the event
has happened).  Therefore, the "mask" variable can not contain valid info
upon return from WaitCommEvent. IOW, the event which "mask" is to identify
hasn't happened yet.

    When this happens, the system sets the hEvent member of the OVERLAPPED
    structure to the not-signaled state before WaitCommEvent returns, and
    then it sets it to the signaled state when one of the specified events
    or an error occurs. The calling process can use one of the wait
    functions to determine the event object's state
  
Since WaitCommEvent returned immediately (none of the masked events has
happened yet) you have to use WaitXXX() if you want to know when one of the
selected events does happen.
  
    and then use the GetOverlappedResult function to determine the results
    of the WaitCommEvent operation.
  
To me the phrase "and then use the GetOverlappedResult function" implies
that this happens _after_ WaitXXX() has returned.  The reasons for calling
GetOverlappedResult are unclear.  The docs for GetOverlappedResult state
explicitly that the "lpNumberOfBytesTransferred" return value is undefined,
so the call appears to be useless in this context.  [Indeed I've never seen
it used following WaitCommEvent()/WaitForXXX()].
  
    GetOverlappedResult reports the success or failure of the operation, and
    the variable pointed to by the lpEvtMask parameter is set to indicate
    the event that occurred.

lpEvtMask was the pointer passed to WaitCommEvent().  This last sentance
says to me that the variable to which lpEvtMask points isn't written to
until _after_ WaitForXXX() has returned.

> Looking further, I can't find *anything* that implies this is true.

I don't see how the above section can be interpreted any differently, and I
don't see how WaitCommEvent() can return a mask value describing an event
that hasn't happened yet.

> KB Q175551 has sample code for Windows CE that uses overlapped IO, and it
> examines the mask immediately after the call.

FWIW, The WinCE serial driver doesn't work the same way as serial devices on
other Win32 platforms.  Notice that this example doesn't pass an overlapped
structure pointer to WaitCommEvent().  One of my companies projects had to
use Embedded NT rather than WinCE due to the differences in I/O on WinCE. At
least that's what the Win32 guys claimed.

> Further, if what you say is correct, I would expect Python COM port 
> based programs to regularly crash!  In your scenario, by the time the 
> overlapped function ended up writing to our mask variable, that address 
> would be invalid.

I don't see any other interpretation.  If WaitCommEvent() can return
immediately, the mask variable obviously isn't valid at that point. Perhaps
in the overlapped case, the output mask value information is discarded, but
that's not what the above text says.

> However, it would be pretty close to the top of the stack, so I would expect
> it to corrupt whatever was running when the mask was actually written.  I
> know a number of people have used these APIs to do serious serial port
> control - either directly, or via another wrapper.  I would be very
> surprised we survived this long.
>
> Can you find an explicit reference to this?

I think the text quoted above is pretty explicit. It's from
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/devio/base/waitcommevent.asp

Overlapped read/write work in similar ways, you pass a pointer to the
source/destination when you initiate the operation, then after doing
WaitForXXX(), you check the contents of the destination or the byte count.

Here's the example program from the Win32All Demo directory:

    88	
    89	    def _ComPortThread(self):
    90	        overlapped = OVERLAPPED()
    91	        overlapped.hEvent = CreateEvent(None, 1, 0, None)
    92	        while 1:
    95	            rc, mask = WaitCommEvent(self.handle, overlapped)
    
If WaitCommEvent() always waits for the event to happen so that mask is
valid, then we know that receive data is ready.  Why not just call
ReadFile() at this point?  Because the WaitCommEvent is overlapped, the
WaitCommEvent returns immediately whether or not data is ready, and we have
to do the WaitForXXX() below:
    
    96	            if rc == 0: # Character already ready!
    97	                SetEvent(overlapped.hEvent)
    98	            rc = WaitForMultipleObjects([overlapped.hEvent, self.eventStop], 0, INFINITE)
    99	            if rc == WAIT_OBJECT_0:
   100	                # Some input - read and print it
   101	                flags, comstat = ClearCommError( self.handle )
   102	                rc, data = ReadFile(self.handle, comstat.cbInQue, overlapped)
   103	                WaitForSingleObject(overlapped.hEvent, INFINITE)
   104	                sys.stdout.write(data)

-- 
Grant Edwards                   grante             Yow!  I represent a
                                  at               sardine!!
                               visi.com            



More information about the Python-list mailing list