http://qs321.pair.com?node_id=1168655

apeks_okad has asked for the wisdom of the Perl Monks concerning the following question:

Dear Perl Monks, I need a help in Win32::API

I am working on a hardware test automation, The interface to hardware is Automotive Interface 4 (AI4 : USB to SENT) and the H/W manufacturer has provided a C DLL which exports set of functions to communicate with the hardware.

I am trying to import the C DLL via Win32::API module so i can automate the communication via perl.

I have a problem with one API ,KSENT_TX. This function requires a pointer to an array of unsigned char as one of its arguments.

The header file extern declarations(provided by the manufacturer) looks as follows:

MOD_EXTERN unsigned char _stdcall KSENT_TX(unsigned char channel, unsigned char *data, unsigned long cyclic);

I have problems passing the right values to this function. Perl.exe crashes every time I try executing my code.

my $Tx = Win32::API::More->new( 'AID.dll', 'KSENT_TX', 'CPN', 'C'); #### $^E is non-Cygwin only die "Error: $^E" if ! $Tx; #### or on Cygwin and non-Cygwin die "Error: ".(Win32::FormatMessage(Win32::GetLastError())) if ! $Tx; my $rettx = $Tx->Call(0, $data_ref, 1);

I tried various methods to pass the array reference

my $data_ref = [0x81 , 0x55, 0x00, 0xE0];

or

my @data = (129,85,0,224); my $data_ref = \@data;

also

my $data_ref = pack ('C*', (0x81 , 0x55, 0x00, 0xE0));

All of it leads to the same PERL crash. I am a novice to PERL development so maybe I am missing something really basic here. Can you please help me with this problem

Replies are listed 'Best First'.
Re: How to pass a pointer to an array of 'unsigned char' C data type with Win32::API ("U0")
by tye (Sage) on Jul 27, 2016 at 16:39 UTC
    my $data_ref = pack ('C*', (0x81 , 0x55, 0x00, 0xE0));

    That is close to what you want. It would have worked on a slightly older version of Perl. However, p5p have decided that packing binary structures in Perl should honor Unicode (don't get me started). So you get problematic results:

    $ say "0x81, 0x55, 0x00, 0xE0" 129 85 0 224 $ say "unpack 'U0C*', pack 'C*', 0x81, 0x55, 0x00, 0xE0" 194 129 85 0 195 160

    You're first and last values, having their 8th bit set, got turned into UTF-8 byte sequences (2 bytes each). You can prepend "U0" to the front of the pack template to avoid such.

    - tye        

      Oh... now that makes sense.

      So I changed the code accordingly as follows

      my $data_ref = pack ('U0C*', (0x81 , 0x55, 0x00, 0xE0));

      But still end up with the same problem

      Maybe its worth the mention that I use Strawberry Perl 5.24.0 (32Bit) on a Win7 64 Bit Machine (since the DLL provided by the manufacturer is a 32 Bit DLL). Could this lead to problems with the array reference?

        Could this lead to problems with the array reference?

        I don't see any reason that should lead to any problems.

        There are other approaches (apart from Win32::API) that you can take - eg XS or Inline::C.
        I personally prefer either of those (especially the latter) over Win32::API as I've always found Win32::API difficult to get right - though it's possible that the deficiency is in me, as opposed to Win32::API.

        For XS/Inline::C you'll need the header file (which it seems you already have), the dll (which you definitely already have) and a suitable import library (which you might not have).
        To create the import library ( let's call it libAID.a), place a copy of AID.dll in the cwd and run:
        gendef AID.dll dlltool --kill-at --input-def AID.def --output-lib libAID.a
        I would then install Inline::C and access the dll functionality using it.
        (You'll want to first run perldoc Inline::C-Cookbook for some basic instructions on how to successfully use the module.)

        Cheere,
        Rob

        I don't see any reason for Perl to crash. But you don't even provide the error message associated with the Perl crash so we don't have much to go on to diagnose the problem.

        - tye