Beefy Boxes and Bandwidth Generously Provided by pair Networks
"be consistent"
 
PerlMonks  

Re: WIN32::API and void*

by bulk88 (Priest)
on Jul 23, 2012 at 20:52 UTC ( [id://983235]=note: print w/replies, xml ) Need Help??


in reply to WIN32::API and void*

__declspec(dllimport) myTable myTableCreate();
Are you sure that that is myTable and not myTable *?

void * is a char * in Win32 API because of bad grandfathered design decisions. If you want a C void * in Win32 API use UINT_PTR instead. UINT_PTR is a pointer sized integer, not a pointer to a integer in C and Win32 API.

my $CreateTable = new Win32::API("myDll.dll","myTable myTable Create() +");
I think "myTable myTable" is a typo and "Create()" is a type, you said earlier the function is called myTableCreate. In any case, you can't return a Win32::API::Struct as the return value since its not implemented (see https://github.com/bulk88/perl5-win32-api/blob/master/API.xs#L767 and https://github.com/bulk88/perl5-win32-api/blob/master/API.pm#L287 ), hmm, that fact is not in the pod, I better fix that.
my $LoadTableFromFile = new Win32::API("myDll.dll","int myTableLoadFro +mFile(char const* filename, LPMyTable pointToMyTable)")
Get rid of the const. It wont parse for sure. Try replacing LpMyTable with LPHANDLE, or upgrading to my version of Win32::API if you want a less quirky struct system, https://github.com/bulk88/perl5-win32-api . Reread the pod in my version also.
__declspec(dllimport) enum myStatus myTableLoadFromFile(char const* fi +lename, MyTable* pointToMyTable);
enums won't parse and are not supported by Win32 API. Change "enum myStatus" to an int (I think you already did that), and write some perl code, maybe as a hash, to decode the int to an english identifier. Also get rid of __declspec(dllimport), that won't parse either. As nice as Win32 API's C prototype parsing system sounds https://github.com/bulk88/perl5-win32-api/blob/master/API.pm#L417, it is nowhere near what a real C compiler will parse, improve it if you can ( I couldn't ), the info you need is here http://msdn.microsoft.com/en-us/library/ttt561z3.

I think your fudging/anonymizing your API to the point where I can't understand it. My final question is, why are you using the myTable struct instead of a UINT_PTR */LPHANDLE? is myTable something you invented to emulate void ** or myTable is from your C API's headers? does myTable have other secret members after member opaque? Does "myTable myTableCreate();" really return a "myTable" and not a "myTable *"?

Replies are listed 'Best First'.
Re^2: WIN32::API and void*
by sgv_81 (Novice) on Jul 24, 2012 at 08:41 UTC

    Bulk88, thanks for your reply. You made truly helpful points.

    Here you are some Q's and A's. Spoiler alert, some of them might be quite obvious (sorry, newbie):

    "If you want a C void * in Win32 API use UINT_PTR instead." Hence,

    Win32::API::Struct->typedef( cTbl => qw{UINT_PTR opaque;});
    ?

    "Are you sure that that is myTable and not myTable *?".

    Yes, it returns the object myTable and not myTable*. At least according to the h files. (I would have expected a pointer too)

    "think 'myTable myTable' is a typo "".

    You are absolutely right, it should read as the declaration above. Luckily the typo wasn't in my code. How can I introduce changes after Ctrl-C Ctrl-V something? I call it a finger dislexia.

    "In any case, you can't return a Win32::API::Struct as the return value since its not implemented"".

    Does this mean that I can't use Win32::API to link to this function? Is ther any workaround? Because without this functionality, I think I may not be able to call any other function. At the end of the day, tables need to be created to pass them as parameters.

    "Try replacing LpMyTable with LPHANDLE, or upgrading to my version of Win32::API".

    I'm using Win32::API 0.68, is there a newer version in CPAN I have totally ignored? Anyways, if I go for LPHANDLE, I understand that this should work:

    my $LoadTableFromFile = new Win32::API("myDll.dll","int myTableLoadFro +mFile(char* filename, LPHANDLE pointToMyTable)")
    This gets rid of the Line 23 Error (Can't call mehtod "Pack on an undeifned value at ./API32.pl line 23).

    "My final question is, why are you using the myTable struct instead of a UINT_PTR */LPHANDLE?"

    No smart answers to that one, in the defininition of myTable in the API header:

    typedef struct myTable{ void* opaque; ///< Opaque pointer to storage } myTable;
    So, through the documentation in http://search.cpan.org/~cosimo/Win32-API-0.68/API.pm#USING_STRUCTURES I've decided that the way to define this structure in perl is
    Win32::API::Struct->typedef( myTable=> qw{PVOID opaque;});
    Probably, PVOID has to be changed by UINT_PTR (?) or are you suggesting that all the myTable calls should be changed into LPHANDLE or UINT_PTR * (I don't think I have understood the difference between them). Actually not defining the structure in perl would be nicer.

    "is myTable something you invented to emulate void ** or myTable is from your C API's headers? does myTable have other secret members after member opaque? Does "myTable myTableCreate();" really return a "myTable" and not a "myTable *"? "

    Well, I wish this were me trying to opaque my API as much as possible. What happens it's that I'm just the client of this library wich handles some talbes I normally handle them using its GUI interface but I need to compare every day that there are no changes between to different sets of tables (more than 40 in each set). This is why, I'm trying to write a script that checks the differences making calls to this library and write a report with any changes. I have a compiled dll and the h files with the exports of the library, so it's not really I have the flexibility to modify that part of the C code. I'm quite restricted to the function definitions provided in the dll, I'm afraid. I can't see any other members after/before the opaque member, but again I can't see the implementation of myTable, only the h files and according to those files, myTableCreate() returns myTable not myTable*.

      The CPAN version of Win32::API is 0.68, that version has alot of problems. I found that it was faster for me to write and compile XS code than use Win32::API 0.68. I found myself spending more time in the C debugger looking at the C stack and overriding package level subs in Win32::API with my own implementations at runtime, stepping through the Perl code in Win32::API and finding silent failures instead of fatal errors when things go wrong inside Win32::API's Perl side or api usage mistakes from the user/me, and basically saying to myself, is this a Win32::API bug, or my Perl code's bug all the time. Wasn't Win32::API made for the case where you want it to be EASIER than C? My Win32::API prototypes looked like
      $api = Win32::API::new->($dll, $function, "N", "NNNNNNNNNNNN");
      with me doing all the zero/sign extends, packing, [pack a double to a 8 byte string, split into 2 4 byte chunks, unpack the 2 4 byte strings to pack's J letter, then feed as 2 numeric scalar integers into 2 "N"s into Win32::API, remember endianess!], pointer contents reading into scalar, struct building, by hand with un/pack(). Just use XS at this point. So I tried to fix Win32::API.

      "In any case, you can't return a Win32::API::Struct as the return value since its not implemented"".

      Does this mean that I can't use Win32::API to link to this function? Is ther any workaround? Because without this functionality, I think I may not be able to call any other function. At the end of the day, tables need to be created to pass them as parameters.


      The LPHANDLE I tell you to use will not be packed and unpacked automatically with 0.68, LPHANDLE (and all pointers to numeric things) are pointers to strings in 0.68, and the C API will get a char * with a printable number in ASCII if you dont pack it yourself. The "Pack()" command warnings you see are because Pack() is broken in 0.68 (and has been since Day 1 of C prototype parsing in Win32::API, late 1990/early 2000s). Try installing my non-CPAN .70/.71 of Win32::API https://github.com/bulk88/perl5-win32-api and read its pod and changes file too, you might find you problems went away. If you must use the CPAN version, here is an example how to use LPHANDLE with 0.68.
      { #old API psuedo pointer handling my $pass = 1; my $hnd = "\x00" x length(pack('J', 0)); $function = new Win32::API($test_dll, 'BOOL __stdcall GetHandle(LPHAND +LE pHandle)'); $pass = $pass && defined($function); #takes "\xAB\xCD\xED\x00" $pass = $pass && $function->Call($hnd) == 1; $hnd = unpack('J', $hnd); $pass = $pass && $hnd == 4000; ok($pass, 'GetHandle operates correctly'); $pass = 1; $function = new Win32::API($test_dll, 'BOOL __stdcall FreeHandle(HANDL +E Handle)'); $pass = $pass && defined($function); #takes 123 $pass = $pass && $function->Call($hnd) == 1; ok($pass, 'FreeHandle operates correctly'); }


      IMHP myTableCreate basically returns a "HANDLE" in Win32 API speak, or a void * in C. PVOID is a char * in Win32 API, which will never work for you, since Win32 API will try to read the string that is AT the mem address that myTableCreate returns. When you pass that "char *" back to your C API in myTableLoadFromFile, the C API will get some truncation of what it expects at the target of "opaque" (null character means end of string), at a totally different memory address that is from the Perl Scalar and not the memory address myTableCreate returned, and myTableCreate better be returning a valid memory address and not a handle/record number/GUID/encrypted pointer. I seriously would try your best NOT to use Win32::API::Struct unless you have to, I tried to clean it up somewhat for 0.71 (check my github commits) but there are too many architectural flaws with how C type intelligence works with Win32::API/::Struct/::Type. myTable after call contains 1 and only 1 member, which is a opaque pointer, so its really a void ** at the end of the day, and since you create the myTable, and it comes from your memory, it has no other slices after opaque.

      Unrelated to Win32::API, would myTableLoadFromFile ever change the value of member opaque between calls to myTableLoadFromFile? I know from COM-ish APIes, void ** is used to pass in a object, with the function possibly destroying the existing one, and putting in a new one. Unless the api designer is crazy (he might already be from what you said), if myTableLoadFromFile just wanted an object instance, it would ask for the void *. If it wants be able to destroy, and/or replace the object instance, it will ask for a void **. Some C APIs, unlike Kernel32 Windows, return error/status codes/integers from all functions, so to create an object instance a void ** must be supplied, because the return value is used for something else and there can only be 1 return value.

      Final note, neither 0.68 or my 0.71 support pass by copy structs. There is a hack around it on 32 bit Windows. I mentioned it above briefly.

        Thanks. It seems that using your version would mean a lot less of a headache for me, unfortunately, the script won't be running on my machine, and its owner/admin has pushed for this version of WIN32::API. Probably with the only aim of making my existance a little bit more challenging... So, I'm going to try to make the script work with HANDLES and LPHANDLES.

        IMHP myTableCreate basically returns a "HANDLE" in Win32 API speak, or a void * in C: Which basicly means that the way to properly declare myTableCreate() is:  my $CreateTable = new Win32::API("ctbl_v2.dll","HANDLE cTblCreate()"); And $CreateTable will hold the HANDLE of the new empty table.

        This is how I've put together the perl code so far:

        #! /usr/bin/perl use Win32::API; # Load the table creator from the dll. # Some info about the API function: # 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"; } $pass = $pass && defined($CreateTable->Call()) == 1; #Create an empty table. if($pass) { $aTable = $CreateTable->Call(); print("Table Created"); } else { die "Cant Create a Table"; }

        This code at least doesn't crash and prints "Table Created". I have ignored the "pack" and "unpack" calls since the API function returns a HANDLER directly. Let me know if I got this wrong.

        Let's try to "fill" the table with data from a file using the API function int myTableLoadFromFile(*char filename, myTable* raw_result). It seems that raw_result will be a void**, probably for the reasons you pointed out. Using some fuzzy logic I've determined that must be LPHANDLER (pointer to myTable, hence pointer to HANDLER, hence LPHANDLER).

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

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

        my $LoadTableFromFile = new Win32::API("myDll.dll","int myTableLoadFro +mFile(char* filename, LPHANDLE raw_result)"); if(not defined $LoadTableFromFile) { die "Can't import API HANDLELoadFromFile(): $!\n"; } my $pathTable = 'C:/Temp/Requests/TestTable.TBL'; $pathTable = pack('Z',$pathTable); $pass = 1; $pass = $pass && defined($LoadTableFromFile->Call($pathTable,$aTable)) + == 1; if($pass) { my $isLoaded = $LoadTableFromFile->Call($pathTable,$aTable); print("Table loaded"); } else { die "Can't load the table from the file $pathTable"; }

        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?

Log In?
Username:
Password:

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

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

    No recent polls found