http://www.perlmonks.org?node_id=30409
Category: Win32 Stuff
Author/Contact Info Chad Johnston
cjohnston@rockstardevelopment.com
Guildenstern
Description: Memory information for Win32. Total and available physical, page, and virtual memory.
This is my first ever module, so any constructive criticism will be eagerly welcomed!
Update (1 Sept 00): Completely reworked the after tye's comments. This module is very different from the original.

This module has been superceded by Win32::SystemInfo.
#  Copyright (c) 2000 by Chad Johnston.  All rights reserved.

package Win32::MemoryInfo;

use Win32::API;
use strict;
use vars qw($VERSION);

$VERSION = '0.02';

#==================
sub MemoryStatus (\%;$) {
#==================
#
   my $return = shift;    #hash to return
   my $ret_type = shift;  #what format does the user want?
   my %fmt_types = 
   ( B => 1, KB => 1024, MB => 1024*1024, GB => 1024*1024*1024);
   my @params = qw(MemLoad TotalPhys AvailPhys TotalPage
                   AvailPage TotalVirtual AvailVirtual);
   my %results;          #results of fn call
   my $MemFormat;        #divisor for format
   my $dwMSLength;       #validator from fn call

   $MemFormat = 
   ($ret_type =~ /^[BKMG]B?$/) ? $fmt_types{$ret_type} : $fmt_types{B}
+;

   my $GlobalMemoryStatus ||= 
   new Win32::API("kernel32", "GlobalMemoryStatus", ["P"], "V") or ret
+urn;
   
   my $MEMORYSTATUS = pack "L8",(0, 0, 0, 0, 0, 0, 0, 0);

   $GlobalMemoryStatus->Call($MEMORYSTATUS);
   
   ($dwMSLength, @results{@params})  =
   unpack "L8", $MEMORYSTATUS;
   return undef if ($dwMSLength == 0);

   if (keys(%$return) == 0) {
   foreach (@params) {
    $return->{$_} = ($_ eq "MemLoad") ? $results{$_} : $results{$_}/$M
+emFormat;
    }
   }
   else {
    foreach (@params){
     $return->{$_} = $results{$_}/$MemFormat unless (!defined($return-
+>{$_}));
     }
   }
   1;
}

__END__

=head1 NAME

Win32::MemoryInfo - Memory information on Win32 systems

=head1 SYNOPSIS

    use Win32::MemoryInfo;

    my %mHash;
    if (Win32::MemoryInfo::MemoryStatus(%mHash))
    {
     ...process results...
    }
     
    To get specific values: 
    my %mHash = (TotalPhys => 0, AvailPhys => 0);
    if (Win32::MemoryInfo::MemoryStatus(%mHash))
    {
     ...mHash contains only TotalPhys and AvailPhys values...
    }
    
    Change the default return value:
    Win32::MemoryInfo::MemoryStatus(%mHash,"MB");

=head1 ABSTRACT

With this module you can get total/free memory on Win32 systems,
including installed RAM (physical memory) and page file. 

The current version of Win32::MemoryInfo is available at:

  [nowhere yet]

=head1 DESCRIPTION

=over 4

Module provides one function:

=item MemoryStatus

B<MemoryStatus>(%mHash,[$format]);

   %mHash                      - The hash that will receive the result
+s.
                                 Certain values can be set prior to th
+e
                                 call to retrieve a subset. (See below
+)
   $format                     - Optional parameter. Used to set the o
+rder
                                 of magnitude of the results. (See bel
+ow)

   Determines the current memory status of a Win32 machine. Populates
   %mHash with the results. Function returns undef on failure.
   
   Values returned through the hash:
   MemLoad                     - Windows NT 3.1 to 4.0: The percentage
+ of
                                 approximately the last 1000 pages of 
+physical
                                 memory that is in use.
                               - Windows 2000: The approximate percent
+age of
                                 total physical memory that is in use.
   TotalPhys                   - Total amount of physical memory (RAM)
+.
   AvailPhys                   - Available physical memory (RAM).
   TotalPage                   - Allocated size of page (swap) file.
   AvailPage                   - Available page file memory.
   TotalVirtual                - Total physical + maximum page file.
   AvailableVirtual            - Total amount of available memory.
   
   Values returned through the hash can also be specified by setting
   them before the function is called. 
       my %mHash = (TotalPhys => 0);
       Win32::MemoryInfo::MemoryStatus(%mHash);
     
   Will return only the total physical memory.
   
   MemoryStatus return values in bytes by default. This can be changed
+ with
   the $format parameter. Valid values for $format are:
       B        -  Bytes (default)
       KB       -  Kilobytes
       MB       -  Megabytes
       GB       -  Gigabytes

=back

No functions are exported. 

=head1 INSTALLATION

This module can be used by simply placing it in your /Win32 directory
somewhere in @INC.

Use pod2html to build a html version of this POD.

This module requires
Win32::API module by Aldo Calpini

=head1 CAVEATS

The information returned by the MemoryStatus function is volatile. 
There is no guarantee that two sequential calls to this function
will return the same information. 

On computers with more than 4 GB of memory, the MemoryStatus function
can return incorrect information. Windows 2000 reports a value of -1
to indicate an overflow. Earlier versions of Windows NT report a value
that is the real amount of memory, modulo 4 GB. 

