Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ramiroencinas/f474e8432e7fb8ec7f299a107430709d to your computer and use it in GitHub Desktop.
Save ramiroencinas/f474e8432e7fb8ec7f299a107430709d to your computer and use it in GitHub Desktop.
Getting Windows Memory Usage with NativeCall

Getting Windows Memory Usage with NativeCall

Raku NativeCalls provide a way to interact with dynamic libraries that follow the C calling convention and are very useful for obtaining information from the operating system, such as memory usage.

In this article we will see how to get the memory usage from a Windows system.

The MEMORYSTATUSEX C++ structure

Win32 API provides the MEMORYSTATUSEX structure:

typedef struct _MEMORYSTATUSEX {
  DWORD     dwLength;
  DWORD     dwMemoryLoad;
  DWORDLONG ullTotalPhys;
  DWORDLONG ullAvailPhys;
  DWORDLONG ullTotalPageFile;
  DWORDLONG ullAvailPageFile;
  DWORDLONG ullTotalVirtual;
  DWORDLONG ullAvailVirtual;
  DWORDLONG ullAvailExtendedVirtual;
} MEMORYSTATUSEX, *LPMEMORYSTATUSEX;

that contains information about the current memory in bytes, including:

  • Total physical memory: ullTotalPhys
  • Available physical memory: ullAvailPhys

These two members will allow us to calculate the memory usage by substracting ullAvailPhys from ullTotalPhys.

The MEMORYSTATUSEX structure has the dwLength member that needs to be set before calling the function GlobalMemoryStatusEx that populates the MEMORYSTATUSEX structure.

Declaring the MEMORYSTATUSEX Class with NativeCall

Raku Nativecalls use the repr('CStruct') trait into a declaration class to interact with C structures. We can declare this class to use the C++ MEMORYSTATUSEX structure as follows:

class MEMORYSTATUSEX is repr('CStruct') {    
  has uint32 $.dwLength is rw; 
  has uint32 $.dwMemoryLoad;    
  has uint64 $.ullTotalPhys;
  has uint64 $.ullAvailPhys;
  has uint64 $.ullTotalPageFile;
  has uint64 $.ullAvailPageFile;
  has uint64 $.ullTotalVirtual;
  has uint64 $.ullAvailVirtual;
  has uint64 $.ullAvailExtendedVirtual;
};

It's important mapping the member types between C++ and Raku:

  • The Raku type uint32 is equivalent to C++ Win32 DWORD type.
  • The Raku type uint64 is equivalent to C++ Win32 DWORDLONG type.

The $.dwLength member is rw (read and write) to set the size of the structure later.

The GlobalMemoryStatusEx C++ function

This C++ function populates the MEMORYSTATUSEX structure using as argument the LPMEMORYSTATUSEX pointer type:

BOOL GlobalMemoryStatusEx( LPMEMORYSTATUSEX lpBuffer );

Raku doesn't use pointers to perform a Native Call to this function, instead it uses a Raku function with the native trait as we will see below.

Building the Native function

Raku Native Calls allows to use the C++ GlobalMemoryStatusEx function as follows:

use NativeCall;

sub GlobalMemoryStatusEx(MEMORYSTATUSEX) is native('Kernel32') returns int32 { * };
  • use NativeCall loads the Raku NativeCall context.
  • GlobalMemoryStatusEx is the function name and must be the same as the original name in C++.
  • MEMORYSTATUSEX is the name of the class we have declared before to interact with the C++ struct that will contain the members we need ($.ullTotalPhys and $.ullAvailPhys). Actually, this class acts as the pointer that goes into the argument of the C++ GlobalMemoryStatusEx function.
  • The trait is native('Kernel32') exposes the Win32 library that contains the GlobalMemoryStatusEx function.
  • This function returns a bool value that is represented by the int32 type.

Getting the results

To use the MEMORYSTATUSEX class with the GlobalMemoryStatusEx Native function we need to instanciate it in an object, $data for example:

my MEMORYSTATUSEX $data .=new;

Also, let's not forget to pass the size of the structure (or $data object) setting the $data.dwLength member with the current size. The structure size is the $data object size and we can get it with the function nativesizeof:

$data.dwLength = nativesizeof($data);

Now we are ready to populate the MEMORYSTATUSEX struct ($data object) calling the GlobalMemoryStatusEx Native function with the $data object as argument:

GlobalMemoryStatusEx($data);

The $data object acts like the C++ LPMEMORYSTATUSEX pointer.

Finnaly, the results that we need are in the values of the members of the $data object:

my $memoryUsage = $data.ullTotalPhys - $data.ullAvailPhys;

say "Current Memory Usage: $memoryUsage Bytes.";

Here you have available this example applied in a Raku module.

As we have seen in this example, the use of Raku Native Calls allows extending the Raku's functionality to the universe of the operating system through its dynamic libraries. Furthermore, by making the appropriate calls to different operating systems, we can create applications that work on any of them.

More information about Raku Native calling interface in the Raku documentation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment