Skip Menu |

This queue is for tickets about the Win32-API CPAN distribution.

Report information
The Basics
Id: 92971
Status: resolved
Priority: 0/
Queue: Win32-API

People
Owner: Nobody in particular
Requestors: markus.ortner [...] utanet.at
Cc:
AdminCc:

Bug Information
Severity: (no value)
Broken in: (no value)
Fixed in: 0.83_01



Subject: Win32::API::Struct (v 0.65) and fixed length WCHAR array crashes Perl
Date: Wed, 12 Feb 2014 19:57:10 +0100
To: bug-Win32-API [...] rt.cpan.org
From: Markus Ortner <markus.ortner [...] utanet.at>
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: 258: if ($type =~ /\w\*(\d+)/) { 259: $repeat = $1**Win32::API::Type::sizeof($orig)*; 260: $type = "a$repeat"; 261: } ... 350: if ($type =~ /\w\*(\d+)/) { 351: $repeat = $1**Win32::API::Type::sizeof($orig)*; 352: $type = "*a*$repeat"; 353: } 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'. 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? With kind regards, Markus
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.ortner@utanet.at wrote: Show quoted text
> 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. Show quoted text
> > 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. Show quoted text
> > 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.
Subject: tz.pl
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);
On Wed Feb 12 13:57:45 2014, markus.ortner@utanet.at wrote: Show quoted text
> Is it possible to make a correction in a future update of this module to > handle non-CHAR fixed length arrays correctly? > > With kind regards, > Markus
Fixed in 0.83_01.