Examining Remote System Status

Wednesday Jul 14th 2004 by Nancy Nicolaisen

Learn how to remotely monitor system status with Nancy's RAPI Demo example program.

This month, we'll continue our examination of the RAPI Demo example program. The RAPI Demo illustrates the use of a great variety of tools for monitoring CE devices from the desktop, as well as some techniques for making embedded CE systems physical and logical extensions of desktop applications. The example code for this lesson is available here. In this lesson, we learn how to remotely monitor system status.

Initializing The System Status Property Page

Let's take a look at the code that implements the System Status page of the property sheet control. In the case of RAPI Demo, we don't have to make any changes to the header file for the CSystemStatusPage class. All of our modifications are to the implementation file SystemStatusPage.cpp. The first of these modifications is the inclusion of the RAPI header.

#include <rapi.h>

The behavior of the System Status page is entirely implemented in the initialization member function, CSystemStatusPage::OnInitDialog().

BOOL CSystemStatusPage::OnInitDialog() 

   SYSTEM_INFO               SystemInfo;
   STORE_INFORMATION         StoreInfo;
   MEMORYSTATUS              MemStats;
   CEOSVERSIONINFO           VersionInfo;
   char                      szInfoBuff[64];

The data declaration in OnInitDialog() consists almost entirely of structures that will be familiar from earlier examples. The following code illustrates the use of their members, but not exhaustively. The key thing to keep in mind about these structures is that all of their string members will be reported to your program in Unicode format. To display them in the list control, all strings must first be translated back into multibyte character set format.

   //Initialize RAPI before use
   HRESULT hr = CeRapiInit();
   if ( hr != ERROR_SUCCESS )
      { return FALSE; }

Before we can use the RAPI subsystem, we must make it available to our application by calling the function CeRapiInit(). Always test the return from this call before continuing with RAPI operations. When you are finished making RAPI calls, you must uninitialize the subsystem and free its resources by calling the companion function CERapiUninit().

//Uninitialize RAPI when you are finished making RAPI calls

You may wonder why we didn't just initialize RAPI in the constructor of the application object RapiDemoApp and uninitialize it in the destructor. In theory, this seems the sensible thing to do. However, in my experience, the RAPI connection tends to be more stable if initialization and uninitialization closely bracket your RAPI operations.

The first RAPI call we make is CeGetVersionEx(). RAPI function names and argument lists are generally the same as the CE side counterpart, except "Ce" prefixes the function name. Notice we use sprintf() to format strings to insert in the listbox m_SysStatsList. The members of the CEOSVERSIONINFO structure being displayed are all of type DWORD. Being numeric, they need no translation and can be passed as arguments to single byte string handling routines.

CeGetVersionEx((LPCEOSVERSIONINFO) &VersionInfo);

sprintf( szInfoBuff, "%s", "CE Version Stats:" );
m_SysStatsList.InsertString( -1, szInfoBuff );

sprintf( szInfoBuff, "    %s : %li", "Major Version",
         VersionInfo.dwMajorVersion );
m_SysStatsList.InsertString( -1, szInfoBuff );

sprintf( szInfoBuff, "    %s : %li", "Minor Version",
         VersionInfo.dwMinorVersion );
m_SysStatsList.InsertString( -1, szInfoBuff );

