Click here to Skip to main content
15,886,840 members
Please Sign up or sign in to vote.
1.00/5 (1 vote)
See more:
Hello,

I am building an application in MFC,my application monitors the user in the windows,this application makes a log file if the user has CREATED OR DELETED OR RENAMED a folder in the system.So kindly can any one help me in doing this.

Can i have a sample code in MFC,i will be very much thank full if this thing is solved.
Posted

1 solution

You have to write some monitoring code. This was a big project for me about 6 months ago, I found that the best way to do it is with the ReadDirectoryChangesW() API. The problem I had was all of the articles/examples I found were either bloated and sloppy or not usable. My next article will be to publish my monitor code but if you need quick results start there (ReadDirectoryChangesW).

heres some example code, I tried to clean it up some for you, its purpose was to monitor xml files in a directory for changes, when a file was edited it would compare the files and report the differences. This example is only monitoring one directory now but I know its possible to monitor many:

DWORD WatchThread(CDataMonitor* pThis)
{
	ASSERT(pThis);

	.... 

	// Setup for Async I/O
	pThis->m_olWatch.hEvent = ::CreateEvent(NULL, TRUE, FALSE, NULL);
	HANDLE hEvents[2];
	hEvents[0] = pThis->m_hStopEvents[1];
	hEvents[1] = pThis->m_olWatch.hEvent;
	
	// Begin monitor loop
	DWORD dwBytesReturned = 0;	
	FILE_NOTIFY_INFORMATION buffer[1024] = {0};
	CString strRenameFileOld = _T(""); // Rename events consists of 2 messages
	while(TRUE)
	{
		// Call directory watch function async
		if(!::ReadDirectoryChangesW(pThis->m_hWatchDirectory, &buffer, sizeof(buffer), FALSE, 
			FILE_NOTIFY_CHANGE_CREATION|FILE_NOTIFY_CHANGE_ATTRIBUTES|FILE_NOTIFY_CHANGE_LAST_WRITE|
			//FILE_NOTIFY_CHANGE_DIR_NAME|FILE_NOTIFY_CHANGE_SIZE|FILE_NOTIFY_CHANGE_LAST_ACCESS|
			//FILE_NOTIFY_CHANGE_CREATION|FILE_NOTIFY_CHANGE_SECURITY|
			FILE_NOTIFY_CHANGE_FILE_NAME, &dwBytesReturned, &pThis->m_olXMLWatch, NULL))
		{
			pThis->OnWindowsError(_T("WatchThread"), _T("ReadDirectoryChangesW"), 
				_T("Starting XML directory monitor"));
			// Kill Self
			pThis->m_pMonitorThread = NULL;
			AfxEndThread(0, TRUE);
			ASSERT(0);
			return 0;
		}

		// Wait for change in directory or shutdown event
		DWORD dwWait = ::WaitForMultipleObjects(2, hEvents, FALSE, INFINITE);
		if(dwWait ==WAIT_OBJECT_0)
		{
			// Shutdown event set
			AfxEndThread(1, FALSE);
			return 1;
		}

		// Reset the event manually every time
		::ResetEvent(hEvents[1]);

		// Process notify buffer
		int i = 0;
		do
		{
			// Get unicode file name from structure 
			CString strFileName = CString(buffer[i].FileName).Left(buffer[i].FileNameLength / 2);
			
.......

				// Create both full file path names
				TCHAR szCompareFile[MAX_PATH] = {NULL};
				_tcscpy(szCompareFile, szDir);
				_tcscpy(szCompareFile + _tcslen(szCompareFile), FileName); 
				TCHAR szBackupFile[MAX_PATH] = {NULL};
				_tcscpy(szBackupFile, szBackupPath);
				_tcscpy(szBackupFile + _tcslen(szBackupFile), FileName); 

				#ifdef _DEBUG
				// Create status message
				CString strStatusText = _T("");
				CTime tm = CTime::GetCurrentTime();
				strStatusText = tm.Format("%H:%M:%S - ");
				#endif
				CString strStatusDesc = _T("");

				// See what changed
				switch(buffer[i].Action)
				{
					case FILE_ACTION_ADDED: 
					{
						strStatusDesc = _T("File added to directory"); 
						// Add backup file
						/*int iRetries = 0;
						BOOL bSuccess = FALSE;
						while( ((bSuccess = ::CopyFile(szCompareFile, szBackupFile, FALSE)) == FALSE) && 
							(::GetLastError() == ERROR_SHARING_VIOLATION) && 
							(iRetries++ < NUM_SHARE_RETRIES) 
						)
							Sleep(SHARE_RETRY_MS);
						if(!bSuccess)
						{
							pThis->OnWindowsError(_T("WatchThread"), _T("CopyFile"), 
								_T("Monitoring - Event: FILE_ACTION_ADDED"));
							ASSERT(0); // This should never happen
						}*/
						if(!::CopyFile(szCompareFile, szBackupFile, FALSE))
						{
							// Occasionally this fails with sharing error, the solution
							//   below is sloppy but always seems to work
							if(::GetLastError() == ERROR_SHARING_VIOLATION)
							{
								Sleep(100);
								if(!::CopyFile(szCompareFile, szBackupFile, FALSE))
								{
									pThis->OnWindowsError(_T("XMLWatchThread"), _T("CopyFile"), 
										_T("Monitoring - Event: FILE_ACTION_ADDED"));
									ASSERT(0); // This should never happen
								}
							}
						}
						// Notify base class
						pThis->m_csNotify.Lock();
						pThis->OnFileAdded(strFileName);
						pThis->m_csNotify.Unlock();
						break; 
					}
					case FILE_ACTION_REMOVED: 
					{
						strStatusDesc = _T("File removed from directory"); 
						// Delete backup file
						if(!::DeleteFile(szBackupFile))
						{
							pThis->OnWindowsError(_T("WatchThread"), _T("DeleteFile"), 
								_T("Monitoring - Event: FILE_ACTION_REMOVED"));
							//ASSERT(0); // This should never happen
						}
						// Notify base class
						pThis->m_csNotify.Lock();
						pThis->OnFileDeleted(FileName);
						pThis->m_csNotify.Unlock();
						break;
					}
					case FILE_ACTION_MODIFIED: 
					{
						// First check for presence of backup file, when a file is renamed we get a 
						//  FILE_ACTION_RENAMED_OLD_NAME and then a FILE_ACTION_MODIFIED instead of 
						//  FILE_ACTION_RENAMED_OLD_NAME and FILE_ACTION_RENAMED_NEW_NAME like we should
						if(!pThis->FileExists(szBackupFile))
						{
							// This is the second half of a rename event, in first half we deleted
							//  old so now we will copy new
							strStatusDesc = _T("File renamed - this is the new name"); 
							// Create full old name file/path
							CString strRenameFile = pThis->m_BackupPath;
							if(strRenameFile.Right(1) != _T("\\"))
								strRenameFile += _T("\\");
							//RenameFile += _T("XML\\");
							strRenameFile += strRenameFileOld;
							// Rename the file
							int iResult = _trename(strRenameFile, szBackupFile);
							if(iResult != 0)
							{
								CString strErr = _T("");
								switch(iResult)	{
									case 13/*EACCES*/: Err = _T("Access error, newname already exists or could not be created"); break; 
									case 2 /*ENOENT*/: Err = _T("File or path specified by oldname not found"); break; 
									case 22/*EINVAL*/: Err = _T("Name contains invalid TCHARacters"); break;
								}
								pThis->m_csNotify.Lock();
								pThis->m_strLastError.Format(
									_T("Function: WatchThread - _trename Failed - Error %d: %s"), iResult, Err);
								pThis->OnError(pThis->m_LastError);
								pThis->m_csNotify.Unlock();
							}
							// Notify base class
							pThis->m_csNotify.Lock();
							pThis->OnFileRenamed(strRenameFileOld, strFileName);
							pThis->m_csNotify.Unlock();
							break;
						}
						
						// Regular modified event
						strStatusDesc = _T("File modified - time stamp or attributes"); 
						// Compare last write file property 
						if(pThis->CompareFileProperties(szCompareFile, szBackupFile) > 0)
						{
							// Add this file to the work queue if it isnt already there
							pThis->m_csWorkList.Lock();					
							POSITION pos = pThis->m_cslWorkList.Find(FileName);
							if(pos == NULL)
								pThis->m_cslWorkList.AddTail(FileName);
							pThis->m_csWorkList.Unlock();
						}
						break; 
					}
					case FILE_ACTION_RENAMED_OLD_NAME: 
					{
						strStatusDesc = _T("File renamed - this is the old name"); 
						// Store old name, will rename backup file in next message
						strRenameFileOld = strFileName;
						break; 
					}
					case FILE_ACTION_RENAMED_NEW_NAME:
					{
						// This never happens (like it should), put bebug break here 
						//   just in case it magically starts working one day
						strStatusDesc = _T("File renamed - this is the new name"); 
						ASSERT(0);
						break;
					}
				}

				#ifdef _DEBUG
				// Send status update
				strStatusText += strFileName;
				strStatusText += _T(" - ");
				strStatusText += strStatusDesc;
				pThis->m_csNotify.Lock();
				pThis->OnStatusUpdate(strStatusText);
				pThis->m_csNotify.Unlock();
				#endif
			}
		}
		while(!buffer[++i].NextEntryOffset);
	}

	return 1;
}



I hope this helps, good luck
 
Share this answer
 
v2
Comments
Member 9274864 12-Sep-12 4:08am    
What is CDataMonitor,do i need to call this function in my code
JJMatthews 12-Sep-12 8:26am    
It was a thread class I wrote for monitoring. I was trying to give you an example of how to use the ReadDirectoryChangesW API (if thats the way you want to do it).

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900