Exploring RapiDemo Initializations

The RapiDemo application relies on the MFC implementation of the Property Sheet Control. This type of control is really nothing more than a stack of dialog boxes, surmounted by a control that looks like the tabs in an index file, recipe box, or your junior high school binder. The function of the property sheet control is to arbitrate "page changing" behavior. When a user clicks on a tab, the property sheet control brings the corresponding dialog to the top of the z-order.

This means that each "page" of the property sheet must have a dialog resource from which the sheet can build a view. You don't have to worry about making the dialog templates exactly the same size because the property sheet control is assembled based on the size of the largest constituent page. However, the styles of the dialog resources are important. Set these styles for property page dialog resources, using the File.Properties dialog:

  • On the Styles Tab, set Style to "Child", Border to "Thin", check the "Title Bar " checkbox, and clear other checkboxes.
  • On the More Styles tab, check the "Disabled" checkbox.

Now, let's dissect the code for the application-specific behaviors of the classes that make up RapiDemo.

The Application Object, RapiDemoApp

The code for the class that implements the application object, CRapiDemoApp, is generated for us, for the most part. We make a few small modifications to the RapiDemoApp.cpp file. First, we add the header file for the class that implements the property sheet. (If you forget to do this, you'll get compiler error messages naming the CAllPages class members as undefined symbols.)

#include "AllPages.h"

We also modify the InitInstance() member, where the property sheet object is constructed, initialized, and launched as a modal dialog.

////////////////////////////////////////////////////////////////////
// The one and only CRapiDemoApp object

CRapiDemoApp theApp;

////////////////////////////////////////////////////////////////////
// CRapiDemoApp initialization

BOOL CRapiDemoApp::InitInstance()
{

   AfxEnableControlContainer();


   CAllPages   AllPages( "RapiDemo", NULL, 0 );
   m_pMainWnd = &AllPages;
   AllPages.AddPropPages();
   AllPages.DoModal();

   return FALSE;
}

We declare and initialize an object of the CAllPages class. This class implements the property sheet control, and is derived from CPropertySheet. CPropertySheet has three constructors. The one we use to construct the CAllPages object takes the following arguments: a character string specifying the caption for the property sheet control, the handle to the control's parent window, and the index of the property page that is initially visible. A property page's index is determined by the order in which it was added to the control. Passing a NULL parent window handle sets the application's main window as parent of the property sheet control.

Next, we call the AllPages AddPropPages() member function, which adds the individual pages to the property sheet control. Calling the DoModal() member launches the property sheet control, making it visible when the application opens.

Construction and Initializing The Property Sheet Object, AllPages

The CAllPages class contains the code that implements the property sheet control. First, we make modifications to both the class header file, AllPages.h. For starters, we add some include files:

#include "WalkReg.h"                 // "Walk Registry Tree" page's
                                     // class header
#include "SystemStatusPage.h"        // "System Status" page's class
                                     // header
#include "RemoteFileAccessPage.h"    // "Remote File Access" page's
                                     // class header

#include <rapi.h>

The first three #include files are for the classes that implement the behavior of the individual pages in the property sheet control. The last one is the RAPI header and is needed in any source file that calls RAPI functions. (If you forget to add this include file, RAPI function calls and structures will come up as undeclared symbols when you build.)

// Implementation
public:
   CRemoteFileAccessPage m_RemoteFileAccessPage;
   CSystemStatusPage     m_SystemStatusPage;
   CWalkReg              m_WalkRegPage;
   void AddPropPages();
   virtual ~CAllPages();
   HICON m_hIcon;

We add three member variables, one for each property page of the property sheet control. These members are typed according to the class that implements the behavior for its specific page. We also add a function prototype for the AddPropPages(); member.

Next, we make some additions to the AllPages.cpp file, adding the AddPropPages() member function.

////////////////////////////////////////////////////////////////////
// CAllPages message handlers

void CAllPages::AddPropPages()
{
   m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
   m_psh.dwFlags |= PSP_USEHICON;
   m_psh.hIcon    = m_hIcon;
   m_psh.dwFlags |= PSH_NOAPPLYNOW;    // Lose the Apply Now button
   m_psh.dwFlags &= ~PSH_HASHELP;      // Lose the Help button

   AddPage(&m_RemoteFileAccessPage);
   AddPage(&m_WalkRegPage);
   AddPage(&m_SystemStatusPage);

}

The first part of this function manipulates the PROPSHEETHEADER structure, a base class data member that defines the appearance and behavior of the property sheet control. It's a fairly large structure that allows great flexibility in the creation of the property sheet control. The m_psh base class data member is a pointer to this structure.

The next three lines add property page objects to the property sheet control. The pages' indices are the reverse of the order in which they were added. Put another way, in the example shown above, the leftmost tab of this property sheet control will be the "System Status" page, the "Walk The Registry Tree" tab will be in the middle, and the rightmost tab will be for the "Remote File Access" page.

Initializing The System Status Property Page

In this case, we don't have to make any changes to the header file for the CSystemStatusPage class. All of our modifications are to the SystemStatusPage.cpp implementation file. by including 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()
{
   CPropertyPage::OnInitDialog();

   SYSTEM_INFO               SystemInfo;
   STORE_INFORMATION         StoreInfo;
   SYSTEM_POWER_STATUS_EX    PowerStats;
   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 code below 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 a 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 CeRapiInit() function. 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 CERapiUninit() companion function.

//Uninitialize RAPI when you are finished making RAPI calls
CERapiUninit().

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 the function name is prefixed by "Ce". Notice we use sprintf() to format strings to insert in the m_SysStatsList listbox. 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",
                          VersionInfo.dwBuildNumber);
m_SysStatsList.InsertString( -1, szInfoBuff );

sprintf( szInfoBuff, "    %s : %li", "Platform Id",
                          VersionInfo.dwPlatformId);
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",
                          MemStats.dwMemoryLoad);
