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

I'm trying to fix a serious bug in my Win32::SystemInfo module that affects Win9x. The fix involves including a DLL with the module that will determine the CPU speed. The DLL contains code released by Intel, and I have successfully used it in a Visual C++ based project at work. I can't seem to get it work with Win32::API, however.
The function I'm calling in the DLL is defined like this:
struct FREQ_INFO cpuspeed(int clocks);. FREQ_INFO is a struct that contains four unsigned longs.

My test code is this:
use strict; use Win32::API; my $FREQ_INFO = pack("L4",0,0,0,0); my $CpuSpeed ||= new Win32::API ("cpuinf32", "cpuspeed", ["I"], "P") or die "Could not load DLL\n"; $FREQ_INFO = $CpuSpeed->Call(0); my ($in_cycles, $ex_ticks, $raw_freq, $norm_freq) = unpack("L4",$FREQ_ +INFO); print "In cycles: $in_cycles\n"; print "Ex ticks: $ex_ticks\n"; print "Raw freq: $raw_freq\n"; print "Norm freq: $norm_freq\n";

Running this code generates a Privileged Instruction exception. I thought that the problem might lie in the code I'm calling, since it executes ring 0 operations on the CPU. I changed the Win32::API call to import a different function, called cpunormspeed, that takes an int and returns an unsigned long. When I make that function call, I get a generic application error.
Am I making an error in the return parameter? Should I just get the return value in an I or N and unpack it without packing first?

Guildenstern
Negaterd character class uber alles!

Replies are listed 'Best First'.
(tye)Re: Difficulties with Win32::API
by tye (Sage) on Oct 31, 2000 at 22:25 UTC

    Okay, you can't use "P" for a return value unless what is being returned is a simple '\0'-terminated string. So you have to get the raw pointer back (a return value that is a struct that won't fit into a register is returned as a pointer to the struct -- I think). So you use a return value of "I" and use little-used parts of unpack() [and even pack()]. I also removed the one argument being passed in as that got it working for me:

    use strict; use Win32::API; my $CpuSpeed ||= Win32::API->new ("cpuinf32", "cpuspeed", [], "I") or die "Could not load DLL\n"; my $aInfo = $CpuSpeed->Call(); my $pInfo = pack("L",$aInfo); my $sInfo = unpack("P16",$pInfo); my ($in_cycles, $ex_ticks, $raw_freq, $norm_freq) = unpack("L4",$sInfo); print "In cycles: $in_cycles\n"; print "Ex ticks: $ex_ticks\n"; print "Raw freq: $raw_freq\n"; print "Norm freq: $norm_freq\n";

    The reason that you can't use "P" is because Perl really hates to deal with pointers except to buffers that it allocated itself. If you use "P", then Win32::API will look at that address for the first '\0' and copy all of the characters upto and including it into a buffer that Perl likes, which would mean that the copy will be missing much of the data since the long values are small enough that they have zero bytes in them.

    So here is what is going on in the working code...

    $aInfo gets set to an integer containing the address of the buffer that Perl doesn't want to deal with very well. pack() builds a string, $pInfo, that contains this same pointer value because the only way Perl (by itself) can deal with foreign pointers is via "p" and "P" of unpack() and those require that the pointer be packed into a string. unpack("P16") copies the 16 bytes that the pointer points to into a buffer that Perl likes, $sInfo. The final unpack() pulls the values out of this copy of the struct.

            - tye (but my friends call me "Tye")
(Guildenstern: Strange Solution) Re: Difficulties with Win32::API
by Guildenstern (Deacon) on Oct 31, 2000 at 22:57 UTC
    OK, after much help from tye, I was able to hack together something that sems to work. I added a new function to the DLL that looks like this:
    struct FREQ_INFO GetCpuSpeed() { return cpuspeed(0); }

    The only thing I'm doing different here is to remove the parameter to the GetCpuSpeed call, since 99.99999% of the time cpuspeed gets called with parameter 0 anyway. Works like a champ now (after adding tye's code to make sure I get numbers out that make sense).

    Guildenstern
    Negaterd character class uber alles!