Beefy Boxes and Bandwidth Generously Provided by pair Networks
Problems? Is your data what you think it is?
 
PerlMonks  

Low-level version of Win32::API::Struct

by sgv_81 (Novice)
on Aug 01, 2012 at 10:39 UTC ( [id://984755]=perlquestion: print w/replies, xml ) Need Help??

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

I'm working with Win32::API against a Win32 dll. My machine is 64 b and I'm using Cygwin. I'm trying to create a struct defined in C as:

typedef struct cRefStr { size_t length; char const* data; } cStrRef;
I've tried to define the following structure with Win32::API::Struct:
Win32::API::Struct->typedef(CSTRREF =>qw{int n; char* buffer});
The complete perl code is:
# Sub all # cTblAtKey -> findInTableByKey # cTbl -> myTable # cCell -> myCell # ctbl_v2 -> myDll my $aKey = "UserName"; print $aKey."\n"; my $lengthaKey = length($aKey); print $lengthaKey."\n"; Win32::API::Struct->typedef(CSTRREF =>qw{int n; char* buffer}); my $structParameter = Win32::API::Struct->new('CSTRREF'); $structParameter->{n}=$lengthaKey; $structParameter->{buffer}=$aKey; # C function defined as: # myCell* findInTableByKey(myTable that, struct cRefStr key, unsigned +offset); # with: # struct myCell { # unsigned char opaque[10]; #}; my $aKeyFunc = Win32::API->new("myDll.dll",'findInTableByKey', 'NSN',' +C','_cdecl'); if(not defined $aKeyFunc) { die "Couldn't load the function findInTableByKey from the dll"; } $cellPtr = $aKeyFunc->Call($outputTable,$structParameter,0);
This breaks with a "Segmentation fault (core dumped)" error.

I've managed to use other functions of the same dll against $outputTable (this was the thread) and I know it is a valid parameter. So I've determined that the issue is with the Struct. I'm trying to substitute $structParameter by its low-level version as suggested in the Win32::API doc, this is using something like pack('NC',$size,$aKey).

I've tried to use a simplified example to understand how this would work. The code in the Win32::API doc, to test struct works as expected:

#### define the structure Win32::API::Struct->typedef( POINT => qw{ LONG x; LONG y; }); #### import an API that uses this structure my $getCursor = new Win32::API('user32', 'GetCursorPos','S','N'); #### create a 'POINT' object my $pt = Win32::API::Struct->new('POINT'); #### call the function passing our structure object $getCursor->Call($pt); #### and now, access its members my ($x, $y) =($pt->{x}, $pt->{y}); print "The cursor is at: $x, $y\n";

However the low-level version suggested in the same doc, throws an error: "Can't call method 'Pack' on an undefined value at ./TestStruct.pl line 27". The broken code:

#### import an API that uses this structure my $getCursor = new Win32::API('user32', 'GetCursorPos','S','N'); #### create a 'POINT' object $lpPoint=pack('LL',0,0); #### call the function passing our structure object $getCursor->Call($lpPoint); #### and now, access its members my ($x,$y) = unpack('LL',$pt); print "The cursor is at: ".$x.", ".$y."\n";
Any suggestion?

Replies are listed 'Best First'.
Re: Low-level version of Win32::API::Struct
by BrowserUk (Patriarch) on Aug 01, 2012 at 12:06 UTC
    My machine is 64 b ... pack('NC',$size,$aKey).
    1. Firstly, template char 'N' is a 32-bit unsigned integer in network (big-endian) order.

      If you are running Windows on Intel (90%+ are), then you would need 'V' which is 32-bit unsigned in VAX (little-endian) order.

    2. But ... as you are on 64-bit OS, if you are running a 64-bit perl: (you don't say?)

      Then size_t will equate to a 64-bit unsigned int which would be template 'Q'.

    The simplest template would be 'J' which should work for 32-bit or 64-bit. (Excepting 32-bit perl's compiled for 64-bit ints.)


    With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.

    The start of some sanity?

      Thank you, I understand that I'm in the wierd option of 32 bit perl for 64 int, Intel and Windows XP (but perl running through Cygwin). I think your post will be very helpful when I make the simplified example from my previous post work and I move into the original problem.

      As a quick refresh, the simplified example from my previous post:

      #### import an API that uses this structure my $getCursor = new Win32::API('user32', 'GetCursorPos','S','N'); #### create a 'POINT' object $lpPoint=pack('LL',0,0); #### call the function passing our structure object $getCursor->Call($lpPoint); #### and now, access its members my ($x,$y) = unpack('LL',$pt); print "The cursor is at: ".$x.", ".$y."\n";
      No matter which parameter I use within pack ('J','L','N','Q' or 'V') in $lpPoint=pack('LL',0,0); I still get the error "Can't call method Pack on a undefined value". The error pops up in $getCursor->Call($lpPoint);.

      From CPAN doc on Win32::API seems that I can substitute the Win32::API::Struct for the packed variable and then pass it to the Win32::API object. Any idea what I'm missing?

        That is not an example from the docs.

        my $getCursor = new Win32::API('user32', 'GetCursorPos','S','N');
        should be
        my $getCursor = new Win32::API('user32', 'GetCursorPos','P','N');
        since $lpPoint is a scalar string formally. 'S'/'T' are only for ::Struct objects. The result of pack() is a scalar string. Not a blessed object. If you had a newer version of ::API, you would get a proper croak message
        croak("Win32::API::Call: parameter %d must be +a%s", i+1, " Win32::API::Struct object!\n");
        much better than 0.68 blindly doing ->Pack() on whatever scalar happens to fall in as argument which is what is happening in your case.
Re: Low-level version of Win32::API::Struct
by Anonymous Monk on Aug 01, 2012 at 11:06 UTC
Re: Low-level version of Win32::API::Struct
by bulk88 (Priest) on Aug 01, 2012 at 17:04 UTC
    I'm trying to substitute $structParameter by its low-level version as suggested in the Win32::API doc, this is using something like pack('NC',$size,$aKey).

    Win32::API's letter codes and pack()'s letter codes are different, do not use letters from one on the other, always reread the docs for pack and Win32::API. You want 'LP' (L=native (LE) 32 bit, P=pointer to scalar string) for pack. That pack 'L' is a 'I' for Win32::API BTW.
    # myCell* findInTableByKey(myTable that, struct cRefStr key, unsigned +offset); # with: # struct myCell { # unsigned char opaque[10]; #}; my $aKeyFunc = Win32::API->new("myDll.dll",'findInTableByKey', 'NSN',' +C','_cdecl');
    Your prototype is wrong. 'C' as a return type either will be a undef, or a mangled something (number or single letter). You probably want 'N' instead of C to save that myCell * as a Perl scalar number.

    If you C header prototype is absolutly correct. 'S' and ::Struct won't work. "struct cRefStr key" is a pass by copy parameter, not a pointer or pass by reference. Thats going to require serious hacking on your part to get working since Win32::API can't do pass by copy structs/strings/anything except native machine numbers. I know its possible.

    Since that "struct cRefStr" has a size_t and a char *, and is pass by copy now, on 32 bit machine code ONLY (x64 machine code has a different calling convention and something else must be done), replace the 'S' letter with a 'N' and then a 'P', you will not have a struct anymore in Perl but the C function will think it is getting the pass by copy struct. So 'NSN' becomes 'NNPN'.

    The recipe for pass by copy struct with ::API on 32 bit machine code is, do a pack() to create the binary packed struct as a scalar string. Then add nulls to the end of the scalar string to align it to a multiple of 4 (I think)(this can also be done with the pack() letter codes during the previous pack). Now do "length($packed)/4", 4 being the byte size of an ::API N on 32 bits, this is the number of Ns you will turn the packed scalar string into an array of scalar numbers, "@structasnums = unpack('L6', $packedstruct);", then for the prototype to ::API, you put "length($packed)/4" 'N's in a row where that struct is in the C header prototype so it looks like "$obj = new Win32::API('somedll.dll', 'aFunction', 'PPNNNNNNN', 'N');" . Then you do "$obj->Call($something, $foo1, @structasnums, $lastparam);". To break down the ::API prototype, "'PPNNNNNNN'", P=$something, P=$foo1, NNNNNN=@structasnums, N=$lastparam.

    From the last thread you made, WIN32::API and void*, your running 32 bit cygwin perl with 64 bit ints on x64 Windows.

      I have made the following changes:

      aKeyFunc = Win32::API->new("myDll.dll",'findInTableByKey', 'NNPN',' +C','_cdecl');
      returns a pointer (as expected). When I unpack it using 'P10' I recover the content of the cell in my table (i.e. the function works!!!).THANKS!

        What are you unpacking with a 'P10'? That 'C' won't return a pointer, I think that 'C' as a return type is an undef in 0.68. You might also be leaking memory.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://984755]
Approved by Corion
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others pondering the Monastery: (3)
As of 2024-04-25 23:53 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found