Beefy Boxes and Bandwidth Generously Provided by pair Networks
Clear questions and runnable code
get the best and fastest answer
 
PerlMonks  

Solved: How to use 'unsigned char' C data type with Win32::API

by sam_bakki (Pilgrim)
on Mar 12, 2012 at 11:55 UTC ( [id://959112]=perlquestion: print w/replies, xml ) Need Help??

sam_bakki has asked for the wisdom of the Perl Monks concerning the following question:

Dear Perl Monks,

I need a help in Win32::API

I am working on a hardware test automation, The interface to hardware is USB-I2C and we have a C DLL which exports set of functions to communicate with hardware.

I am trying to import the C DLL via Win32::API module so i can automate some test cases via perl. Right now, I could able to read & write to particular memory address using the exported APIs in DLL

I have a problem with one API , Which is i2cGetDeviceAddress , it returns unsigned char. I could not able to properly use Win32::API to get the data from this API. This API is working well in C code

I have given API prototype in C , C code and its output and my perl code

Please help me to use 'unsigned char' with Win32::API module.

use Win32::API; #C proptype # typedef unsigned char __stdcall i2cGetDeviceAddress_type(); # extern i2cGetDeviceAddress_type *i2cGetDeviceAddress; #C++ Prototype # extern "C" Byte __stdcall i2cGetDeviceAddress(void); # C Code # printf ("\n \n DEV: %d , %c , %x ", i2cGetDeviceAddress(),i2cGetDevi +ceAddress(),i2cGetDeviceAddress()); # # Output: DEV: 70 , F , 46 #Perl Code # When i use 'char' as a return type , API is not returning anything # my $i2cGetDeviceAddressFunc = Win32::API->new('CrdI2C32', 'char i2cG +etDeviceAddress()') or warn "\n ERROR: Can not import API:i2cGetDevic +eAddress , $^E ,"; # When i use 'unsigned char' as a return type, I get, Win32::API::pars +e_prototype: WARNING unknown output parameter type 'unsigned' #my $i2cGetDeviceAddressFunc = Win32::API->new('CrdI2C32', 'unsigned c +har i2cGetDeviceAddress()') or warn "\n ERROR: Can not import API:i2c +GetDeviceAddress , $^E ,"; # So, I used INT , The following way is working for some other API but + not this API :( my $i2cGetDeviceAddressFunc = Win32::API->new('CrdI2C32', 'INT i2cGetD +eviceAddress()') or warn "\n ERROR: Can not import API:i2cGetDeviceAd +dress , $^E ,"; my $ret = 0; $ret = $i2cGetDeviceAddressFunc->Call(); $ret = unpack ('C*',pack ("i",$ret)); print "\n $ret "; # prints 152 $ret = sprintf ("0x%X",$ret); print "\n $ret"; #prints 0x98 but i wanted 0x46

Update:
OS: Windows 7 Perl: Active Perl 5.12 , x86 flavour (MSWin32-x86-multi-thread)

UPDATE: Problem is solved

The above code itself is fine. I was confused with API call sequence. When I call I2CWrite API and then i2cGetDeviceAddress , It is providing correct output (i.e 70)

Thanks a Lot to BrowserUk his suggestion was useful to me to implement some other API using Win32::API

Thanks to tye for the $ret &= 0xFF; tip. I did not know this, That is why i am using complex pack & unpack

Replies are listed 'Best First'.
Re: How to use 'unsigned char' C data type with Win32::API
by BrowserUk (Patriarch) on Mar 12, 2012 at 13:44 UTC
    # So, I used INT , The following way is working for some other API but not this API :(

    That should work.

    Given this dll:

    #include <stdio.h> __declspec(dllexport) unsigned char __stdcall getChar() { unsigned char c = (unsigned char)( rand() & 0xFF ); printf( "Returning %d\n", c ); return c; }

    This script:

    #! perl -slw use strict; use Win32::API; my $getChar = Win32::API->Import( 'junkdll', 'int getChar()' ) or die $!, $^E; print getChar() for 1 .. 10;

    Produces this output:

    C:\test>t-w32api.pl Returning 41 41 Returning 35 35 Returning 190 190 Returning 132 132 Returning 225 225 Returning 108 108 Returning 214 214 Returning 174 174 Returning 82 82 Returning 144 144

    Not the most convenient form (numeric) to receive 'char' data, but you seem to want the numeric value anyway. You're just working too hard at trying to convert the value when there is no need to.


    With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.

    The start of some sanity?

      Hi BrowserUk

      For some reason, having 'int' and getting data from my DLL is not working well.

      my $i2cGetDeviceAddressFunc = Win32::API->new('CrdI2C32', 'int i2cGetD +eviceAddress()') or warn "\n ERROR: Can not import API:i2cGetDeviceAd +dress , $^E ,"; my $ret = 0; $ret = $i2cGetDeviceAddressFunc->Call(); print "\n RET: $ret";

      Output

      1. Run RET: 53113496 2. Run RET: 52195992 3. Run RET: 52851352 4. Run RET: 52458136 5. Run RET: 51540632

      Output is not what i expected and more over every run, it produce different output. But the C code which calls the i2cGetDeviceAddress() API produces same output always.

      I don't have source code of the DLL (CRDI2C32.DLL) , We bought this from other company, They gave C, C++, VB & PASCAL example code which has API prototypes.

      PASCAL Sample: xx.pas Function i2cGetDeviceAddress : Byte; + stdcall; external 'CrdI2C32.DLL'; C Sample: xx.h typedef unsigned char __stdcall i2cGetDeviceAddress_type(); extern i2cGetDeviceAddress_type *i2cGetDeviceAddress; xx.c i2cGetDeviceAddress_type *i2cGetDeviceAddress = NULL; .... i2cGetDeviceAddress = (i2cGetDeviceAddress_type*)GetProcAddress(hDll, +"i2cGetDeviceAddress"); if (i2cGetDeviceAddress == NULL) { FreeLibrary(hDll); return 22; // function not found in library } VB Sample: xx.bas Declare Function i2cGetDeviceAddress Lib "CrdI2C32.DLL" () As Byte

        I didn't look in detail (I suspect the relevant details could be buried quite deep), but it looks likely to be a bug in Win32::API to me.

        Even if not a bug in Win32::API, it looks like an uninitialized four-byte value is having only some of its bytes overwritten. So why not just work around it and pull out only the byte you care about?

        $ret &= 0xFF;

        Though, the return values you show actually show the lowest two bytes being consistently 0x7298, which seems rather unlikely.

        printf "%d 0x%x\n", $_, $_ for 53113496, 52195992, 52851352, 52458136, 51540632 53113496 0x32a7298 52195992 0x31c7298 52851352 0x3267298 52458136 0x3207298 51540632 0x3127298

        ...So it could easily be neither of the things I guessed. Is 0x98 (152) the value you are expecting to get?

        - tye        

        We bought this from other company

        The one you have seems to be broken. What size is it? You probably need to send it back under warranty and exchange it for a new one.


        With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
        Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
        "Science is about questioning the status quo. Questioning authority".
        In the absence of evidence, opinion is indistinguishable from prejudice.

        The start of some sanity?

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://959112]
Approved by marto
Front-paged by Corion
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others cooling their heels in the Monastery: (5)
As of 2024-03-19 05:45 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found