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: [email protected]
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, [email protected] 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);