m_SysStatsList.InsertString( -1, szInfoBuff );

sprintf( szInfoBuff, "    %s : %li", "Total Physical Memory",
                          MemStats.dwTotalPhys);
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;
}   SYSTEM_POWER_STATUS_EX;


CeGetSystemPowerStatusEx(
                        (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",
                          PowerStats.BatteryFlag);
m_SysStatsList.InsertString( -1, szInfoBuff );

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

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

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

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

sprintf( szInfoBuff, "    %s : %li", "Backup Battery Full LifeTime",
         PowerStats.BackupBatteryFullLifeTime);
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 ");
      break;
         .
         .
         .
   case PROCESSOR_HITACHI_SH4: 
   default:
      sprintf( szProcessorType, "%s", " Unknown ");
      break;
}    //end switch

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

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

m_SysStatsList.InsertString( -1, "  " );

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

      CeRapiUninit();
return TRUE;

Looking Ahead

In the next installment, we'll look into initializing the System Status and Remote File Access pages of the property sheet control.



About the Author

Nancy Nicolaisen

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 and CodeGuru, she has written several books, including Making Win 32 Applications Mobile.

Comments

  • There are no comments yet. Be the first to comment!

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • The impact of a data loss event can be significant. Real-time data is essential to remaining competitive. Many companies can no longer afford to rely on a truck arriving each day to take backup tapes offsite. For most companies, a cloud backup and recovery solution will eliminate, or significantly reduce, IT resources related to the mundane task of backup and allow your resources to be redeployed to more strategic projects. The cloud - can now be comfortable for you – with 100% recovery from anywhere all …

  • With JRebel, developers get to see their code changes immediately, fine-tune their code with incremental changes, debug, explore and deploy their code with ease (both locally and remotely), and ultimately spend more time coding instead of waiting for the dreaded application redeploy to finish. Every time a developer tests a code change it takes minutes to build and deploy the application. JRebel keeps the app server running at all times, so testing is instantaneous and interactive.

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds