in reply to Re^9: WIN32::API and void*
in thread WIN32::API and void*

Do not pack $LeftTable and $RightTable. They should be 12345, not "\x12\x34....". myTable is a void * and is a HANDLE/pointer sized integer, myTable * is a void **. I saw you create $packedOutputTable from ,$outputTable with CreateTable but never checked $outputTable for null/0/false. $pointerToCount looks fine. I don't think the 2 doubles cause problems at this point.

Good, so I only have to work out wich letter to use in pack to pass $LeftTable and $RightTable. I'll have a go tomorrow and let you know (btw thanks again, I'm learning a lot). In reference with $packedOutputTable:

my $outputTable = $CreateTable->Call(); my $packedOutputTable = pack('J',$outputTable);
I understand that's the right way to pass a void**.

Replies are listed 'Best First'.
Re^11: WIN32::API and void*
by bulk88 (Priest) on Jul 26, 2012 at 19:06 UTC
    You should be able to figure it out yourself, on a x86-32 Perl, pack letter for pointer is V or L. Since a pointer is a 32 bit little endian number. J is the way I write "portable" code, since its 32 on 32 and 64 on 64, except when you have a non standard 32 bit perl with "quads"/"native 64 bit" support, which is what I think you have. Some people think that a 32 bit perl with "quads"/"native 64 bit" is a long overdue feature of perl so its not a bad thing. I would replace all the pack 'J's with pack 'L's or pack 'V's. You might want to put this in, its how Win32::API figures out what the pack letter for a pointer is
    #const optimize BEGIN { use Config; eval ' sub pointer_pack_type () { "' .($Config{ptrsize} == 8 ? 'Q' : 'L'). '" }'; }
    then do something like
    $packednum = pack( pointer_pack_type() , 123456);
    I still would like to see a Dumper() of the parameters you passed to myTableCompare. About your DLL, do you have the source code to it or not? If you have the source code for that DLL you can use a C debugger with symbols, you could figure out in about a minute why it crashed. Even without the source code to that DLL, you can figure out if it crashed inside the DLL, or Win32::API/Perl crashed from C stack corruption (the latter only happens with stdcall functions, cdecls very very rarely corrupt the C stack, but they may see garbage parameters if they are prototyped incorrectly by the caller, a wrong prototype with a stdcall is 100% fatal). Most MS DLLs, the C functions always "validate" their parameters, with SEH or IsBadReadPtr and friends, so when you pass a NULL ptr, or a "(char *)1234", they fail with GLR=ERROR_INVALID_PARAMETER, they DONT crash. Non-MS DLLs, its upto the programmer of that DLL on what to do, usually they don't do what MS does and simply crash.

    If it works in C, its guaranteed that, somehow, someway, Win32::API my non-CPAN 0.69-0.71 will work. I say my non-CPAN versions because 0.68 for example, if the return type is a float, the C function is never called (bug in 0.68). If you want to see pushing Win32::API 0.68 to its limits (calling MS COM object "C++" methods), look at this thing I wrote, use C++ COM objects without any compiler, I had to runtime replace the insides of Win32::API 0.68 to pull it off.

      All tuned in!

      I've made my tests before reading your last post, and I came to the conclusion that I had to pack myTable as 'L'. Then I've plugged your code in and confirmed it, which is reassuring.

      This is the final code for anybody that may find it helpful:

      #! /usr/bin/perl use Win32::API; # Will found out the appropiate length to pack integers in this machin +e #const optimize BEGIN { use Config; eval ' sub pointer_pack_type () { "' .($Config{ptrsize} == 8 ? 'Q' : 'L'). '" }'; } # Control vars $pass = 1; $printOutValues = 1; if($printOutValues) { use Data::Dumper; $Data::Dumper::Useqq = 1; } # Create a table using the dll. # Some info about the API function: # __declspec(dllimport) myTable myTableCreate(); # Some info about the type myTable: # typedef # struct myTable{ # void* opaque; ///< Opaque pointer to storage # } myTable; $CreateTable = new Win32::API("myDll.dll","myTableCreate", 'V','N'); if(not defined $CreateTable) { die "Can't import API myTableCreate(): $!\n"; } #Create an empty tables. if($pass) { $LeftTable = $CreateTable->Call(''); $RightTable = $CreateTable->Call(''); $outputTable = $CreateTable->Call(''); # Make sure that the function does return a memory position, and n +ot a 0. $pass = $pass && ($LeftTable>0 && $RightTable>0 && $outputTable>0) +; if($pass){print("Empty Tables Succesfully Created. Pass code: ".$p +ass.".\n");}; if($pass && $printOutValues) { print "...\n"; print "These are the empty tables HANDLEs:\nLeft Table:\t".$Le +ftTable."\nRight Table:\t".$RightTable."\nOutput Table:\t".$outputTab +le."\n"; print "...\n"; } } # pack the table. if($pass) { $packedOption = pointer_pack_type(); $packedLeftTable = pack($packedOption,$LeftTable); $packedRightTable = pack($packedOption,$RightTable); $packedOutputTable = pack($packedOption,$outputTable); $pass = $pass && (unpack($packedOption,$packedLeftTable) == $LeftT +able && unpack($packedOption,$packedRightTable) == $RightTable && unp +ack($packedOption,$packedOutputTable) == $outputTable); if($pass){ print("Table communication between perl and dll is corr +ect. Pass code: ".$pass."\n");} if($pass && $printOutValues) { print "...\n"; print "These are the empty tables LPHANDLEs (Pack Option: ".$p +ackedOption."):\nLeft Table:\t"; print Dumper($packedLeftTable); print "Right Table:\t"; print Dumper($packedRightTable); print "Output Table:\t"; print Dumper($packedOutputTable); print "...\n"; } } # Path definition. $pathTableLeft = 'C:/Temp/Requests/TableTest1.TBL'; $pathTableRight = 'C:/Temp/Requests/TableTest2.TBL'; #$pathTableRight = 'C:/Temp/Requests/TableTest1.TBL'; # Load the function. # Load the table file loader from the dll. # Some info about the API function: # __declspec(dllimport) enum cStatus myTableLoadFromFile(char const* f +ilename, myTable* raw_result); # enum cStatus will treated as an int and interpreted using the array +@DllTableStatus. $LoadTableFromFile = new Win32::API("myDll.dll","myTableLoadFromFile", + 'PP', 'I','_cdecl'); if(not defined $LoadTableFromFile) { die "Can't import API myTableLoadFromFile(): $!\n"; } if($pass) { my @DllTblStatus = ("OK Status, no errors", "Failed to allocate me +mory", "Unable to open or save file", "Unable to convert value to req +uested type", "Unknown error", "Validation Failed", "Cell could not b +e found"); my $isLoadedLeft = 1; my $isLoadedRight = 1; $isLoadedLeft = $LoadTableFromFile->Call($pathTableLeft,$packedLef +tTable); $isLoadedRight = $LoadTableFromFile->Call($pathTableRight, $packed +RightTable); $pass = $pass && ($isLoadedLeft==0 && $isLoadedRight==0); if($pass) { $LeftTable = unpack($packedOption,$packedLeftTable); $RightTable = unpack($packedOption, $packedRightTable); print("Tables: ".$pathTableLeft." and ".$pathTableRight." +loaded correctly. Pass code: ".$pass."\n"); if($pass && $printOutValues) { print "...\n"; print "After loading the tables with the files, the ta +bles LPHANDLEs (Pack Option: ".$packedOption."):\nLeft Table:\t"; print Dumper($packedLeftTable); print "Right Table:\t"; print Dumper($packedRightTable); print "Output Table:\t"; print Dumper($packedOutputTable); print "...\n"; print "After laoding the tables with the files, the ta +bles HANDLEs (Unpack Option: ".$packedOption."):\nLeft Table:\t".$Lef +tTable."\nRight Table:\t".$RightTable."\nOutput Table:\t".$outputTabl +e."\n"; print "...\n"; } } else { if($isLoadedLeft>0){print("Loading table error on ".$pathTable +Left.": ".@DllTblStatus[$isLoadedLeft]."\n");} if($isLoadedRight>0){print("Loading table error on ".$pathTabl +eRight.": ".@DllTblStatus[$isLoadedRight]."\n");} } } # Load the table comparer from the dll. # Some info about the API function: # __declspec(dllimport) bool myTableCompare(myTable lhs, myTable rhs, +myTable* output, unsigned int *count, double relative_tolerance, doub +le absolute_tolerance); # bool is not an acceptable type. So the result of the function is sto +red in an int. $TableComparer = new Win32::API("myDll.dll","myTableCompare", 'NNPPDD' +,'N','_cdecl'); if(not defined $TableComparer) { die "Can't import API myTableCompare(): $!\n"; } if($pass) { my $count = 0; my $pointerToCount = pack('L',$count); my $RelativeTol=0.001; my $AbsTol=1.0; if($pass) { $areTheSame=$TableComparer->Call($LeftTable, $RightTable, $pac +kedOutputTable, $pointerToCount, $RelativeTol, $AbsTol); $outputTable = unpack($packedOption,$packedOutputTable); if($areTheSame==1) { print $pathTableLeft." and ".$pathTableRight." are equal.\ +nThe returned value of myTableCompare was: ".$areTheSame."\n"; } else { print $pathTableLeft." and ".$pathTableRight." are NOT equ +al.\nThe returned value of myTableCompare was: ".$areTheSame."\n"; } if($pass && $printOutValues) { print "...\n"; print "After comparing both tables, the tables LPHANDLEs ( +Pack Option: ".$packedOption."):\nLeft Table:\t"; print Dumper($packedLeftTable); print "Right Table:\t"; print Dumper($packedRightTable); print "Output Table:\t"; print Dumper($packedOutputTable); print "...\n"; print "After comparing both tables, the tables HANDLEs (Un +pack Option: ".$packedOption."):\nLeft Table:\t".$LeftTable."\nRight +Table:\t".$RightTable."\nOutput Table:\t".$outputTable."\n"; print "...\n"; } } }

      If the tables are the same, I get the following results:

      Empty Tables Succesfully Created. Pass code: 1. ... These are the empty tables HANDLEs: Left Table: 48120353 Right Table: 48120401 Output Table: 48120449 ... Table communication between perl and dll is correct. Pass code: 1 ... These are the empty tables LPHANDLEs (Pack Option: L): Left Table: $VAR1 = "!B\336\2"; Right Table: $VAR1 = "QB\336\2"; Output Table: $VAR1 = "\201B\336\2"; ... Tables: C:/Temp/Requests/TableTest1.TBL and C:/Temp/Requests/TableTest +1.TBL loaded correctly. Pass code: 1 ... After loading the tables with the files, the tables LPHANDLEs (Pack Op +tion: L): Left Table: $VAR1 = "\31\261\337\2"; Right Table: $VAR1 = "y\333\340\2"; Output Table: $VAR1 = "\201B\336\2"; ... After laoding the tables with the files, the tables HANDLEs (Unpack Op +tion: L): Left Table: 48214297 Right Table: 48290681 Output Table: 48120449 ... C:/Temp/Requests/TableTest1.TBL and C:/Temp/Requests/TableTest1.TBL ar +e equal. The returned value of myTableCompare was: 1 ... After comparing both tables, the tables LPHANDLEs (Pack Option: L): Left Table: $VAR1 = "\31\261\337\2"; Right Table: $VAR1 = "y\333\340\2"; Output Table: $VAR1 = "q\335\340\2"; ... After comparing both tables, the tables HANDLEs (Unpack Option: L): Left Table: 48214297 Right Table: 48290681 Output Table: 48291185 ...
      Note that The returned value of myTableCompare was: 1. Which is fair enough since we've specified that the return type of myTableCompare is numeric:
      $TableComparer = new Win32::API("myDll.dll","myTableCompare", 'NNPPDD' +,'N','_cdecl');

      However if the tables are not equal, the results I get are the following:

      Empty Tables Succesfully Created. Pass code: 1. ... These are the empty tables HANDLEs: Left Table: 48120353 Right Table: 48120401 Output Table: 48120449 ... Table communication between perl and dll is correct. Pass code: 1 ... These are the empty tables LPHANDLEs (Pack Option: L): Left Table: $VAR1 = "!B\336\2"; Right Table: $VAR1 = "QB\336\2"; Output Table: $VAR1 = "\201B\336\2"; ... Tables: C:/Temp/Requests/TableTest1.TBL and C:/Temp/Requests/TableTest +2.TBL loaded correctly. Pass code: 1 ... After loading the tables with the files, the tables LPHANDLEs (Pack Op +tion: L): Left Table: $VAR1 = "\31\261\337\2"; Right Table: $VAR1 = "a:\342\2"; Output Table: $VAR1 = "\201B\336\2"; ... After laoding the tables with the files, the tables HANDLEs (Unpack Op +tion: L): Left Table: 48214297 Right Table: 48380513 Output Table: 48120449 ... C:/Temp/Requests/TableTest1.TBL and C:/Temp/Requests/TableTest2.TBL ar +e NOT equal. The returned value of myTableCompare was: 48168704 ... After comparing both tables, the tables LPHANDLEs (Pack Option: L): Left Table: $VAR1 = "\31\261\337\2"; Right Table: $VAR1 = "a:\342\2"; Output Table: $VAR1 = "1%\342\2"; ... After comparing both tables, the tables HANDLEs (Unpack Option: L): Left Table: 48214297 Right Table: 48380513 Output Table: 48375089 ...
      In this case, The returned value of myTableCompare was: 48168704. Is this expected? Is the boolean defined as 1 if it's True and not 1 (in this case 48168704) if it is False. I would personally have expected a 0 instead.

        I am nitpicking your code.
        $CreateTable = new Win32::API("myDll.dll","myTableCreate", 'V','N');
        Put the cdecl thing on. Cdecl should be on for every C function, no exceptions. So far we learned that there are no stdcall functions in your DLL. I explained earlier why myTableCreate wont crash as stdcall even though stdcall is wrong. In your debug code's print messages you call the tables "handles". I dont know if that is correct. Win32 API calls it a HANDLE because "void *" doesnt work with Win32 API. Here is a tool to prove if they are pointers or not
        { my $IsBadReadPtr = Win32::API->new( 'kernel32.dll', 'BOOL IsBadReadPtr( UINT_PTR lp, UINT_PTR ucb) +', ); die "getting IsBadReadPtr failed" if !$IsBadReadPtr; sub IsBadReadPtr { return $IsBadReadPtr->Call($_[0], $_[1]); } }
        This is a wrapper around IsBadReadPtr. IsBadReadPtr will literally tell you if you will access violation/seg fault on any particular pointer from attempting to read that pointer block. All your 48,000,000s tables are probably pointers. What the length of the table memory block in the 48,000,000s? I dont know, maybe you know since you have the docs to your DLL. I can tell you that it is atleast 4 byte long since that is the minimum allocation size, cuz you have a 4 or 8 byte header behind the start of the malloc block that the malloc system puts there for book keeping, to make the pools tidy, the actual allocation is 8 (4 byte header, 4 bytes to you, you asked for 1-4 bytes) or 16 (8 byte header, 8 bytes to you, you asked for 1-8 bytes). Now if you use IsBadReadPtr, either all the table handles are readable, or none of them are readable. 48,000,000 things can really be a GUID/HANDLE/Encrypted pointer/record number/generation counter and not a memory block. Also the 48,000,000 may not 1 for 1 line up with malloc calls. For example, the 48,000,000 pointers might be pointers into 1 malloced block that is an array of all table headers that are live by the DLL, inside the DLL, so you couldn't call free() on then even if you wanted. Remember IsBadReadPtr can not tell the difference between allocated from the OS but free memory, and allocated from the OS and allocated from malloc memory. IsBadReadPtr is just a debugging tool, you dont have to use it.

        You do things like
        if($isLoadedRight>0){print("Loading table error on ".$pathTabl +eRight.": ".@DllTblStatus[$isLoadedRight]."\n");}
        and
        $pass = $pass && ($LeftTable>0 && $RightTable>0 && $outputTable>0) +;
        but here you did
        $pass = $pass && ($isLoadedLeft==0 && $isLoadedRight==0);
        Why not do != 0 instead of > 0, what if its a negative number? (in 0.68 all return/out values are signed, unsigned is ignored and you get signed, thats a bug with 0.68).
        These are the empty tables HANDLEs: Left Table: 48120353 Right Table: 48120401 Output Table: 48120449 ... Table communication between perl and dll is correct. Pass code: 1 ... These are the empty tables LPHANDLEs (Pack Option: L): Left Table: $VAR1 = "!B\336\2"; Right Table: $VAR1 = "QB\336\2"; Output Table: $VAR1 = "\201B\336\2"; ... Tables: C:/Temp/Requests/TableTest1.TBL and C:/Temp/Requests/TableTest +1.TBL loaded correctly. Pass code: 1 ... After loading the tables with the files, the tables LPHANDLEs (Pack Op +tion: L): Left Table: $VAR1 = "\31\261\337\2"; Right Table: $VAR1 = "y\333\340\2"; Output Table: $VAR1 = "\201B\336\2"; ... After laoding the tables with the files, the tables HANDLEs (Unpack Op +tion: L): Left Table: 48214297 Right Table: 48290681 Output Table: 48120449
        Yep, the reason for void ** is because the DLL will swap out the table pointer if it internally reallocates the table pointer because the old table pointer didn't meet the DLL's requirements (size, location, etc). It also means you need to be diligent, to not use the old void * (which will crash since its freed/reallocated to something else memory).
        In this case, The returned value of myTableCompare was: 48168704. Is this expected? Is the boolean defined as 1 if it's True and not 1 (in this case 48168704) if it is False. I would personally have expected a 0 instead.
        No. Error numbers in unix http://www.ibm.com/developerworks/aix/library/au-errnovariable/ and in Windows, 0 is success http://msdn.microsoft.com/en-us/library/windows/desktop/ms681382%28v=vs.85%29.aspx. BOOL in windows is 0 failed, true. MS headers say 1 is TRUE, and BOOL functions usually return 1, I've never seen a BOOL that returns something other than 1 on success, but everyone tests for true, not == 1, note you have a "bool" not a microsoft "BOOL", and your header file and the prototype is very messed up if it says bool/BOOL and delivers pointers. If the programmer was responsible, there should be symbolic constants somewhere in your header file that say what the non pointer return values/return flags are, and what is the cut off number between table pointers and status codes for myTableCompare. Who wrote this DLL?

        In terms of special magic return values and pointers, MS DLL that return "HANDLE", either return -1 or 0 on failure (read the docs, some do -1, some do 0, some do both), or a HANDLE. With an unknown function, everything under 2^16 (65536) or 2^12 (4096, 1 page) is not a pointer. In GDI handles, the upper 16 bits is a generation counter to prevent a new handle from being the same as a recently freeded handle, so things that use a freeded handle will fail rather than work erratically on the new handle. The lower 16 bits is the index into an array, see http://msdn.microsoft.com/en-us/library/ms810501.aspx. Kernel handles are different.

        It should be obvious that myTableCompare is giving you a table */void * to do something with. Since if its "the same" it gives you 1, I will guess 0 is error, and "different but success", you get a table with the delta table. So myTableCompare really returns a myTable, with opaque being 0, 1, or a table. Also I've never seen you free any of the table *s. You will have to do that eventually.

        Do you have docs or source code for this DLL or only the header file? Download StudPE and look at the export (and if you have time or curiosity, import table) table of that DLL. The PE Export table will show everything you can possibly call in that DLL from C, unless that is a C++ DLL. If its C++ then you have a big problem (virtual tables, etc). The PE export table does not show the prototypes of the functions though. There are ways to reverse engineer and learn the prototypes of the functions if you have the time. I've done it successfully before, since I have some assembly skills.