Using RAPI to Find Remote Files

In this lesson, you’ll learn the uses of a RAPI powertool, the function CeFindAllFiles. This function has no CE side analog. To create powerful, panoramic CE file handling capabilities, our best bet is to work from the desktop side, using RAPI.

Initializing The Remote File Access Page

For this lesson, we return to the RAPI Demo example program. The code for this program is available on the Developer.com Web site. Let’s take a look at the Remote File Access page of the application’s property sheet control.

The Remote File Access page is slightly more complicated than the System Status page (the substance of our last lesson), owing to the fact that it has more controls and is somewhat more interactive.

Figure 1: The Remote File Access Page of the RAPI Demo Property Sheet Control

We’ll begin by looking at the modifications made to the header file for the class, RemoteFileAccessPage.h. Using the resource editor and ClassWizard, a member variable was created for each of the dialog’s controls. The variables are of the same type as the controls they encapsulate.

// Dialog Data
   //{{AFX_DATA(CRemoteFileAccessPage)
   enum { IDD = IDD_FILE_ACCESS_DIALOG };
   CButton     m_GetCEOID;
   CButton     m_GetLastWriteTime;
   CButton     m_GetFoldersOnly;
   CButton     m_GetCreationTime;
   CButton     m_UpdateFilesList;
   CListBox    m_SearchPath;
   CListBox    m_FilesList;
   CEdit       m_FileInput;
   //}}AFX_DATA

Here’s how we implement the behavior of the Remote File Access page. First, we have to initialize the control variables, and after that we have to respond with the appropriate information when the user presses the “Update File List ” button. The initialization step is handled in OnInitDialog().

We insert two search path strings in the m_SearchPath. list control Notice the double backslash characters in the path strings. In C and C++, a single backslash character is interpreted as the first character of an “escape sequence.” An example of an escape sequence is the tab character, represented as “\t”. By convention, “\\” is interpreted as a single backslash character.

We initialize the list control selection to the first visible string (0th ordinal), and we clear all of the checkbox controls. The check box controls correspond to items of file information that we can optionally retrieve.

BOOL CRemoteFileAccessPage::OnInitDialog()
{
   CPropertyPage::OnInitDialog();


   //Insert Search List Items
   m_SearchPath.InsertString( -1, "\\*.*" );
   m_SearchPath.InsertString( -1, "\\Windows\\*.*" );

   //Set search list selection
   m_SearchPath.SetCurSel( 0 );

   //Clear File Attribute Flag members
   m_GetLastWriteTime.SetCheck(0);
   m_GetCreationTime.SetCheck(0);
   m_GetFoldersOnly.SetCheck(0);
   m_GetCEOID.SetCheck(0);

   return TRUE;  // return TRUE unless you set the focus to a control
                 // EXCEPTION: OCX Property Pages should return FALSE
}

The actual work of the Remote File Access page isn’t done until the user presses the “Update File List” button. In the OnUpdateFileButton() member, first we clear the list control with a call to ResetContent(), and then we initialize the RAPI subsystem with CeRapiInit().

////////////////////////////////////////////////////////////////////
// CRemoteFileAccessPage message handlers

