Dr. Mu has asked for the wisdom of the Perl Monks concerning the following question:

I have a legacy Perl/Tk app that I wrote a few years ago that works fine under Win98. It uses Win32::SerialPort to communicate with an external device. The first time it runs on an NT or XP machine, everything works great. But if I exit the program and then restart it, it won't open the serial port, and $^E contains the message, "Overlapped I/O operation is in progress." Oddly enough, if I then run a non-Perl app that uses the same serial port, it works fine, and I can go back to the Perl app, and it'll run fine again, too -- until I exit and restart.

I use the following code to open the port:

$PortName = 'COM1'; $Baudrate = 9600; while ($cmd = shift @ARGV) { $PortName = shift @ARGV if $cmd eq '-p'; $Baudrate = shift @ARGV if $cmd eq '-b' } unless ($Port = new Win32::SerialPort($PortName) and $Port->are_baudrate(115200) and $Port->baudrate($Baudrate) and $Port->parity('none') and $Port->databits(8) and $Port->stopbits(2) and $Port->handshake('rts') and $Port->buffers(4096,4096) and $Port->binary(1) and $Port->xon_limit(3000) and $Port->xoff_limit(300) and $Port->rts_active(1) and $Port->write_settings) { $mw->messageBox( -message => "Can't open $PortName at $Baudrate baud.", -type => 'OK' ); print $^E; exit }
Before the program exits after a normal, working run, the following subroutine is executed:
sub PowerDown { $Port->rts_active(0); $Port->write_settings; Win32::Sleep(1000); $Port->read; $Port->close || warn "Close failed.\n"; undef $Port; exit }
This subroutine was added in a vain attempt to make sure the port is cleared and closed properly before exiting. It wasn't needed under Win98. I never get the "Close failed" warning, BTW. If anyone can see what I've apparently failed so far to see, I (and my customer) would be very grateful. Thanks.

Update: I suspected that the error I was getting had something to do with handshaking, i.e. that the port was getting closed with characters remaining in the transmit buffer, awaiting a CTS from the equipment it's connected to. To test this theory, I rewrote the PowerDown subroutine as follows:

sub PowerDown { $Port->handshake('none'); $Port->rts_active(0); $Port->write_settings; Win32::Sleep(1000); $Port->read; $Port->close || warn "Close failed.\n"; undef $Port; exit }
This solved the problem -- sort of. I don't get an error anymore when I open the port on the second go'round. But I do get a "Buffer Overrun" error after it's open and a bunch of data left over from the last time the port was opened. Also, on shutdown, I occasionally get a window manager protocol error from Tk. (I'm using WM_DELETE_WINDOW to intercept the intent to exit.) That's probably harmless, but there's got to be a cleaner, more reliable way to do this...

Update 2: I think I may have gotten to the root of the problem. Buried in the "Bugs" section of the Win32::SerialPort docs is the statement, "On NT, a read_done or write_done returns False if a background operation is aborted by a purge. Win95 returns True." Without the above modified PowerDown routine, a purge is likely what Perl performs on the port when it exits, leaving it in a non-write_done state. Assuming that Perl uses these flags to determine whether the port can reopen, the error message I was getting makes perfect sense. On the other hand, other non-Perl apps are not so constrained and are perfectly happy to open the port anyway. So my question is, rather than requiring programmers to go through the gyrations outlined above to close an open port so it's not left in a closed-but-not-really limbo, why not simply make the module more forgiving on opening the port, as other programming environments evidently do?

Replies are listed 'Best First'.
Re: Win32::SerialPort Error: Overlapped-I/O operation is in progress ($^E too late)
by tye (Sage) on Mar 27, 2005 at 21:39 UTC

    $mw->messageBox( ... ) surely overwrites $^E. The "overlapped" 'error' is often found in $^E, probably because some common, internal task that perl.exe performs generates that result.

    Once you grab $^E's contents sooner, your next problem will likely be figuring out which method call is the one that is failing. But you should be able to get the real failure reason, which may be enough to point a finger somewhere.

    - tye        

      Good point. So I put the test right after the new Win32::SerialPort($PortName), and that's where it failed, with the same error message.
        Did new Win32::SerialPort($PortName) return undef? $^E is only meaningful if it did.
Re: Win32::SerialPort Error: Overlapped-I/O operation is in progress
by dcd (Scribe) on Mar 29, 2005 at 02:29 UTC
    I'm confused - you said your later changes (to allow the write to complete) solved the problem, but that doesn't explain at all why Win32::SerialPort acted differently on a restart than a non-Perl application. Where is the state informations saved, or what is Win32::SerialPort looking at that other apps don't?
      The Update 2 is closer to the actual issue. There is a purge_all when the port is closed (close is also called from DESTROY). When I look at the TK example I put into the CPAN distribution (eg/demo7.plx), I see a blocking write near the beginning with the comment:
      # "write" first to init "write_done"
      and explicit checks of write_done(0) around the Tk callbacks to prevent beginning new writes while an old one is in progress. That demo also ran with no handshaking.

      In the code given, it is not clear which internal operations are blocking/non-blocking. But he does comment on handshaking and buffer management (and uses the is_xx_limit parameters).

      I have to speculate a bit about the state information. It is not stored explicitly, although when he closes the app with characters remaining in the write buffer - but blocked by the handshaking - they are actually in the serial driver's buffer (during an "overlapped", i.e. non-blocking TO PERL background write). It is not clear if both Perl and Tk are closed before restarting. He may be holding an OS handle open. But I don't know for sure.

      I do think flushing the buffer on close (e.g. by setting handshake to 'none' or doing a blocking write) will fix the problem.

      -bill

      Edit by tye: Preserve formatting

        Hi Bill,

        Thanks for taking the time to comment. Sorry about the confusion surrounding my post. Both Perl and Tk were closed before restarting.

        There are actually two issues here. One regards closing the port with characters remaining. The sensible approach, I think, would be for the system just to empty the buffer on a close, rather to wait for any handshaking to unblock (which may never happen) or to leave the port in a closed-but-nonempty state. If the programmer is concerned about those last characters getting to the connected device (I'm not), he can check the transmit buffer and wait for it to empty before issuing the close command. But when a close is issued, it should mean just that -- graceless though it might be -- regardless of state. And the system should be relied upon to mop up any messes left behind.

        The second issue regards opening a port that's in this weird state of limbo. Non-Perl apps don't seem to have a problem recognizing that the port can be forced back open and proceed accordingly without complaint. It would be nice if Perl apps could do the same.