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

I'm trying to write a Perl script to access a device through National Instrument's gpib driver (specifically through their dll).

Looking at the documentation for the Win32::API module, I can see how to access functions in a dll file. However, I also will need access to some global pointer variables from the dll. I haven't figured out if the Win32::API module can give me access to those variables.

Does anyone have any suggestions or ideas on how to access variables in a dll file?

--------------------

Before the questions begin, I probably should share a few other details about my reasons for not pursuing other methods.

  1. gpib::ni --> This uses XS and I'm encountering a compiler issue that is preventing the module from installing. My search of the web leads me to believe that the issue is related to using a different compiler (and/or compiler version) that what ActiveState used to compile their Perl distribution.

    (Contacting the module developer for help is no longer an option. Based on information on his web site, he passed away last year.)

  2. XS, SWIG, Inline --> These methods/module requires the use of a compiler, which probably means that I'll have a similar issue as described above.

    Plus, my end goal is to use ActiveState's PerlApp to create a stand-alone executable that can be run from any Windows box. The systems that would actually use the code don't have access to the internet and I don't want the headache of managing the Perl environment of possibly hundreds of systems.

    Anyways, I'd like to avoid having to distribute a compiler on each machine in order for them to use the stand-alone executable.
Any ideas, suggestions, or pointers would be greatly appreciated.

From NI's documentation on their free driver, the following variables are "exported":

int *user_ibsta; int *user_iberr; int *user_ibcnt; long *user_ibcntl;
After calling some of the functions, I would need to check the value of these variables to determine if an error occurred.

From NI's documentation, here's examples of how to access the variables from C/C++:

HINSTANCE Gpib32Lib = NULL; Gpib32Lib=LoadLibrary("GPIB-32.DLL"); if (Gpib32Lib == NULL) { return FALSE; } Pibsta = (int *) GetProcAddress(Gpib32Lib,(LPCStr)"user_ibsta"); Piberr = (int *) GetProcAddress(Gpib32Lib,(LPCStr)"user_iberr"); Pibcntl = (long *) GetProcAddress(Gpib32Lib,(LPCStr)"user_ibcnt");

Replies are listed 'Best First'.
Re: How to use Win32::API to access variable in DLL file?
by BrowserUk (Patriarch) on Jun 23, 2009 at 00:21 UTC

    You don't need Win32::API nor compiler, to access those values. It can all be done using Win32 that comes with AS Perl's.

    Given a dll that contains this exported integer (compiled as test.dll):

    __declspec(dllexport) int testint = 12345;

    That might be accessed from a C program so:

    #include <windows.h> #include <stdio.h> int main( int argc, char **argv ) { HMODULE hm = (HMODULE)LoadLibrary( "test.dll" ); int *ptestint = (int *)GetProcAddress( hm, "testint" ); printf( "Addr: %x Value: %d\n", ptestint, *ptestint ); return 1; }

    It can be accessed from (AS) perl as follows:

    #! perl -slw use strict; use Win32; my $dll = Win32::LoadLibrary( 'test' ) or die $^E; my $proc = Win32::GetProcAddress( $dll, 'testint' ) or die $^E; print unpack 'V', unpack 'P4', pack 'L!', $proc; __END__ C:\test>\perl32\bin\perl.exe 773754.pl 12345

    The complicated bit is the last line:

    1. pack 'L!', $proc converts the UV returned by GetProcAddress() back into a binary value;
    2. unpack 'P4', ... fetches 4-bytes (the exported integer) from that address;
    3. unpack 'V', ... converts those 4-bytes into a perl scalar;

    Assuming that your running on a platform where int and long and synonymous, that should work for all three values. Otherwise you'll need to adjust the 'V' for the last one.


    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.
      Cool! Thanks! That's exactly what I was looking for: a simple pure Perl solution. Plus, it looks like you saved me a trip through time. :)

      From ActiveState's documention on Win32::GetProcAddress :
      Returns the address of a function inside a loaded library. The information about what you can do with this address has been lost in the mist of time. Use the Win32::API module instead of this deprecated function.

      Now there's one concern in my mind. If I use Win32 to access the variables and Win32::API to access the functions, would the two modules load different instances of the dll? If so, I'll have another problem on my hands.

      If I understand correctly, I suspect that I can follow your example and by using only the pack 'L!', $proc syntax of the last line to get the address of variables and functions. With that, I should be able to get any variable or function in my code to 'point' to a corresponding variable/function in the dll and would not necessarily need Win32::API. Then I don't have to worry about the possibility of my code accessing different instances of the dll.

      If I get this all figured out, I'll be sure to post an update on what I did so that others can learn from my travels guided by your useful information.

      Again, thanks for the help!
        If I use Win32 to access the variables and Win32::API to access the functions, would the two modules load different instances of the dll?

        No. If the dll in question is already loaded, then LoadLibrary() just returns a handle to it.

        If I understand correctly, I suspect that I can follow your example and by using only the pack 'L!', $proc syntax of the last line to get the address of variables and functions. With that, I should be able to get any variable or function in my code to 'point' to a corresponding variable/function in the dll and would not necessarily need Win32::API. Then I don't have to worry about the possibility of my code accessing different instances of the dll.

        Like I say, not a possibility. Besides which, whilst you can get the procedure addresses in the same way, the Pure Perl technique for invoking that address is still lost in the mists of time. On top of that, how would you set up the parameter passing?


        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.
Re: How to use Win32::API to access variable in DLL file?
by syphilis (Archbishop) on Jun 23, 2009 at 06:44 UTC
    ...the issue is related to using a different compiler (and/or compiler version) that what ActiveState used to compile their Perl distribution

    ActiveState used VC 6.0, and there can be issues if you use a later version of VC. However, you could use the feely available MinGW compiler and avoid the trouble you're referring to - as MinGW uses the msvcrt.dll runtime (same as VC 6.0).

    Just ppm install MinGW, then set ACTIVEPERL_CONFIG_CC=gcc and you're right to build by running:
    perl Makefile.PL
    nmake test
    nmake install

    If nmake is not in your path, use dmake instead. (It was also installed with ppm install MinGW.)

    Cheers
    Rob
Re: How to use Win32::API to access variable in DLL file?
by thunders (Priest) on Jun 22, 2009 at 21:23 UTC

    It sounds like the least painful option may be to get XS stuff working on your platform

    You may get more mileage from Strawberry Perl which comes packaged with the gcc compiler that built it. If you need an installable .exe file PAR should be a viable option for Strawberry Perl

    If that's not an option, because you need code specific to ActiveState's distribution one of the following freely availible Visual C++ compilers may be able to build the XS code Visual C++ 2008 Express or Visual C++ 2005 Express