Initializing The DesktopView With A List Of Remote Databases

In the last installment, we began the examination of a desktop MFC application that scans a remote device, inventorying the databases it finds there. Now, we continue our exploration of this example, RemoteDBScan.

Initialization

When the RemoteDBScan application opens, it displays a control populated with a list of remote databases and their attributes. We build and initialize the list control in the OnInitialUpdate() member of the CRemoteDBScanView class. Building a list view is a two-step process. First, we add columns to the list control, and after that we can add individual rows, one item at a time.

Before we begin to build the list control, we do the base class initialization, calling CListViewEx:: OnInitialUpdate(). Next, we test the CRemoteDBScanView member variable m_bColumnsExist to see whether columns have already been added to the control (m_bColumnsExist is set to FALSE in the constructor for the class). If there are columns already, we bail out of the initialization. Otherwise, we set this member to TRUE and procede.

void CRemoteDBScanView::OnInitialUpdate()
{
   CListViewEx::OnInitialUpdate();


   // TODO: You may populate your ListView with items by directly
   // accessing its list control through a call to GetListCtrl().
   CListViewEx::OnInitialUpdate();
   if( m_bColumnsExist )
   {
      return;
   }
   else
   {
      m_bColumnsExist = TRUE;
   }

Now, we add seven columns to the list control. To add columns, first we get a reference to the list control associated with the view.

// insert columns
CListCtrl& ListCtrl = GetListCtrl();

We define the attributes of individual columns by initializing an LV_COLUMN structure. Here's the typedef for LV_COLUMN:

typedef struct _LVCOLUMN {
   UINT mask;           //mask flags define which members are valid
   int fmt;             //flag for column heading alignment
   int cx;              //column width in pixels
   LPTSTR pszText;      //column heading
   int cchTextMax;      //length of heading buffer
   int iSubItem;        //this column's sub item index
   int iImage;          //image list index for column icon
   int iOrder;          //0 based column index
} LVCOLUMN;

The LV_ COLUMN structure is used both to set and retrieve information about a column. The mask member defines which members of the structure are to be considered valid, and has these possible values:

Table 1: LV_COLUMN Mask Flag Constants and Their Meanings

Mask Flag Constant Meaning
LVCF_FMT fmt is valid or should be returned
LVCF_IMAGE iImage is valid or should be returned
LVCF_ORDER iOrder is valid or should be returned
LVCF_SUBITEM iSubItem is valid or should be returned
LVCF_TEXT pPszText is valid or should be returned
LVCF_WIDTH cx is valid or should be returned

The mask flags may be combined with logical OR, so that you can set or retrieve as many or few LV_COLUMN members as you like.

The fmt member controls the column heading alignment and the placement of an optional image next to the heading. The heading of the leftmost column must be left aligned. You can use one of heading styles shown in the table below:

Table 2: LV_COLUMN Heading Style Flags and Their Meanings

Heading Format Flag Meaning
LVCFMT_CENTER Center heading text
LVCFMT_LEFT Left align heading text
LVCFMT_RIGHT Right align heading text

Now, we declare and initialize an LV_COLUMN structure. We set the mask for members fmt, cx, pszText an iSubItem members. We get the dimensions of the client area, and use this to set the column width. Looping through the string resources, we load the caption string for each column and also use the loop index to set the value of iSubItem. We set each column heading format to left alignment, and insert the column with a call to ListCtrl.InsertColumn().

int i;
LV_COLUMN lvc;
lvc.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;

CRect rectClient;
this->GetClientRect( &rectClient );
for(i = IDS_DATABASE_NAME; i<= IDS_DATABASE_CEOID; i++)
{
   CString str;
   str.LoadString(i);
   lvc.iSubItem = i - IDS_DATABASE_NAME;
   lvc.pszText = (LPSTR)(LPCTSTR)str;
   lvc.cx = rectClient.right / (IDS_DATABASE_CEOID -
                                IDS_DATABASE_NAME);
   lvc.fmt = LVCFMT_LEFT;
   ListCtrl.InsertColumn(i,&lvc);
}

Once we have columns in the list control, we are ready to add rows containing the remote database attributes. The first step is a familiar one—we initialize RAPI with a call to CeRapiInit().

HRESULT hr = CeRapiInit();
if( hr != ERROR_SUCCESS )
   {return ;}

If we are successful, we'll begin our search for remote databases.

Initializing The DesktopView With A List Of Remote Databases

Enumerating Remote Databases

Setting up to enumerate the databases is a two-step process. First, we call CeFindFirstDatabase(). The parameter we pass indicates what type of database we want to enumerate. The database type is an arbitrary constant that is set by the application that initially creates the database. It's not a required parameter for database creation, but it can be useful if an application creates and uses a family of databases. Setting their types equal makes it easy to find all of them again if you need to manipulate them as a group. Passing 0 as the parameter to CeFindFirstDatabase() causes all databases to be enumerated, regardless of type. On success, CeFindFirstDatabase() returns a handle to an enumeration context.

To actually find a database, we call CeFindNextDatabase(), passing the handle returned by CeFindFirstDatabase() as its only parameter. CeFindNextDatabase() returns the CE object identifier (CEOID) of the found database. An OID can uniquely identify many types of objects: files, directories, databases, or individual database records. We don't have a special function for getting information for each of these types of objects. Instead, we have a function that returns attribute information for any of the object types, CeOidGetInfo().

CeOidGetInfo() takes two parameters: the CEOID for the object being queried, and the address of a CEOIDINFO structure. Here is the typedef for CEOIDINFO:

typedef struct _CEOIDINFO {
        WORD wObjType;          //the type of object being queried
        DWORD dwSize;           //the size of this structure
        WORD wPad;              //used for structure alignment
        union {                           //object specific member
                CEFILEINFO infFile;       //of the union is returned
                CEDIRINFO infDirectory;   //populated with
                CEDBASEINFO infDatabase;  //attribute data
                CERECORDINFO infRecord;
              };
} CEOIDINFO;

The information in which we are interested is returned in the CEDBASEINFO member of the union. We'll get down to business populating the list after we take a look at this key structure:

typedef struct _CEDBASEINFO { 
        DWORD dwFlags;            //which structure members are
                                  //valid database name
        WCHAR szDbaseName[CEDB_MAXDBASENAMELEN];
        DWORD dwDbaseType;        //application defined database type
        WORD wNumRecords;         //number records in this database
        WORD wNumSortOrder;       //number of active sort orders
        DWORD dwSize;             //database size in bytes
        FILETIME ftLastModified;  //last modified time
                                  //an array of structures defining
                                  // sort order specs
        SORTORDERSPEC rgSortSpecs[CEDB_MAXSORTORDER];
        } CEDBASEINFO;

The information returned in the CEDBASEINFO structure gives you all of the information you need to open any database on the CE device, applying sort orders if they exist. You can use the dwFlags member to detect which other structure members are valid. The flags are found in the low order word of dwFlags, and can be isolated like this:

//get the aggregated flags
WORD wFlagWord = LOWORD( dwFlags );

//is the CEDB_VALIDDBFLAGS flag set
WORD wFlagTest;
WFlagTest = wFlagWord & CEDB_VALIDDBFLAGS;
if( wFlagWord )
{
   //do something with the flags
}

Here are the possible values of dwFlags:

Table 3: CEDBASEINFO Flags

dwFlags Value Meaning
CEDB_VALIDMODTIME ftLastModified member is valid and
CEDB_VALIDNAME szDbaseName member is valid.
CEDB_VALIDTYPE dwDbaseType member is valid.
CEDB_VALIDSORTSPEC rgSortSpecs member is valid.
CEDB_VALIDDBFLAGS The low-order word of the dwFlags member is valid.

The following code enumerates the remote databases and adds them to our list control on the desktop. This continues until the value returned in enumCeoid becomes FALSE, indicating that we've retrieved everything matching our criteria.

HANDLE hEnum;
CEOID enumCeoid;
CEOIDINFO oidInfo;
BOOL bOk;

//enum databases
hEnum = CeFindFirstDatabase(0);
while(  enumCeoid = CeFindNextDatabase( hEnum))
{
   CeOidGetInfo(enumCeoid, &oidInfo);

   AddRowToList( enumCeoid, &oidInfo );


}

Before we move on, notice that after we've exhausted the enumeration loop, we close OnInitialUpdate() by uninitializing RAPI.

    CeRapiUninit();

}

Looking Ahead

In the next installment, we'll see the mechanics of adding a row of database attributes to the desktop list control with a call to AddRowToList(). This is a somewhat more complicated job than you might suspect, because the list control treats its contents as a matrix of individual elements, rather than as a collection of rows.



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

  • Live Event Date: August 20, 2014 @ 1:00 p.m. ET / 10:00 a.m. PT When you look at natural user interfaces as a developer, it isn't just fun and games. There are some very serious, real-world usage models of how things can help make the world a better place – things like Intel® RealSense™ technology. Check out this upcoming eSeminar and join the panel of experts, both from inside and outside of Intel, as they discuss how natural user interfaces will likely be getting adopted in a wide variety …

  • Java developers know that testing code changes can be a huge pain, and waiting for an application to redeploy after a code fix can take an eternity. Wouldn't it be great if you could see your code changes immediately, fine-tune, debug, explore and deploy code without waiting for ages? In this white paper, find out how that's possible with a Java plugin that drastically changes the way you develop, test and run Java applications. Discover the advantages of this plugin, and the changes you can expect to see …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds