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

$pass = $pass && defined($CreateTable->Call()) == 1;
You probably leaked a table. Do doing the Call() means you run the C function and the defined check is meaningless since the result will be 0 (probably NULL pointer/failure for the C function) or a pointer (12345 format), never undefined. Check defined only on $CreateTable API obj. I posted code from a test suite of Win32::API, you need to change it a little bit more.
$aTable = $CreateTable->Call(); print("Table Created");
Correct, try printing $aTable to the screen, I prefer Data::Dumper.
use Data::Dumper; $Data::Dumper::Useqq = 1; print Dumper($aTable);
$aTable should be a decimal number, 123456 style. "HANDLER" isn't a C or Win32 API type. Its "HANDLE". If you tried HANDLER you should be getting warnings printed to console, but I'm not sure if 0.68 will report unknown types or not.
About the whole packing and unpacking of the handlers before passing them out, I'm not sure whether I should pack $aTable or not. I understand that because it's the value returned by an API function, $aTable should already be stored with the appropiate format (unless there is any transformation behind the scenes before perl stores its value).
"appropiate format", no its not. Thats why 0.68 sucks.
I have packed the string with the file path since in the header files it's advised that the string containing the filename has to be null terminated (I've checked http://perldoc.perl.org/functions/pack.html and sounds like a pack('Z', string) to me).
Useless, all scalar strings in perl are automatically null terminated at all times secretly so they can be passed to OS/library C function that want null terminated (even if you feed binary garbage to the C function, the C function will hit a null in your binary garbage, or hit the secret null, either way, it will never buffer overflow. This is a ansi/ascii null, not a UTF16 null, if your doing UTF16 in perl, you need to write "\x00\x00" yourself. The secret null character never appears in the perl language, so if you put the scalar to disk, or through a network, then you need to add the null yourself with pack or
.= "\x00";
Something isn't right since I get a "Segmentation fault (core dumped)" error. Which means I'm not passigng the correct arg for LPHANDLER raw_result. I've tried to "$aTable=pack('J',$aTable);" before passing it to myTableLoadfromFile, but I still get the same error. Any idea what I'm missing?
The pack J is what your missing. After you pack $aTable, do a print Dumper on it, it should be 4 or character, some of the characters might be in "\123" octal escape sequence since they are unprintable binary. You can also try
$packedaTable = pack('J', $aTable); die "bad code" if unpack('J', $packedaTable) != $aTable;
you can also scrap the C prototype interface of Win32 API and use the letter interface.
my $LoadTableFromFile = new Win32::API("myDll.dll","int myTableLoadFro +mFile(char* filename, LPHANDLE raw_result)");
becomes
my $LoadTableFromFile = new Win32::API("myDll.dll", "myTableLoadFromFi +le", 'PP', 'I');
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.

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.

Replies are listed 'Best First'.
Re^6: WIN32::API and void*
by sgv_81 (Novice) on Jul 25, 2012 at 18:52 UTC

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

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