Thu Feb 13 12:23:33 2014: Request 92971 was acted upon.
Transaction: Correspondence added by BULKDD
       Queue: Win32-API
     Subject: Win32::API::Struct (v 0.65) and fixed length WCHAR array crashes 
Perl
   Broken in: (no value)
    Severity: (no value)
       Owner: Nobody
  Requestors: markus.ort...@utanet.at
      Status: new
 Ticket <URL: https://rt.cpan.org/Ticket/Display.html?id=92971 >


First of all, I do not recommend using Win32::API::Struct. It works sometimes. 
But not always. And has lots of bugs and edge cases. I personally use pack() 
and unpack() myself, and convert the C struct to pack language myself using my 
head. There is also this 
http://search.cpan.org/~mhx/Convert-Binary-C-0.76/lib/Convert/Binary/C.pm but 
I've never personally used it. My opinion on Win32::API::Struct is I discourage 
its use, but it can get some maintenance and bug fixes over time if necessary.

On Wed Feb 12 13:57:45 2014, markus.ort...@utanet.at wrote:
> Dear Daniel and Cosimo,
> 
> I was using the impressive Win32::API module to make a call to the 
> kernel function GetTimeZoneInformation, which is defined as
> 
>      DWORD GetTimeZoneInformation(LPTIME_ZONE_INFORMATION 
> lpTimeZoneInformation)
> 
> and used Win32::API::Struct (v0.65) to define the TIME_ZONE_INFORMATION 
> structure:
> 
>      Win32::API::Struct->typedef( TIME_ZONE_INFORMATION => qw{
>          LONG       Bias;
>          WCHAR      StandardName[32];
>          SYSTEMTIME   StandardDate;
>          LONG       StandardBias;
>          WCHAR      DaylightName[32];
>          SYSTEMTIME   DaylightDate;
>          LONG       DaylightBias;
>      });
> 
> However, Perl crashes when I use following code:
> 
>      my $tz = Win32::API::Struct->new('TIME_ZONE_INFORMATION');
>      GetTimeZoneInformation($tz) or die "GetTimeZoneInformation failed: 
> $^E\n";
> 
> I figured out that the root cause of the crash was the packing / 
> unpacking of the item StandardName (and DaylightName), which is a fixed 
> length, wide character string of type WCHAR that is 'UTF-16LE' encoded.
> 
> This is because Win32::API::Struct::getPack and 
> Win32::API::Struct::getUnpack do not take the size of the type into 
> account, but always assume a 8-bit type size (CHAR):
> 
> 258:            if ($type =~ /\w\*(\d+)/) {
> 259:                $repeat = $1;
> 260:                $type = "a$repeat";
> 261:            }
> ...
> 350:            if ($type =~ /\w\*(\d+)/) {
> 351:                $repeat = $1;
> 352:                $type = "Z$repeat";
> 353:            }
> 
> However, for the "$packed_size" the correct value "$type_size * $repeat" 
> is calculated:
> 
> 358:            $packed_size += ( $type_size * $repeat ) + $type_align;
> 
> So, at a first glance I would suggest to use the corrected repeat count 
> also in line 259 and 351, respectively. Furthermore, getUnpack should 
> not assume a null-terminated (ASCIZ) string, but a string with arbitrary 
> binary data as getPack does:

I'm not sure that reading it as binary is practical or back compat safe. You 
expect a char [] to be a string, not filled with binary nulls.

> 
> In order to remain compatible, getUnpack may assume a null-terminated 
> string if and only if the original type is CHAR (or TCHAR). I have not 
> checked if it is also possible to implement a decoding of a WCHAR that 
> is encoded as 'UTF-16LE'.

No. There is no support or API for WCHAR in Win32::API. I never figured out how 
to write one and what features people want http://perlmonks.org/?node_id=970481 
. So I didn't design an API except for 
https://metacpan.org/pod/Win32::API#SafeReadWideCString . There wasnt any 
either prior to me taking over maintenance. This the what you have to do 
https://metacpan.org/source/BULKDD/Win32-API-0.77/t/00_API.t#L252 to process 
wide stuff with Win32::API. There is also an interal ::API design problem of 
what is an array of numbers, vs a string. char is special cased as a string in 
many places. I would have to do that to WCHAR too. Currently WCHAR is a short.

> 
> I tested the module with above modifications and it is working fine.
> 
> Is it possible to make a correction in a future update of this module to 
> handle non-CHAR fixed length arrays correctly?

I need to do more research and think of breakage modes.

You didn't post a sample script of what you are doing with Win32::API. Can you 
please post one? I attached what I tried. Yes the result is garbage because of 
the wrong size in bytes of the WCHAR array so you did show a real bug in this 
ticket that should get fixed.
use Win32::API;
use Data::Dumper;

Win32::API::Struct->typedef(SYSTEMTIME => qw(
  WORD wYear;
  WORD wMonth;
  WORD wDayOfWeek;
  WORD wDay;
  WORD wHour;
  WORD wMinute;
  WORD wSecond;
  WORD wMilliseconds;
));


Win32::API::Struct->typedef(TIME_ZONE_INFORMATION => qw(
  LONG       Bias;
  WCHAR      StandardName[32];
  SYSTEMTIME StandardDate;
  LONG       StandardBias;
  WCHAR      DaylightName[32];
  SYSTEMTIME DaylightDate;
  LONG       DaylightBias;
));


my $function = Win32::API::More->new(
      'kernel32.dll', 'DWORD WINAPI GetTimeZoneInformation( LPTIME_ZONE_INFORMATION lpTimeZoneInformation );'
);
die "Error: $^E" if ! $function;

my $tz = Win32::API::Struct->new('TIME_ZONE_INFORMATION');

die "Error: $^E" if ! $function->Call($tz);
print Dumper($tz);

Reply via email to