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

I've printed the Dumper, and I think it's encoraging to know that I get 48120353 after aTableCreate is called. The table J-packs to !B\336\2\0\0\0\0 and unpacks to the original table.

All together the create section of the code becomes:

my $CreateTable = new Win32::API("myDll.dll","HANDLE myTableCreate()") +; if(not defined $CreateTable) { die "Can't import API myTableCreate(): $!\n"; } #Create an empty tables. $aTable = $CreateTable->Call(); use Data::Dumper; $Data::Dumper::Useqq = 1; print Dumper($aTable); print("Empty Table Created.\n"); # pack the table. my $packedaTable = pack('J',$aTable); print Dumper($packedaTable); if(unpack('J',$packedaTable) == $aTable) {print "I know how to pack this table.\n";} else {die "I dont know how to pack this table";}
The results:
$VAR1 = 48120353; Empty Table Created. $VAR1 = "!B\336\2\0\0\0\0"; I know how to pack this table.

Well that sounds good to me (ignoring ! it's and array of blocks of chars or blocks of 1 or 3 integers). There is something created at least. Let's carry on with the loading. After some changes:

# aTable Path my $pathATable = 'C:/Temp/Requests/testTable.TBL'; # Load the function. my $LoadTableFromFile = new Win32::API("myDll.dll","myTableLoadFromFil +e", 'PP', 'I'); if(not defined $LoadTableFromFile) { die "Can't import API myTableLoadFromFile(): $!\n"; } my $isLoaded = $LoadTableFromFile->Call($pathATable,$packedaTable); print("Table loaded");
What I get it's the famous "Segmentation fault (core dumped)".

Regarding the pack J, you can't remove it. Reread my last post, your forgetting the difference between a HANDLE and a LPHANDLE on API 0.68. What I've understood it's that LPHANDLE is treated as a pointer to a string, and has to be packed to get a \xAB\xCD\xED\x00 format. While HANDLE is a number. Well in my case twice, since I'm using a Win64 machine. This time I've pased $packedaTable. I don't feel terribly confident about this part, so if you think I'm missing something, please point it out.

Final question, are you on 32 bits or 64 bit windows and what is the calling convention of your C functions? I think you have cdecl functions, since a no parameter stdcall and a no parameter cdecl are interchangable and identical under the hood, so whats might be why create doesn't crash, but myTableLoadFromFile does. Well, my machine is 64 bits. You think that's the issue?. About the calling convention, In the header file of the library there isn't any explicit reference to the calling convention for passing parameters. There is only a reference to the "storage class" with __declspec. For example for myTableLoadFromFile,

#ifdef MYTABLE_EXPORTS # define MYTABLE __declspec(dllexport) #else # define MYTABLE __declspec(dllimport) #endif MYTABLE enum cStatus myTableLoadFromFile(char const* filename, myTable +* raw_result);
But I agree, it's pretty suspicious that it seems to work when we needn't pass any parameter (as in myTableCreate) and breaks if we pass them (myTableLoadTableFromFile).

Replies are listed 'Best First'.
Re^7: WIN32::API and void*
by bulk88 (Priest) on Jul 25, 2012 at 20:52 UTC
    I think I wasn't clear. On your or your client's 64 bit Windows machine, are you running a 32 bit perl or a 64 bit perl? (I think your 64 bit perl because J returned 8 bytes).

    In C header files traditionally, if __stdcall isn't written in the function declaration AFTER the C preprocessor (WINAPI and CALLBACK both become __stdcall), it is a __cdecl. MSVC by default is __cdecl and I assume every other Windows 32bit compiler in the world is. I've never see an actual use of the command line switch used to switch the default from cdecl to stdcall in my life Try
    # Load the function. my $LoadTableFromFile = new Win32::API("myDll.dll","myTableLoadFromFil +e", 'PP', 'I', '_cdecl');
    At this point, 32 or 64, it doesn't matter, since I am running low on ideas. You must use '_cdecl' exactly that way, a typo in 0.68 means '__cdecl' and 'cdecl' become stdcall.

    I am starting to think your DLL has problems and its not a problem with API. Does the DLL actually work if you write C code for it? Googling "ctbl_v2.dll" returned nothing so its something private you have. Next idea, now I'm shooting in the dark, can you tried changing
    my $pathATable = 'C:/Temp/Requests/testTable.TBL';
    to
    my $pathATable = 'C:\Temp\Requests\testTable.TBL'; #no ""s
    ?

    Another idea, lets assume there are secret members after opaque, change
    # pack the table. my $packedaTable = pack('J',$aTable); print Dumper($packedaTable); if(unpack('J',$packedaTable) == $aTable) {print "I know how to pack this table.\n";} else {die "I dont know how to pack this table";}
    to
    # pack the table. my $packedaTable = pack('J',$aTable); print Dumper($packedaTable); if(unpack('J',$packedaTable) == $aTable) {print "I know how to pack this table.\n";} else {die "I dont know how to pack this table";} $packedaTable .= "\x00" x 100000000; #100MB allocation
    48120353 sounds like a reasonable memory pointer. Hmm, when you say "What I get it's the famous "Segmentation fault (core dumped)".", are you on Windows or on Linux? Windows doesn't have "Segmentation faults" it has "access violations". The string "Segmentation fault" isn't even in Perl. Assuming your using Windows, have you clicked the "To see what data this error report contains click here" thing and then "view technical information", and looked where the crash happened and if it happened in your private DLL or not?

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

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

      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.

      By the way this is the part of the code that works, in case somebody needs it:

      #! /usr/bin/perl use Win32::API; # Load the table creator from the dll. # C signature: __declspec(dllimport) myTable myTableCreate(); # typedef # struct myTable { # void* opaque; ///< Opaque pointer to storage # } myTable; $pass=1; my $CreateTable = new Win32::API("myDll.dll","HANDLE myTableCreate()") +; if(not defined $CreateTable) { die "Can't import API myTableCreate(): $!\n"; } #Create empty tables. $pass = 1; if($pass) { my $LeftTable = $CreateTable->Call(); my $RightTable = $CreateTable->Call(); my $outputTable = $CreateTable->Call(); # Make sure that the function does return a memory position, and n +ot a 0. $pass = $pass && ($LeftTable>0 && $RightTable>0); if($pass){print("Empty Tables Succesfully Created. Pass code: ".$p +ass.".\n");}; } # pack the table. if($pass) { my $packedLeftTable = pack('J',$LeftTable); my $packedRightTable = pack('J',$RightTable); my $packedOutputTable = pack('J',$outputTable); $pass = $pass && (unpack('J',$packedLeftTable) == $LeftTable && un +pack('J',$packedRightTable) == $RightTable && unpack('J',$packedOutpu +tTable) == $outputTable); if($pass){ print("Pack-Unpack round trip completed. Pass code: ".$ +pass."\n");} } # Path definition. my $pathTableLeft = 'C:/Temp/Requests/testTable1.TBL'; my $pathTableRight = 'C:/Temp/Requests/testTable2.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. my $LoadTableFromFile = new Win32::API("myDll.dll","myTableLoadFromFil +e", '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 = $LoadTableFromFile->Call($pathTableLeft,$packed +LeftTable); my $isLoadedRight = $LoadTableFromFile->Call($pathTableRight, $pac +kedRightTable); $pass = $pass && ($isLoadedLeft==0 && $isLoadedRight==0); if($pass) {print("Tables: ".$pathTableLeft." and ".$pathTableRight." loa +ded correctly. Pass code: ".$pass."\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");} } }

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

        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?