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);