Wednesday, 8 December 2021

Serial Perl Revisited

I'd posted this item on creating a custom serial driver / interface in  PERL a few years ago, and needing to revisit it for another project, I found that there was now an issue with running the Win32::Serial module on 64-bit Windows, Windows 10 specifically.

The problem was, when I ran some Perl that previously worked for me, it now failed when querying a serial port with an error like this;

error: Second Read attempted before First is done at xantech.pl line 47.

Some Google-Fu later, I found a reference to the issue on PerlMonks, and a link by user cr8josh to the fix by Christopher Oicles which is published here.

It looks like there's a risk that the rt.cpan site will close, so I'm reproducing the fix here for my own future sanity;

The only problem with this module on 64-bit windows is the layout of the OVERLAPPED struct.  The 32-bit version of this struct has a size of 20 bytes, but the 64-bit version is 32 bytes, because the fields “Internal”, “InternalHigh” and “hEvent” are all quadwords instead of doublewords (“Pointer” also becomes a quadword, but it is part of a union which is quadword-sized in both 32- and 64-bit versions, and it is not used in Win32API::CommPort).

The fix is trivial, and allows both bitnesses to share the same Perl Module.  Here are the edits I made to CommPort.pm which got it working:

Line 467

Original:

    my $OVERLAPPEDformat="LLLLL";

Edited:

    my $OVERLAPPEDformat = (length pack 'P') * 8 == 32 ? "LLLLL" : "QQLLQ";

    # “length pack 'P'” gives you the size of a null pointer, then I multiply by 8 to get a bit count, just for clarity.

 

Lines 768 and 769

Original:

    $self->{"_R_OVERLAP"}            = " "x24;

    $self->{"_W_OVERLAP"}          = " "x24;

Edited:

    $self->{"_R_OVERLAP"}            = " "x32;

    $self->{"_W_OVERLAP"}          = " "x32;

 

# These lines populate the fields with some default (and invalid) bytes.  It really doesn’t look like over-allocating these by a few bytes can cause problems, so I just unconditionally set these to the 64-bit version of the stuct’s length (but I didn’t test under 32-bit after this change, so someone should verify the exposure).

 

(for me, the CommPort.pm is in C:\Strawberry\perl\vendor\lib\Win32API)





2 comments:

Anna said...

Interesting post, thanks for sharing.

Anonymous said...

I hoped that this was the solution and wondering why there was no final fix years later (in 2025). But for me the edit dit not change anything. I have a loopback connector and putty echo's the typed text. However, If I send something to the serial port in perl, it does not return anything. This is not to be nasty to you aff course, just in case somebody else has the same issue. However, I do not have a solution yet...