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

Mission accomplished. It was the "_cdecl". I've included it and tables are loaded perfectly!!!.

Good to know it wasnt a Win32::API bug or a bug in your DLL. I am very confused, since your pack 'J' (Perl Internal Scalar Unsigned Value)  letter is 64 bits long, yet you have a x86-32 DLL and a x86-32 Perl. Do you have one of the freak Perl builds with native 64 bit integers, but 32 bit pointers running 32 bit assembly code? I will assume that for the rest of this post.

I have some doubts about how HANDLE parameters will translate into the non-prototype calls. I've made a test with myTableCreate() and I think I should treat them as 'N' (I print the dumper for both the prototype and the non-prototype and I get $VAR1=48120369 and $VAR1=48120417, which doesn't seem bad). Furthermore, I'm not sure how they'd be packed (if they'd need to) before passing them to an API function as parameters.
Win32::API's letter codes are "inspired by" but ARE NOT the same letters as for pack. 'N' for Win32::API means pointer sized number. For pack, well I thought you should be using J, since that is normally 4 bytes on 32 bit and 8 bytes on 64 bits, but for your machine, J isn't the right letter. For myTable struct, it "worked" but J was sorta a bug/bad choice. The last 4 "\x00" are ignored by your DLL, and the myTable "struct" you gave is actually 2 pointers long (8 bytes), but the DLL doesn't know and doesn't care (a property of little endian numbers). You shouldn't use J with pack for pointers in multimember structs non-stock Perl build. You need to find the appropriate letter, see pack.

 For instance:

__declspec(dllimport) bool myTableCompare(myTable lhs, myTable rhs, my
+
Table* output, unsigned int *count, double relative_tolerance, double
+
absolute_tolerance)
Assuming BOOL=INT, which seems to work fine with the Kernel32 API BOOL Beep(long,long), the above C declaration translate into the following perl call:
my $TableComparer = new Win32::API("myDll.dll","myTableCompare", 'NNPP

+
DD','I','_cdecl');
I'm not completely sure, because I get a segmentation fault when I call $TableComparer with the following parameters:
$count = 1;
$pointerToCount=pack('I',$count);
$RelativeTol=0.001;
$AbsTol=1.0;
my $areTheSame=$TableComparer->Call($LeftTable,$RightTable,$packedOutp
+
utTable,$pointerToCount, $RelativeTol,$AbsTol);

Note that in this version of the code, I've passed $LeftTable and $RightTable without packing them before. I've tried to pass their J-packed versions but I still get a segmentation fault.

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.

Another doubt I have comes from the unsigned int pointer in the previous example, I have assumed that the signature is 'NNPPDD', and I've created the pointer using pack('I',$count), which I don't know whether it's right.

Correct. Any 'P' letters must be "ab\x01\x02cd" strings. Never ever 123.

PS: I'm using Windows and Cygwin, probably that's why sometimes things look a little bit MS-like and others a little bit Unix-like...

90% of my knowledge is Visual C or ActivePerl, 10% Mingw or Strawberry. 0% cygwin. I dont have a cygwin perl installed. Seg fault sounds correct for Cygwin Perl, since Cygwin Perl thinks and acts like Linux.

Can you do a print Dumper on all the parameters in "my $areTheSame=$TableComparer->Call($LeftTable,$RightTable,$packedOutputTable,$pointerToCount, $RelativeTol,$AbsTol);" before you do the Call() that crashes?

Replies are listed 'Best First'.
Re^10: WIN32::API and void*
by sgv_81 (Novice) on Jul 26, 2012 at 17:34 UTC

    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**.

      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.