views:

1323

answers:

3

I need to know how much physical memory a windows machine has, using Perl.

I've tried using Win32::SystemInfo. However this module states the following caveat:

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

So on a machine which has 2-4 GB of physical memory, I get a false answer.

Is there a way to get the correct amount of physical memory? Perhaps another module? Or directly using Win32::API?

Edit: From the comments people gave here, it looks like the limitation is in the Win32 API , and not specific to Win32::SystemInfo. However, the OS does know exactly how much physical ram is available, so there must be a way to extract that information. If not in Perl then maybe in another language?

+1  A: 

I can only assume that the caveats attending Win32::SystemInfo's results are also caveats attending the raw Win32 API calls, as Perl itself certainly has no problem handling such large numbers. In which case the possibility of extracting accurate information looks a bit bleak.

I've also heard in passing that current 32-bit versions of Windows can only use about 3.2Gb of RAM on a machine that has >= 4Gb installed, which may be hearsay, but which jibes with the limitation being in the API itself.

j_random_hacker
It depends on how much memory space is being reserved by hardware. Example my machine with 3GB physical shows a bit under 3GB available because I have a 1GB video card.
Darryl Braaten
That makes sense. Perhaps the Win32::SystemInfo problem and the 3.2Gb limit problem are really separate problems then.
j_random_hacker
+1  A: 

This information can be pulled from WMI, or using SNMP if you choose to enable SNMP on the box it will be running on. For WMI, I don't have a Perl example offhand but for a VBScript example see below.

Ref: http://www.microsoft.com/technet/scriptcenter/guide/sas_wmi_dieu.mspx

strComputer = "."

Set objSWbemServices = GetObject("winmgmts:\\" & strComputer)
Set colSWbemObjectSet = _
 objSWbemServices.InstancesOf("Win32_LogicalMemoryConfiguration")

For Each objSWbemObject In colSWbemObjectSet
 Wscript.Echo "Total Physical Memory (kb): " & _
 objSWbemObject.TotalPhysicalMemory
Next

Tested on my XP system and it retrieves the desired results (only 1.5GB RAM here, sorry). I'm quite sure there are WMI interfaces for Perl as well if you want to stick with Perl. If SNMP is an option, the total physical memory can be obtained from SNMP as well using one of the Perl SNMP libraries.

EDIT: Just noticed @Mr. Muskrat's comment regarding Microsoft KB http://support.microsoft.com/kb/274558 - evidently the behavior you're seeing with Perl is a limitation of the Win32 API call, so you might end up with the same results with WMI. Unfortunately I don't have a 2-4GB RAM machine to try this on to verify.

Jay
Thank you , this worked correctly , even on a machine with 4GB. However, I did find a perl solution, which I''ve posted.
Tom Feiner
+1  A: 

As stated in the comments, this is an issue of GlobalMemoryStatus, as it can return answers up to 2GB. And GlobalMemoryStatusEX which solves this issue of the 2GB limit, but only works on 64 bit systems (as far as I can tell).

In the end I'm using the following Perl code, which uses Win32::OLE and WMI class Win32_PhysicalMemory, which returns the correct amount of physical memory even on 32bit systems:

use strict;
use warnings;
use English;
use Win32::OLE qw( EVENTS HRESULT in );
use Readonly;

sub get_physical_memory {
    my $machine                         = shift || '.'; # Default to local machine
    my Readonly $WMI_MEMORY_CLASS_NAME  = 'Win32_PhysicalMemory';
    my Readonly $MEGABYTE               = 1024*1024;

    my $WMI = 
        Win32::OLE->GetObject( "winmgmts:{impersonationLevel=impersonate,(security)}//$machine/" ) || die "Could not get Win32 object: $OS_ERROR";
    my $total_capacity                  = 0;

    foreach my $object ( in( $WMI->InstancesOf( $WMI_MEMORY_CLASS_NAME ) ) ) {
        $total_capacity += $object->{Capacity};
    }

    my $total_capacity_in_mb            = $total_capacity / $MEGABYTE;
    print "Total Memory : $total_capacity_in_mb \n";
    return $total_capacity_in_mb;

}

Tom Feiner