Fri Oct 16 19:41:28 2015: Request 107663 was acted upon.
Transaction: Correspondence added by BULKDD
       Queue: Win32-API
     Subject: Unpacking of byte array still buggy in 0.83_01
   Broken in: 0.82, 0.83_01
    Severity: Normal
       Owner: Nobody
  Requestors: am...@marvell.com
      Status: new
 Ticket <URL: https://rt.cpan.org/Ticket/Display.html?id=107663 >


On Fri Oct 09 10:29:18 2015, am...@marvell.com wrote:
> Hi,
> 
> a few ago I stumbled over the error in Win32API::Struct 0.82 when
> unpacking a BYTE / UNSIGNED CHAR buffer.
> After some debugging I found that the buffers are being packed using
> 'a'.$repeat but got unpacked using 'Z'.$repeat.
> 
> I'm using plain Strawberry Perl 32 and/or 64 bit on Windows 7 64 bit.
> "This is perl 5, version 22, subversion 0 (v5.22.0) built for MSWin32-
> x86-multi-thread-64int"
> 
> I used Win32::API to access FTDI's FTCJTAG.DLL and imported the DLL
> functions using the corresponding .h file and Win32::API::more.
> Great so far! Really easy to import a DLL using the .h file contents.
> 
> The buffers were declared :
> Win32::API::Struct->typedef ('WriteDataByteBuffer', qw (
>         BYTE data[64]));
> Win32::API::Struct->typedef ('ReadDataByteBuffer', qw (
>         BYTE data[64]));
> Win32::API::Struct->typedef ('ReadCmdSequenceDataByteBuffer', qw (
>         BYTE data[128]));
> 
> Win32::API::Type->typedef('PWriteDataByteBuffer',
> 'WriteDataByteBuffer*');
> Win32::API::Type->typedef('PReadDataByteBuffer',
> 'ReadDataByteBuffer*');
> Win32::API::Type->typedef('PReadCmdSequenceDataByteBuffer',
> 'ReadCmdSequenceDataByteBuffer*');

Do not use Win32::API::Struct like that. First Win32::API::Struct is very 
inefficient. Second you null term problem is because it was sent through 
Win32::API and you didn't pass a plain string buffer to be written to. Just pad 
out a regular scalar with as described in 
http://search.cpan.org/~bulkdd/Win32-API/API.pm#CALLING_AN_IMPORTED_FUNCTION

$s = "\x00" x 64;

Then declare the func to Win32::API as a char * or P (I prefer P personally), 
then pass $s to the $api->Call call.
 
Also 64 or 128 bytes is not enough per documents of the C API so I dont think 
you read them correctly.
----------------------------------------------------------------------------------
FTC_STATUS JTAG_Read (FTC_HANDLE ftHandle, BOOL bInstructionTestData, DWORD
dwNumBitsToRead, PReadDataByteBuffer pReadDataBuffer, LPDWORD 
lpdwNumBytesReturned,
DWORD dwTapControllerState)
This function reads data from an external device ie a device attached to a 
FT2232D dual device or
FT2232H dual hi-speed device or FT4232H quad hi-speed device. A FT2232D dual 
device or FT2232H
dual hi-speed device or FT4232H quad hi-speed device communicates with an 
external device by
simulating the JTAG synchronous protocol.
Parameters

ftHandle
Handle of a FT2232D dual device or FT2232H dual hi-
speed device or FT4232H quad hi-speed device.

bInstructionTestData
Specifies the type of register, that data is to be read from on an
external device. Instruction(TRUE) or test data(FALSE).

dwNumBitsToRead
Specifies the number of bits to be read from an external device.
Valid range 2 to 524280. 524280 bits is equivalent to 64K bytes.

pReadDataBuffer
Pointer to buffer that returns the data read from an external
device. Size of buffer should be set to 65535.
----------------------------------------------------------------------------------

> The JTAG_Read function in FTCJTAG.DLL in my case/for my device returns
> 5 bytes. (4 bytes data DWORD and 1 byte JTAG status bits)
> 
> unpack ('Z64', $buffer->data) stops unpacking at the first 0 found ->
> WRONG
> 
> unpack ('a64', $buffer->data) unpacks all bytes even zeros and pads
> with 0.
> 
> I fixed it just by changing Z to a.
> 
> Then while finishing my tasks and documenting, I found the new version
> 0.83_01 that tries to fix something unpacking arrays.
> But unpack ('Z'... for any byte sized array is still wrong, and when
> fixed by a 'a' then the
> Win32::API::_TruncateToWideNull($$itemvalueref) later on kills the
> buffer's values.
> 
> IMHO the Z is only correct if the buffer contains a Zero terminted
> string, if the buffer contains any binary data, 'a' is the only valid
> format specifier for the unpack, because 0s (zeros) are preserved.
> Padding a zero-terminated string with 0s does not harm, whereas
> cutting binary data at the first zero is harmful.

How would you extract whether a char array is null termed or not from this 
struct definition 
https://msdn.microsoft.com/en-us/library/windows/desktop/aa365740%28v=vs.85%29.aspx
 for example?

C lang does not specify whether a char * is random binary or null terminated. 
There is no way to know this programmatically from any C prototype (COM does 
know the different between an array of chars and a string 
https://msdn.microsoft.com/en-us/library/cc237562.aspx but this module isn't 
called Win32::COM). Most char *s are printable bytes, not binary data (like a C 
struct) casted to a char * that is effectively a void *. There are back compat 
problems that an inline char array in a Win32::API::Struct was already null 
termed for years.

> I would suggest
> $type = $type_size == 1 ?
> 'Z'.$repeat #have pack truncate to NULL char
>        :'a'.($repeat*$type_size); #manually truncate to wide NULL char
> later
> 
> to be changed to
> $type = 'a'.($repeat*$type_size);

This causes the treatment of char * (null term) and wchar_t *s (not null term) 
to be different which was one of the issues in 
https://rt.cpan.org/Ticket/Display.html?id=92971 which I fixed.

> Uuuh I can't find Win32::API::_TruncateToWideNull,

It is undocumented, that is why it has _, it is written in XS.

Reply via email to