views:

687

answers:

3

I created a class that handles serial port asynchronously. I use it to communicate with a modem. I have no idea why, but sometimes, when I close my application, I get the Blue Screen and my computer restarts. I logged my code step by step, but when the BSOD appeared, and my computer restarted, the file into which I was logging data contained only white spaces. Therefore I have no idea, what the reason of the BSOD could be.

I looked through my code carefully and I found several possible reasons of the problem (I was looking for all that could lead to accessing unallocated memory and causing AV exceptions).

When I rethought the idea of asynchronous operations, a few things came to my mind. Please verify whether these are right:

1) WaitCommEvent() takes a pointer to the overlapped structure. Therefore, if I call WaitCommEvent() inside a function and then leave the function, the overlapped structure cannot be a local variable, right? The event mask variable and event handle too, right?

2) ReadFile() and WriteFile() also take references or pointers to variables. Therefore all these variables have to be accessible until the overlapped read or write operations finish, right?

3) I call WaitCommEvent() only once and check for its result in a loop, in the mean time doing other things. Because I have no idea how to terminate asynchronous operations (is it possible?), when I destroy my class that keeps a handle to a serial port, I first close the handle, and then wait for the event in the overlapped structure that was used when calling the WaitCommEvent() function. I do this to be sure that the thread that waits asynchronously for a comm event does not access any fields of my class which is destroyed. Is it a good idea or is it stupid?

try
  CloseHandle(FSerialPortHandle);
  if Assigned(FWaitCommEvent) then
    FWaitCommEvent.WaitFor(INFINITE);      
finally
  FSerialPortHandle := INVALID_HANDLE_VALUE;
  FreeAndNil(FWaitCommEvent);
end;

Before I noticed all these, most of the variables mentioned in point one and two were local variables of the functions that called the three methods above. Could it be the reason of the BSOD or should I look for some other mistakes in my code?

When I corrected the code, the BSOD stopped occuring, but It might be a coincidence. How do you think?

Any ideas will be appreciated. Thanks in advance.

+4  A: 
  1. Yes, you do need to keep the TOverlapped structure available for the duration of the overlapped operation. You're going to call GetOverlappedResult at some point, and GetOverlappedResult says it should receive a pointer to a structure that was used when starting the overlapped operation. The event mask and handle can be stored in local variables if you want; you're going to have a copy of them in the TOverlapped structure anyway.

  2. Yes, the buffers that ReadFile and WriteFile use must remain valid. They do not make their own local copies to use internally. The documentation for ReadFile even says so:

    This buffer must remain valid for the duration of the read operation. The caller must not use this buffer until the read operation is completed.

    If you weren't obeying that rule, then you were likely reading into unreserved stack space, which could easily cause all sorts of unexpected behavior.

  3. To cancel an overlapped I/O operation, use CancelIo. It's essential that you not free the memory of your TOverlapped record until you're sure the associated operation has terminated. Likewise for the buffer you're reading or writing. CancelIo does not cancel the operation immediately, so your buffers might still be in use even after you call it.

Rob Kennedy
Thank you for the information. Now I call CancelIo(FSerialPortHandle) and then wait for the FWaitCommEvent to be set, informing me about the I/O operation termination.This question might seem stupid, but is it necessary to call GetOverlappedResult() after then, to make the thread exit or is it enough to call CancelIo() and forget about it? The GetOverlappedResult() function might get thread exit code or something, I don't know. Thanks.
Mariusz
`CancelIo` says "all completion notifications for the I/O operations occur normally," so no, you don't need to call `GetOverlappedResult`; you can just wait for the completion event.
Rob Kennedy
+4  A: 

An application running as a standard user should never be able to cause a bug check (a.k.a. BSOD). (And an application running as an Administrator should have to go well out of its way to do so.) Either you ran into a driver bug or you have bad hardware.

By default, Windows is configured to save a minidump in %SystemRoot%\minidump whenever a bug check occurs. You may be able to determine more information about the crash by loading the minidump file in WinDbg, configuring WinDbg to use the Microsoft public symbol store, and running the !analyze -v command in WinDbg. At the very least, this should identify what driver is probably at fault (though I would guess it's your modem driver).

bk1e
A: 

I read the CancelIo() function documentation and it states that this method cancells all I/O operations issued by the calling thread. Is it OK to wait for the FWaitCommEvent after calling CancelIo() if I know that WaitCommEvent() was issued by a different thread than the one that calls CancelIo()?

  if Assigned(FWaitCommEvent) and CancelIo(FSerialPortHandle) then
  begin
    FWaitCommEvent.WaitFor(INFINITE);
    FreeAndNil(FWaitCommEvent);
  end;

I checked what happens in such case and the thread calling this piece of code didn't get deadlocked even though it did not issue WaitCommEvent(). I tested in on Windows 7 (if it matters). May I leave the code as is or is it dangerous? Maybe I misunderstood the documentation and this is the reason of my question. I apologize for asking so many questions, but I really need to be sure about that.

Thanks.

Mariusz
Since the I/O was initiated as overlapped I would call GetOverlappedResult even after calling CancelIo. But that's just something I would do as a Windows user mode programmer. A bigger question is, if you try your old bug ridden code under Windows 7, does Windows 7 give you the same BSOD that older Windows did? If so, Windows 7 has as big a security hole as older Windows. As bk1e said, even the world's buggiest user mode program shouldn't be able to make Windows BSOD.
Windows programmer