sprintf( szInfoBuff, "    %s : %li", "Build Number",
m_SysStatsList.InsertString( -1, szInfoBuff );

sprintf( szInfoBuff, "    %s : %li", "Platform Id",
m_SysStatsList.InsertString( -1, szInfoBuff );
m_SysStatsList.InsertString( -1, "  " );

Next, we use CeGlobalMemoryStatus() to retrieve memory statistics. Once again, we can use the MEMORYSTATUS structure's numeric members without translation.

CeGlobalMemoryStatus((LPMEMORYSTATUS) &MemStats);

sprintf( szInfoBuff, "%s", "CE Memory Stats:" );
m_SysStatsList.InsertString( -1, szInfoBuff );

sprintf( szInfoBuff, "    %s : %li", "PerCent Usage",
m_SysStatsList.InsertString( -1, szInfoBuff );

sprintf( szInfoBuff, "    %s : %li", "Total Physical Memory",
m_SysStatsList.InsertString( -1, szInfoBuff );

The next call, CeGetSystemPowerStatusEx(), queries the power status of the CE device. The members of the returned structure specify whether the system is running on AC (wall outlet) or DC (battery) power, whether or not the batteries are currently charging, and the remaining life of main and backup batteries. If the system being queried doesn't support all these status requests, it returns -1 in unsupported members. Its arguments, in the order shown, are the address of a SYSTEM_POWER_STATUS_EX structure, and a Boolean flag that instructs the function to acquire the most current information directly from the driver as opposed to using older, cached information.

Notice the use of the sprintf() format string "%x" for ACLineStatus, BatteryFlag, BatteryLifePercent, BackupBatteryFlag, and BackupBatteryLifePercent structure members. These are all single byte values. We haven't used the CE side analog of this function in previous articles, and so include the typedef of the SYSTEM_POWER_STATUS_EX structure:

typedef struct _SYSTEM_POWER_STATUS_EX {
   BYTE ACLineStatus;
   BYTE BatteryFlag;
   BYTE BatteryLifePercent;
   BYTE Reserved1;
   DWORD BatteryLifeTime;
   DWORD BatteryFullLifeTime;
   BYTE Reserved2;
   BYTE BackupBatteryFlag;
   BYTE BackupBatteryLifePercent;
   BYTE Reserved3;
   DWORD BackupBatteryLifeTime;
   DWORD BackupBatteryFullLifeTime;

           (PSYSTEM_POWER_STATUS_EX) &PowerStats, TRUE);
sprintf( szInfoBuff, "%s", "CE Power Stats:" );
m_SysStatsList.InsertString( -1, szInfoBuff );

sprintf( szInfoBuff, "    %s : %x", "AC Line Status",
         PowerStats.ACLineStatus );
m_SysStatsList.InsertString( -1, szInfoBuff );

sprintf( szInfoBuff, "    %s : %x", "Battery Flag",
m_SysStatsList.InsertString( -1, szInfoBuff );

sprintf( szInfoBuff, "    %s : %x", "Battery Life Percent",
m_SysStatsList.InsertString( -1, szInfoBuff );

sprintf( szInfoBuff, "    %s : %li", "Battery Full LifeTime",
m_SysStatsList.InsertString( -1, szInfoBuff );

sprintf( szInfoBuff, "    %s : %x", "Backup Battery Flag",
m_SysStatsList.InsertString( -1, szInfoBuff );

sprintf( szInfoBuff, "    %s : %x", "Backup Battery Life Percent",
m_SysStatsList.InsertString( -1, szInfoBuff );

sprintf( szInfoBuff, "    %s : %li", "Backup Battery Full LifeTime",
m_SysStatsList.InsertString( -1, szInfoBuff );

m_SysStatsList.InsertString( -1, "  " );

Next, we retrieve storage status information with a call to CeGetStoreInformation(). The returned structure gives the size of the CE device's object store and the unused space in the object store.

CeGetStoreInformation((LPSTORE_INFORMATION) &StoreInfo);
sprintf( szInfoBuff, "%s", "CE Store Information:" );
m_SysStatsList.InsertString( -1, szInfoBuff );

sprintf( szInfoBuff, "    %s : %li", "Store Size", StoreInfo.dwStoreSize );
m_SysStatsList.InsertString( -1, szInfoBuff );

sprintf( szInfoBuff, "    %s : %li", "Free Size", StoreInfo.dwFreeSize );
m_SysStatsList.InsertString( -1, szInfoBuff );
m_SysStatsList.InsertString( -1, "  " );

Now, we get detailed information on the CE device's processor, with a call to CeGetSystemInfo(). (Several of the processor constants are omitted from the switch() code below, but they are shown in their in their entirety in the SystemStatusPage.cpp file listing.)

CeGetSystemInfo((LPSYSTEM_INFO) &SystemInfo);

sprintf( szInfoBuff, "%s", "CE System Info:" );
m_SysStatsList.InsertString( -1, szInfoBuff );

sprintf( szInfoBuff, "    %s : %li", "Page Size",
         SystemInfo.dwPageSize );
m_SysStatsList.InsertString( -1, szInfoBuff );

char szProcessorType[48];
switch(SystemInfo.dwProcessorType )
   case PROCESSOR_INTEL_386:
      sprintf( szProcessorType, "%s", " PROCESSOR_INTEL_386 ");
      sprintf( szProcessorType, "%s", " Unknown ");
}//end switch

   sprintf( szInfoBuff, "    %s : %s", "Processor Type", szProcessorType );
m_SysStatsList.InsertString( -1, szInfoBuff );

sprintf( szInfoBuff, "    %s : %li", "Allocation Granularity",
m_SysStatsList.InsertString( -1, szInfoBuff );

m_SysStatsList.InsertString( -1, "  " );

Finally, we uninitialize RAPI, and make a clean exit from the SystemStatusPage initialization member.

return TRUE;

Looking Ahead

In our next installment, we'll learn about a true workhorse of the RAPI suite, CeFindAllFiles(). This function is an important tool because it has no analog on the CE side. To create a powerful, comprehensive tool for CE file handling, working from the desktop is our best approach.

About the Author

Nancy Nicolaisen is a software engineer who has designed and implemented highly modular Windows CE products that include features such as full remote diagnostics, CE-side data compression, dynamically constructed user interface, automatic screen size detection, and entry time data validation.

In addition to writing for Developer.com, she has written several books, including Making Win 32 Applications Mobile.

# # #

Mobile Site | Full Site
Copyright 2017 © QuinStreet Inc. All Rights Reserved