On Intel x86 computers with more than 2 GB and less than 4 GB of memor
+y,
the MemoryStatus function will always return 2 GB for TotalPhys. 
Similarly, if the total available memory is between 2 and 4 GB, AvailP
+hys
will be rounded down to 2 GB.  

This module has been created and tested in a WinNT 4.0 environment on 
+ActiveState
port of Perl 5.6. It has B<not> been tested on Win95/98 or Windows 200
+0 yet.

=head1 CHANGES

 0.02 - Removed SetMemResultType and GetMemResultType
      - Changed return type and parameters.
      - Lots of other changes
 0.01 - Initial Release

=head1 BUGS

Please report.

=head1 VERSION

This man page documents Win32::MemoryInfo version 0.02

August 30, 2000.

=head1 AUTHOR

Chad Johnston C<<>cjohnston@rockstardevelopment.comC<>>

=head1 COPYRIGHT

Copyright (C) 2000 by Chad Johnston. All rights reserved.

=head1 LICENSE

This package is free software; you can redistribute it and/or modify
it under the same terms as Perl itself.

=cut
Replies are listed 'Best First'.
RE: Win32::MemoryInfo
by tye (Sage) on Aug 31, 2000 at 01:39 UTC

    Cool.

    One things I hate about Win32::API is its encouragement of creating strings from barewords. Drop the no strict "subs" and just do:

    my $GlobalMemoryStatus ||= Win32::API->new( "kernel32", "GlobalMemoryStatus", ["P"], "V" ) or return;

    I would not export anything nor have a separate routine and simply pass in the format to the routine as "B" (or nothing) for bytes, "K" for 1024, "M" for 1024*1024, or "G" for 1024**3. I would just drop the whole ceil stuff. What is wrong with saying you have 2.4MB of memory available? And put the single remaining exportable symbol into @EXPORT_OK not @EXPORT.

    And the only major change: You have enough parameters being returned that I'd return them as a hash reference so I don't have to keep looking up what order those values come back in. For extra credit, let the user specify a list of parameters that they are interested in. Return a list of just those values or return the hash ref if no particular parameters were requested.

            - tye (but my friends call me "Tye")
      Whew! Sounds like I know what I'm doing this morning. All great suggestions, and they should be pretty easy to implement.
      I do have one question about your suggestion of letting the user specify what values are wanted back. The only thing I can think of off the top of my head is to have the user fill a hash with certain values, i.e. "TotalPhysMem" => 0, "AvailPhysMem" => 0, etc. Then have the function only fill in the values that are present in the hash. How do I get the hash to the function? Do I need to change the prototype from
      @returns = MemoryInfo();
      to
      MemoryInfo(%returns,"M");
      ?

      Guildenstern
      Negaterd character class uber alles!

        I'll start from the beginning because I don't think I'll make much sense otherwise.

        Okay, the basic idea is that, with so many values being returned (3 is usually too many in my book), remembering the exact order of the values is problematic. Also, if you are returning a whole list of things, it'd be nice to design the interface so it can evolve in an intelligent manner.

        So, just like when you have lots of parameters, it is nice to "name" them (func(This=>"that",Count=>2)), you can return "named" values by returning a hash. I try to avoid returning hashes as lists so let's return a reference to a hash.

        One of the nice things about named parameters is that you can leave out any parameters that you aren't interested in. So let's allow the user the same option here. If they only want page file information in megabytes, they can write:

        my( $avail, $total )= MemoryStatus( "MB", "AvailPageFile", "TotalPageFile" );

        The "MB" parameter is optional and we can detect it due to it being different from any of the field names. If you think you may further extend the interface in the future, you might want to make this distinction clearer and allow for extra arguments by having the field names passed as an array reference:

        my( $avail, $total )= MemoryStatus( "MB", [ "AvailPageFile", "TotalPageFile" ] );
        but I don't think that is warranted in this case.

        If the user wants all or most of the items, then they use it like this:

        my $hMem= MemoryStatus( "K" ); my $load= $hMem->{MemoryLoad};

        This is particularly handy for add-hoc queries in the Perl debugger:

        perl -de 0 DB<1> use Win32::MemoryInfo "MemoryStatus" DB<2> x MemoryStatus() 0 HASH(0x1df2628) 'AvailPageFile' => 3026 'TotalPageFile' => 32768 [...]

        And Perl makes this easy to code as well:

        my @fieldNames= qw( MemoryLoad TotalPhys AvailPhys TotalPageFile AvailPageFile TotalVirtual AvailVirtual ); my %unitSize= ( B=>1, K=>1024, M=>1024*1024, G=>1024*1024*1024 ); #[...] my( $sUnits )= "B"; if( @_ && $_[0] =~ /^[BKMG]B?$/i ) { $sUnits= uc substr(shift(@_),0,1); } my( $nUnits )= $unitSize{$sUnits}; my( $dwMSLength )= 0; my( @fieldValues )= (0)x@fieldNames; my $MEMORYSTATUS= pack "L8", $dwMSLength, @fieldValues; $GlobalMemoryStatus->Call($MEMORYSTATUS); ( $dwMSLength, @fieldValues )= unpack "L8", $MEMORYSTATUS; return if 0 == $dwMSLength; for( @fieldValues ) { $_ /= $nUnits; } my $hFields= {}; @$hFields{@fieldNames}= @fieldValues; return @_ ? @$hFields{@_} : $hFields;
                - tye (but my friends call me "Tye")