GUI 2 Homework #2: File Find Utility


Your second homework assignment is to create a file-find utility, similar to the Explorer Find File functionality. Like Explorers File Find utility, it will search sub-folders for matching filenames but it will not have the extensive search ability nor the ability to execute the found files by double clicking them (though, you are free to add these features if you like).

The program will start its search for files in a separate thread than the main thread, which handles user interface. This means that as files are added to the list control, the user interface will continue to work, and there is no need for a timer or message pump.

Resources

This program does not need a menu. You will need to modify the default icons (the 32x32 and the 16x16) to reflect your program. You will also need to alter the About dialog, to contain your name.

Application Type

This program is to be a dialog-based application, with no document/view support. You can remove the OK and Cancel buttons from the project that AppWizard creates for you.

The CLookFor class

This class is going to assist you in keeping track of what you are looking for, where you are searching, and the current status of the search (i.e., Running, Stopped, or Cancelled). Your dialog class will need an instance of this class, and the search function will need one as a local variable. The class should have the following members:

Data Members

 Variable  Purpose
 static CListCtrl* m_pList;  Pointer to the CListCtrl object, for adding names when found
 CString m_In;  Pathname, or where to look in.
 static CString m_Named;  What to search for
 static CWnd* m_pFindButton;  Pointer to the Find button, so it may be enabled and disabled
 static CWnd* m_pStopButton;  Pointer to the Stop or Cancel button, so it may be enabled and disabled
 static volatile bool m_bCancel;  Flag, indicating if cancel was requested or not.
 static volatile bool m_bRunning;  Flag, indicating if child thread is still running.

Functions

void SetData( const CString& Named, const CString& Path, CListCtrl* pFoundList,
CWnd* pFindButton, CWnd* pStartButton );
Initializes all data members, except the m_bCancel and m_bRunning variables. This function must be called before any other function is called (with a few minor, technical, exceptions).

void SetPath( const CString& Path );
Sets the pathname (m_In) variable from Path.

static void Start();
Disables the Find button, and enables the Stop/Cancel button, then sets the Running flag to true, and the cancelled flag to false.

static void Stop();
Enables the Find button, and disables the Stop/Cancel button, then sets the Running and Cancel flags to false.

static void Cancel();
Sets the Cancel flag to true.

static bool IsCanceled();
Returns status of the canceled flag.

static bool IsRunning();
Returns the status of the running flag.

const CString& GetPath();
Returns the current path to search ( m_In).

const CString& GetNamed();
Returns the current name to search for (m_Named).

CLookFor();
Does nothing.

virtual ~CLookFor();
Does Nothing.

File Searching

In order to accomplish file searching, you will need to use the CFileFind class. This class enables you to get a list of names from a folder, and also identify if a found name was a sub-folder or not. The file searching function will need to be recursive, meaning that when a sub-folder is found in a folder, the function calls itself to search that sub-folder.

The file-searching routine is the function that should be passed to the AfxBeginThread function to start the child thread. As its parameter, it should receive a CLookFor* which was typecast to an LPVOID. The file searching function will need to be a stand-alone function, and not a member of a class.

The search function has two main loops: first it searchs for files matching your request in a given folder. Then, it searches for sub-folders in the same folder. For each found sub-folder, the function will create a new local CLookFor object, initialize it, and call itself with the that local CLookFor (this is the recursive part).

The first loop example, would look like the following:

UINT DoFind( LPVOID pParam )
{
	static int Depth;
	int Index;
	CTime Time;
	CString GenStr;
	BOOL MoreFiles;
	Depth++;
	CLookFor* pLookFor = (CLookFor*)pParam;
	CListCtrl* pList = pLookFor->m_pList;
	CFileFind Find;
	// Step 1: Find matching files in Folder
	CString Path;
	Path = pLookFor->GetPath();
	if( Path.IsEmpty() )
	{
		char * pPath = Path.GetBuffer( _MAX_PATH );
		GetCurrentDirectory( _MAX_PATH, pPath );
		Path.ReleaseBuffer();
	}
	if( Path[ Path.GetLength()-1 ] != ':' && Path[ Path.GetLength()-1 ] != '\\' )
		Path += "\\";
	Path += pLookFor->GetNamed();
	if( Find.FindFile( Path ) )
	{
		do 
		{
			MoreFiles = Find.FindNextFile();
			GenStr = Find.GetFileName();
			if( GenStr.CompareNoCase(".")==0 || GenStr.CompareNoCase("..") ==0 )
				continue;
			Index = pList->InsertItem( pList->GetItemCount(), Find.GetFileName() );
			pList->SetItemText( Index, 1, pLookFor->GetPath() );
			if( Find.IsDirectory() )
				GenStr="<DIR>";
			else
				GenStr.Format( "%d", Find.GetLength() );
			pList->SetItemText( Index, 2, GenStr );
			Find.GetLastWriteTime( Time );
			pList->SetItemText( Index, 3, Time.Format( "%I:%M %p") );
		} while( MoreFiles && !pLookFor->IsCanceled() );
	}
	// Step 2: Look for Sub-directories
	// NOTE: You must write the second loop!

	Depth--;
	if( Depth == 0 ) // Last call
		pLookFor->Stop();
	return( 1 );
}

TIP: When searching for folders, use the wildcard *.*, as pLookFor->GetNamed() was used above.

Dialog Class

Your dialog class will be your main window, and will handle all the user interface. You will need to add the following members:

Data Members

 Variable  Description
 CListCtrl m_FoundList;  The List Control, mapped to the list control (added via ClassWizard)
 CString m_LookIn;  The path to look in, mapped to the edit box(added via ClassWizard)
 CString m_Named;  The file to look for, mapped to the edit box.(added via ClassWizard)
 CLookFor m_Start;  The starting CLookFor, where to begin the search

Functions

virtual BOOL OnInitDialog();
Added by ClassWizard, this is where you will initialize the column names and widths for the list control, as well as thedefault path for the 'Look In' edit box.

afx_msg void OnFind();
Added via ClassWizard, this is the handler for the user clicking the Find button. This function must clear out the data in the list control (m_FoundList), initialize the m_Start variable with data from the m_LookIn and m_Named variables, as well as the CWnd pointers for the controls (i.e., the Find button, using GetDlgItem).

afx_msg void OnStop();
Added via ClassWizard, this handler will simply call the Stop function of the m_Start member variables, which will cause the thread to terminate.

afx_msg void OnSize(UINT nType, int cx, int cy);
Added via ClassWizard, this handler will be called when the user sizes the window. You should size (not move) your Found List list control to shrink and grow as needed to accommodate the new size.

afx_msg void OnClose();
Added via ClassWizard to handle the WM_CLOSE message. This function should check if the m_Start IsRunning function is true. If so, it should call m_Start.Cancel(), and then wait for the child thread to terminate (by checking m_Start.IsRunning(), in a loop).