void CRemoteFileAccessPage::OnUpdateFileButton()
{

   WCHAR       wszFileName[MAX_PATH];

   //clear current list
   m_FilesList.ResetContent();

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

Next, we create a Unicode search path string, based on the selection in the list control, m_SearchPath. Notice that we use the L macro to prefix the literal string that we want to render in Unicode. In earlier examples, we used the TEXT macro for this purpose. The TEXT macro doesn’t work in MFC applications unless they are specifically enabled with Unicode preprocessor definitions.

Programming Tip: Use the L macro to create Unicode literal strings in MFC applications. Here’s an example of the use of the L macro:

L"This is a Unicode literal string"
   LPCE_FIND_DATA pFindDataArray = NULL;
   DWORD dwFoundCount = 0;

   int iSel = m_SearchPath.GetCurSel();
   if( iSel == 0 )
   {
      wcscpy(&wszFileName[0], L"\\*.*" );
   }
   else
   {
      wcscpy(&wszFileName[0], L"\\Windows\\*.*" );
   }

Our tool for retrieving information about files on the CE device is the powerhouse function, CeFindAllFiles(). CeFindAllFiles() has no analog on the CE side. It is specifically designed for RAPI applications, and retrieves all the files and folders under a particular node, in a single call. This is advantageous because it is much more efficient for the CE side than enumerating files and folders individually, and for this reason saves both processing time and battery power.

The arguments to the function are: a pointer to a Unicode string specifying a DOS style search path, a DWORD containg flags that determine what file attributes to return, a count of the returned items, and the address of a pointer of type LPCE_FIND_DATA. On successful return, the last parameter contains the address of an array of CE_FIND_DATA structures. Here is the typedef for CE_FIND_DATA:

typedef struct _CE_FIND_DATA {
DWORD dwFileAttributes;
FILETIME ftCreationTime;
FILETIME ftLastAccessTime;
FILETIME ftLastWriteTime;
DWORD nFileSizeHigh;        //0, unless the overall file size is >
                            //MAXDWORD
DWORD nFileSizeLow;         //file size in bytes
DWORD dwOID;                //CE Object Identifier for this file
WCHAR cFileName[MAX_PATH];  //Unicode file name
} CE_FIND_DATA;

Based on the state of the checkbox controls, we add flags to dwFileAttributeFlags. At a minimum, we’ll retrieve the filename, and optionally the creation time, last write time, and CE Object ID for the file. If the GetFoldersOnly box is checked, we retrieve information for all the folders in the search path and skip over the files. Here’s a complete list of the CE_FIND_DATA () file attribute flags, along with their meanings.

Table 1: CE_FIND_DATA File Attribute Flags And Their Meanings

File Attribute Flag Name Meaning
FAF_ATTRIB_CHILDREN Only retrieve information for directories with child items
FAF_ATTRIB_NO_HIDDEN Omit files or directories that have the hidden attribute set
FAF_FOLDERS_ONLY Retrieve information for directories only
FAF_NO_HIDDEN_SYS_ROMMODULES Omit ROM files or directories
FAF_ATTRIBUTES Return file attributes in dwFileAttributes
FAF_CREATION_TIME Return file creation time in ftCreationTime
FAF_LASTACCESS_TIME Return last access time in ftLastAccessTime
FAF_LASTWRITE_TIME Return last write time in ftLastWriteTime
FAF_SIZE_HIGH Return the high-order DWORD value of the file size in nFileSizeHigh
FAF_SIZE_LOW Return the low-order DWORD value of the file size in nFileSizeLow
FAF_OID Return the CE object identifier of the file in dwOID
FAF_NAME Return the Unicode file name in cFileName
DWORD dwFileAttributeFlags = FAF_NAME;

if( m_GetCreationTime.GetCheck() )
{ dwFileAttributeFlags |= FAF_CREATION_TIME ; }

if( m_GetLastWriteTime.GetCheck() )
{ dwFileAttributeFlags |= FAF_LASTWRITE_TIME ; }

if( m_GetFoldersOnly.GetCheck() )
{ dwFileAttributeFlags |= FAF_FOLDERS_ONLY; }

if( m_GetCEOID.GetCheck() )
{ dwFileAttributeFlags |= FAF_OID; }

BOOL bOk = CeFindAllFiles( (LPCWSTR)&wszFileName[0],
                          dwFileAttributeFlags, &dwFoundCount,
                          &pFindDataArray );

    if(!bOk )
        { return; }

On successful return for CeFindAllFiles, we loop through the array of returned structures, transferring the retrieved information to the list control, m_FilesList. Notice that we use the count of found items, dwFoundCount, returned by CeFindAllFiles to set the loop limit. Because the returned file name is a Unicode string, we must translate it to multi byte character format by using wcstombs() before attempting to insert it into the list control.

char szLineBuff[MAX_PATH];

for( int i = 0; i < dwFoundCount; i++ )
{

   wcstombs( szLineBuff, pFindDataArray[i].cFileName,
             sizeof(szLineBuff));
   m_FilesList.InsertString( -1, szLineBuff );

   if( m_GetCreationTime.GetCheck() )
   {
      CTime FileCreationTime(pFindDataArray[i].ftCreationTime, -1 );
      CString csBuff = FileCreationTime.Format("%A, %B %d, %Y" );
      sprintf( szLineBuff, "  %s: %s", "Creation Time",
              csBuff.GetBuffer(csBuff.GetLength())  );
      m_FilesList.InsertString( -1, szLineBuff );

   }

   if( m_GetLastWriteTime.GetCheck() )
   {
      CTime LastWriteTime(pFindDataArray[i].ftCreationTime, -1 );
      CString csBuff = LastWriteTime.Format("%A, %B %d, %Y" );
      sprintf( szLineBuff, "  %s: %s", "Last Write",
               csBuff.GetBuffer(csBuff.GetLength())  );
      m_FilesList.InsertString( -1, szLineBuff );

   }

   if( m_GetCEOID.GetCheck() )
   {
      sprintf( szLineBuff, "  %s: %s%x", "CEOID", "0x",
               pFindDataArray[i].dwOID );
      m_FilesList.InsertString( -1, szLineBuff );
   }

//    m_FilesList.InsertString( -1, " " );
}//end for()

Notice that our call to CeFindAllFiles() returned a pointer to a dynamically allocated array of CE_FIND_DATA structures. We must free this buffer that was allocated in our behalf before we uninitialize the RAPI subsystem, or memory will be “leaked.”

//free the CE_FIND_DATA structure array buffer
hr =  CeRapiFreeBuffer((LPVOID) pFindDataArray);

//now uninitialize RAPI.
CeRapiUninit();

}

Looking Ahead

In our next lesson, we’ll learn how to remotely walk the CE side Registry tree, a very useful technique for remotely managing CE side configurations.

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read