Calling some of the simpler functions in kernel32.dll.

...very carefully !

First, a little WARNING !

Trying to access Windows DLL's, especially those that perform an operation on your file system, can be hazardous to both your health and the health of your computer, so if you aren't sure of what you are doing, don't !

In this lesson I am going to try and illustrate how one can access some of the simple functions in kernel32.dll. This DLL is just a library of routines which the programmer can use in his own programs. Since the standard Windows DLL's are all written in C, we have to write a 'wrapper' for each of the functions we are trying to use, which links our EUPHORIA program to these C functions, and the information they return.

Most of these C functions return a value, or set of values, by giving the C function a memory POINTER to the desired location of these values. We create the block of memory where we want our information stored, by using the allocate() function, which reserves a block of memory for our use, and returns a value which points to that block of memory. We can use this POINTER to retrieve the information we want, by using the peek() functions in EUPHORIA.

The first kernel32.dll function our program will access is the GlobalMemoryStatus function. It is described in the Win32 Programmers Reference in the following way, using C programming syntax.

( ..first, it's definition.. )
The GlobalMemoryStatus function retrieves information about current 
available memory. The function returns information about both physical 
and virtual memory.
( ..then, the actual 'C' lingo..)
VOID GlobalMemoryStatus(
    LPMEMORYSTATUS lpBuffer     // pointer to the memory status structure  
   );
( ..then, the information it requires.. )
Parameters

lpBuffer

Points to a MEMORYSTATUS structure in which information about current 
memory availability is returned. Before calling this function, the calling 
process should set the dwLength member of this structure. 

( ..and the MEMORYSTATUS structure is defined as:.. )

typedef struct _MEMORYSTATUS { // mst  
    DWORD dwLength;        // sizeof(MEMORYSTATUS) 
    DWORD dwMemoryLoad;    // percent of memory in use 
    DWORD dwTotalPhys;     // bytes of physical memory 
    DWORD dwAvailPhys;     // free physical memory bytes 
    DWORD dwTotalPageFile; // bytes of paging file 
    DWORD dwAvailPageFile; // free bytes of paging file 
    DWORD dwTotalVirtual;  // user bytes of address space 
    DWORD dwAvailVirtual;  // free user bytes 
} MEMORYSTATUS, *LPMEMORYSTATUS;

All this really means, since a DWORD is 4 bytes, and there are 8 of them, is that we have to allocate 32 bytes for our structure, and 'poke' the size of that structure into the first 4 bytes of that structure, before calling the function.

Since GlobalMemoryStatus is really a procedure, and does not return a value, ( as indicated by the VOID reference in its description ), we have to call it with w32Proc(), being sure to give it a pointer to our allocated memory block.

When the function returns, the allocated block of memory will contain the specified information, in 4 byte chunks. We can access this with the EUPHORIA peek4u() function, and place the data into a sequence. Once this is done, we can then return the allocated memory to the system with free().

The following demo program gets the Windows memory information in it's GetMemory() procedure. Please note that the percentage of memory displayed also includes the available memory in the Windows 'cache' at the time, which for most systems, is a variable size, created by Windows 'on-the-fly'.

Since we're at it, I'm also going to get the path of your Windows directory, the path of this program, and the current time, by calling three more functions in kernel32.dll.
These routines are:
GetWindowsDirectoryA,
GetCurrentDirectoryA and,
GetLocalTime.

All this information will be displayed by this program, and the memory and time will be updated every second, by calling an onTimer[] event handler.
This timer will trigger an onPaint[] event handler, which will then update the displayed information.
A pointer to kernel32.dll is created in the onOpen[] event handler, which also creates our timer, sets our timer value to 1 second, and fetches our directory information.

-- sysinfo.exw

-- dynamically displays some system information.
-- wolf/Jan.23/99

include win32lib.ew
without warning

global constant
  MemWin  = create( Window, "", 0, 0, 0, 400, 140, 0 ),
  MyTimer = 1,
-- links to our API functions
  zGlobalMemoryStatus = registerw32Procedure( kernel32, "GlobalMemoryStatus", {C_POINTER} ),
  zGetWindowsDirectory = registerw32Procedure( kernel32, "GetWindowsDirectoryA", {C_POINTER, C_INT} ),
  zGetCurrentDirectory = registerw32Function( kernel32, "GetCurrentDirectoryA", {C_INT, C_POINTER}, C_INT ),
  zGetCurrentTime = registerw32Procedure(kernel32, "GetLocalTime", {C_POINTER} )

sequence meminfo, windirstring, currentdirstring
atom buffer

procedure GetMemory()
  buffer = allocate(32)
  poke4(buffer,32)
  w32Proc(zGlobalMemoryStatus,{buffer})
  meminfo = peek4u({buffer,8})
  free(buffer)
end procedure

-- GetWindowsDirectoryA requires a pointer to the allocated memory,
-- and the value of its length.
-- ( 100 here 'should' be long enough, we hope! )
-- It puts a zero terminated string into the allocated buffer.
function GetWindowsDir()
  sequence WinDir
  integer i
  buffer = allocate(100)
  w32Proc(zGetWindowsDirectory, {buffer, 100})
  WinDir = ""
  i = 0
  while peek(buffer+i) != 0 do
    WinDir = WinDir & peek(buffer+i)
    i = i+1
  end while
  free(buffer)
  return WinDir
end function

-- GetCurrentDirectoryA requires a length value,
-- and a pointer to the allocated memory.
-- It also puts a zero terminated string into the allocated buffer.
function GetCurrentDir()
sequence CurrentDir
atom i, ok
  buffer = allocate(4) -- first get the length using this dummy length.
  i=w32Func(zGetCurrentDirectory, {4, buffer})
  if i=0 then
    ok = message_box( "Couldn't link to C function GetCurrentDirectoryA",
                    "Win32 Error", MB_ICONHAND+MB_TASKMODAL )
    free(buffer)
    abort(1)
  end if
  free(buffer)
  buffer = allocate(i)  -- do it again, with the correct length this time!
  i=w32Func(zGetCurrentDirectory, {i, buffer})
  CurrentDir = ""
  i = 0
  while peek(buffer+i) != 0 do
    CurrentDir = CurrentDir & peek(buffer+i)
    i = i+1
  end while
  free(buffer)
  return CurrentDir
end function

-- and here's the 'time' structure, used by the next function.
--  WORD wYear; 
--  WORD wMonth; 
--  WORD wDayOfWeek; 
--  WORD wDay; 
--  WORD wHour; 
--  WORD wMinute; 
--  WORD wSecond; 
--  WORD wMilliseconds;
-- where a WORD is 2 bytes.

function Getcurrenttime()
sequence currenttime
  buffer = allocate(16)
  w32Proc(zGetCurrentTime, {buffer})
  currenttime = ""
  for i = 0 to 15 do
    currenttime = currenttime & peek(buffer+i)
  end for
  free(buffer)
  return currenttime
end function

procedure onPaint_MemWin(atom self, atom event, sequence params )
  sequence s, t, string1
  GetMemory()
  setPenPos(MemWin,2,2)
  string1= "bytes of memory = " & sprintf("%d",meminfo[3])
  wPuts(MemWin,string1)
  setPenPos(MemWin,2,20)
  string1= "percent of memory in use  = " & sprintf("%d",meminfo[2]) & "%"
  wPuts(MemWin,string1 & "  - including 'virtual cache'.")
  setPenPos(MemWin,2,38)
  string1= "free memory bytes = " & sprintf("%d",meminfo[4])
  wPuts(MemWin,string1)

  setPenPos(MemWin,2,56)
  wPuts(MemWin,"Windows directory = " & windirstring)

  setPenPos(MemWin,2,74)
  wPuts(MemWin,"this program's directory")
  setPenPos(MemWin,2,92)
  wPuts(MemWin,"= " & currentdirstring)

  s=Getcurrenttime()
  t=sprintf("%02d",s[9..10]) & ":" & sprintf("%02d",s[11.12]) 
             & ":" & sprintf("%02d",s[13..14]) 
  setText(MemWin,t & "  - - System Info - -"
end procedure

procedure onTimer_MemWin( atom self, atom event, sequence params )
    repaintWindow( MemWin )
end procedure  
  
procedure onOpen_MemWin(atom self, atom event, sequence params )
  atom kernel32 
  kernel32 = open_dll("kernel32.dll")
  if kernel32 = 0 then
    puts(1,"Couldn't open kernel32.dll!\n")
  end if
  setTimer( MemWin, MyTimer, 1000 )
  windirstring=GetWindowsDir()
  currentdirstring=GetCurrentDir()
end procedure

setHandler( MemWin, w32HOpen,  routine_id( "onOpen_MemWin" ) )
setHandler( MemWin, w32HTimer,  routine_id( "onTimer_MemWin" ) )
setHandler( MemWin, w32HPaint,  routine_id( "onPaint_MemWin" ) )


WinMain(MemWin,Normal)
----

As most of you probably know, Euphoria has it's own functions for getting time and getting the current directory, but I decided to illustrate the 'Windows' way here, just as a simple example of accessing DLL's.

If you study the code and the Win32 Programmers Reference, you will also probably notice I have taken a short-cut in the GetWindowsDir() function, by calling it as a procedure, and ignoring the returned value of the call to the GetWindowsDirectoryA function. ...seems to work though.

The illustrated call to GetCurrentDirectoryA is a much more 'proper' call to a function, and includes some error checking as well, but it's still not perfect.

Note also, that this function is actually called twice. It is used the first time just to get a length value in ( buffer ) for the length of the directory 'string', since we don't know ahead of time, what it will be. In our code for GetWindowsDir(), we just took a guess as to the maximum length of the string, ( 100 ), and hoped it would be long enough.

I have also included a simple variation of the above program:
meminfo.exw, which displays just the memory info, with the percentage displayed in the window title, so if you minimize the program, it will display this in the taskbar. ...enjoy!

While I'm here, I should point out that meminfo.exw, and 3 other examples in these 'tuts', refer to onDestroy[], which doesn't even work in win32lib's versions 0.55.1, or 0.55.5. No major concern, and fixed in subsequent versions.

..end of lesson.

CONTENTS