;; 
;;=====================================================================================----- 
;; 
;;FUNCTION		Memory()  
;;  
;;AUTHOR		Glenn Barnas 
;;  
;;ACTION		Returns the Available Physical RAM in a local or remote system  
;;  
;;VERSION		4.0  - 2013/11/02 
;; 
;;HISTORY               1.0  - 2004/02/04 - Initial Release 
;;                      2.0  - 2005/02/25 - update to allow larger memory sizes (x86) 
;;			3.0  - 2007/10/05 - rewrite to support x64 systems, tighten code 
;;                      4.0  - 2013/11/02 - rewrite to detect large memory blocks on 
;;                                          post-Vista platforms 
;;  
;;SYNTAX		Memory([system])  
;;  
;;PARAMETERS		System - OPTIONAL - String 
;;                      - name of system to query. Default is local system 
;; 
;;REMARKS		Returns Physical RAM size available to the O/S using registry 
;;                      memory allocation map. Returns @ERROR on registry read failure, 
;;                      or 13 / "Data is Invalid" if reg is empty DOES NOT return 
;;                      physical hardware ram value! Some systems allocate RAM to BIOS 
;;                      cache or video adapters and this memory is not reported. This 
;;                      can be adjusted for using the second example below. 
;;  
;;RETURNS               Integer - Available Physical RAM (in Megabytes)  
;;  
;;DEPENDENCIES          None  
;;  
;;TESTED WITH           WinXP, Vista, Win7, Win8 
;;                      Windows Server 2000, 2003, 2008, 2012 
;;                      Tested with up to 16G of RAM  
;; 
;;EXAMPLES              ; Get the available RAM from a remote computer 
;;                      $RAM = Memory('ThatPC') 
;;                       
;;                      ; Adjust for BIOS/Video ram to estimate total physical RAM 
;;                      ; by using the smallest deployed DIMM module as an increment. 
;;                      ; Use "1024" for full GB increments. If the GB flag is used, 
;;                      ; this method must be adjusted to define MSize in GB values 
;;                      ; (e.g. 0.5 instead of 512).  
;;                      $MSize = 512            ; Size of smallest RAM module (DIMM) 
;;                       
;;                      $Mem = Memory()         ; get reported memory 
;;                      $Ma  = $Mem / $MSize    ; determine number of modules installed 
;;                       
;;                      ; If a fractional module is detected, add another full 
;;                      ; module this accounts for a fractional module used for 
;;                      ; BIOS cache or video RAM 
;;                      If $Mem Mod $MSize $Ma = $Ma + 1 EndIf 
;;                       
;;                      ; $Mem now holds a value based more closely on installed 
;;                      ; rather than available RAM 
;;                      $Mem = $Ma * $MSize     ; Memory is #_modules * Mod_Size 
;;                       
;; 
; 
Function Memory(Optional $_System, Optional $_fGB)
 
  Dim $_MemMap				; Physical Memory Map 
  Dim $_Start, $_End, $_Step		; for/next start & step increment 
  Dim $_Sum				; running sum of memory from map 
  Dim $_Idx				; temporary index var 
  Dim $_iMem				; Memory region size index 
  Dim $_Error				; Error placeholder 
  Dim $_Block				; memory block string 
 
  ; Insure $_System has "\\System\" format if it is specified  
  If $_System <> ''
    $_System = '\\' + Join(Split($_System, '\'), '', 3) + '\'
  EndIf
 
  ; Get the memory value from the registry  
  $_Idx = $_System + 'HKLM\hardware\resourcemap\system resources\physical memory'
  $_MemMap = ReadValue($_Idx, '.Translated')
 
  ; Check for invalid read and Return  
  If Len($_MemMap) = 0 Or @ERROR
    $Memory = 0				; return 0 Mbytes  
    $_Error = IIf(@ERROR, @ERROR, 13) 	; Return "Data is Invalid" if no error but data is blank  
    Exit $_Error
  EndIf
 
  ; determine system's O/S type based on architecture 
  $_Idx = $_System + 'HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Environment'
  If ReadValue($_Idx, 'PROCESSOR_ARCHITECTURE') = 'AMD64'
    $_Step  = 40	; Define the step size based on the O/S Architecture 
    $_Start = 33	; Offset where memory descriptor blocks start 
    $_Sum   = 0.0	; no unreported base memory to account for 
    $_iMem  = 10	; Memory size index 
  Else
    $_Step  = 32	; Define the step size based on the O/S Architecture 
    $_Start = 41	; Offset where memory descriptor blocks start 
    $_Sum   = 737280.0	; account for base memory not identified in memory map - x86 only 
    $_iMem  = 2		; Memory size index 
  EndIf
  $_End = Len($_MemMap) - 8
 
  For $_Idx = $_Start to $_End Step $_Step
    $_Block = SubStr($_MemMap, $_Idx, $_Step)
 
    If SubStr($_Block, $_iMem, 1) > 3
      $_Sum = $_Sum + CDbl(Val('&' + SubStr($_Block, $_Step - 1, 2))) * 4294967296.0
                    + CDbl(Val('&' + SubStr($_Block, $_Step - 3, 2))) * 16777216.0
                    + CDbl(Val('&' + SubStr($_Block, $_Step - 5, 2))) * 65536.0
                    + CDbl(Val('&' + SubStr($_Block, $_Step - 7, 2))) * 256.0
                    + CDbl(Val('&' + SubStr($_Block, $_Step - 9, 2)))
    Else
      $_Sum = $_Sum + CDbl(Val('&' + SubStr($_Block, $_Step - 1, 2))) * 16777216.0
                    + CDbl(Val('&' + SubStr($_Block, $_Step - 3, 2))) * 65536.0
                    + CDbl(Val('&' + SubStr($_Block, $_Step - 5, 2))) * 256.0
                    + CDbl(Val('&' + SubStr($_Block, $_Step - 7, 2)))
    EndIf
  Next
 
  ; Sum is in Bytes - return the total as megabytes (or, optionally, Gigabytes) 
  If $_fGB
    $Memory = CInt($_Sum / 1073741824)		; as GB 
  Else
    $Memory = CInt($_Sum / 1048576)		; as MB 
  EndIf
 
  Exit 0
 
EndFunction