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.
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.
# # #