Click here to Skip to main content
15,887,596 members
Articles / Desktop Programming / MFC
Article

Spying a file system

Rate me:
Please Sign up or sign in to vote.
4.26/5 (49 votes)
22 Sep 2003CPOL3 min read 405.3K   7.2K   124   112
Article describes how to create a file system spy application

Image 1

Introduction

Windows applications can do dynamic monitoring of any specified directory. Once changes have occurred and are detected, the spy application can run various tasks ( run antivirus, log activity, determine more information about changes, call other tasks etc).

Win 32 API provides three functions that are based on the events:

  • FindFirstChangeNotification
  • FindNextChangeNotification
  • FindCloseChangeNotification
  • ReadDirectoryChangesW

These allow creating watchdog or spying applications.

How to create

First of all spy application should call

FindFirstChangeNotification 
to create event handler to monitor changes specified as the functions parameters.

HANDLE h = FindFirtsChangeNotification("C:\\Program Files", TRUE, mask); 

This function allows to handle following types of notifications:

  • FILE_NOTIFY_CHANGE_FILE_NAME – File creating, deleting and file name changing

  • FILE_NOTIFY_CHANGE_DIR_NAME – Directories creating, deleting and file name changing

  • FILE_NOTIFY_CHANGE_ATTRIBUTES – File or Directory attributes changing

  • FILE_NOTIFY_CHANGE_SIZE – File size changing

  • FILE_NOTIFY_CHANGE_LAST_WRITE – Changing time of write of the files

  • FILE_NOTIFY_CHANGE_SECURITY – Changing in security descriptors

The result of FindFirstChangeNotification can be passed as parameter in to WaitForSingleObject and when specified event has occurred, application can do various actions such as: antivirus starting, adding record to the log file, and so on. Note that this function does not detect changes, it only creates synchronization event and marks it if changes are made. After our spy application handles changes, it should call FindNextChangeNotification to continue monitoring or FindCloseChangeNotification to finish it.

Win32 API provides also ReadDirectoryChangesW that can operate with following filters (MSDN) :

FILE_NOTIFY_CHANGE_FILE_NAME

Any file name change in the watched directory or subtree causes a change notification wait operation to return. Changes include renaming, creating, or deleting a file.

FILE_NOTIFY_CHANGE_DIR_NAME

Any directory-name change in the watched directory or subtree causes a change notification wait operation to return. Changes include creating or deleting a directory.

FILE_NOTIFY_CHANGE_ATTRIBUTES

Any attribute change in the watched directory or subtree causes a change notification wait operation to return.

FILE_NOTIFY_CHANGE_SIZE

Any file-size change in the watched directory or subtree causes a change notification wait operation to return. The operating system detects a change in file size only when the file is written to the disk. For operating systems that use extensive caching, detection occurs only when the cache is sufficiently flushed.

FILE_NOTIFY_CHANGE_LAST_WRITE

Any change to the last write-time of files in the watched directory or subtree causes a change notification wait operation to return. The operating system detects a change to the last write-time only when the file is written to the disk. For operating systems that use extensive caching, detection occurs only when the cache is sufficiently flushed.

FILE_NOTIFY_CHANGE_LAST_ACCESS

Any change to the last access time of files in the watched directory or subtree causes a change notification wait operation to return.

FILE_NOTIFY_CHANGE_CREATION

Any change to the creation time of files in the watched directory or subtree causes a change notification wait operation to return.

FILE_NOTIFY_CHANGE_SECURITY

Any security-descriptor change in the watched directory or subtree causes a change notification wait operation to return.

Sample

Give your attention to the following code in the demo project:

void ThreadRoute( void* arg )
{
 HANDLE file = FindFirstChangeNotification("c:\\Program Files", 
     FALSE, (DWORD)((Param*)arg)->parameter);
 WaitForSingleObject(file, INFINITE);
 CTime tm = CTime::GetCurrentTime();
 m_Sec.Lock(); // Enter to Critical section for display notification
 int item = pList->InsertItem(pList->GetItemCount(), ((Param*)arg)->message);
 pList->SetItemText(item, 1, tm.Format("%Y/%m/%d - %H:%M:%S"));
 m_Sec.Unlock();

 while (true)
 {
   FindNextChangeNotification(file);
   WaitForSingleObject(file, INFINITE);
   tm = CTime::GetCurrentTime();
   m_Sec.Lock(); // Enter to Critical section for display notification
   item = pList->InsertItem(pList->GetItemCount(), ((Param*)arg)->message);
   pList->SetItemText(item, 1, tm.Format("%Y/%m/%d/ - %H:%M:%S"));
   m_Sec.Unlock();
  }
}

and here is the fragment using ReadDirectoryChangesW

void ThreadRoute1( void* arg ) 
 {
  USES_CONVERSION;
  HANDLE hDir = CreateFile( 
    CString("c:\\Program Files"), /* pointer to the file name */
    FILE_LIST_DIRECTORY,                /* access (read-write) mode */
    FILE_SHARE_READ|FILE_SHARE_DELETE,  /* share mode */
    NULL, /* security descriptor */
    OPEN_EXISTING, /* how to create */
    FILE_FLAG_BACKUP_SEMANTICS, /* file attributes */
    NULL /* file with attributes to copy */
  );

  FILE_NOTIFY_INFORMATION Buffer[1024];
  DWORD BytesReturned;
  while( ReadDirectoryChangesW(
     hDir, /* handle to directory */
     &Buffer, /* read results buffer */
     sizeof(Buffer), /* length of buffer */
     TRUE, /* monitoring option */
     FILE_NOTIFY_CHANGE_SECURITY|
     FILE_NOTIFY_CHANGE_CREATION|
     FILE_NOTIFY_CHANGE_LAST_ACCESS|
     FILE_NOTIFY_CHANGE_LAST_WRITE|
     FILE_NOTIFY_CHANGE_SIZE|
     FILE_NOTIFY_CHANGE_ATTRIBUTES|
     FILE_NOTIFY_CHANGE_DIR_NAME|
     FILE_NOTIFY_CHANGE_FILE_NAME, /* filter conditions */
     &BytesReturned, /* bytes returned */
     NULL, /* overlapped buffer */
     NULL))... /* completion routine */

These are thread functions that do the described spying actions.

Conclusion

The attached Demo application starts separate threads to monitor all possible changes in the "c:\\Program Files" directory and shows occurred notifications and its date/time in the List control. Demo application shows also how to use ReadDirectoryChangesW and compare both methods visually.

Functionality of the Demo application can be extended to determine concrete changes, to log changes in to file, run external applications or tasks on the specified event, use described methods as system service and so on. Readers have full freedom to modify and use the demo project.

License

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


Written By
Software Developer (Senior)
Europe Europe
I am a professional Software Developer with more than 20 years' experience.

I am open for contacts and interesting ideas.
You can write me at:
vitali_eh@yahoo.com

Comments and Discussions

 
GeneralRe: can you tell who created/deleted the file(s)? Pin
korsuas11-Feb-05 0:34
korsuas11-Feb-05 0:34 
GeneralRe: can you tell who created/deleted the file(s)? Pin
need$22-May-05 22:50
need$22-May-05 22:50 
GeneralRe: can you tell who created/deleted the file(s)? Pin
korsuas14-Mar-06 22:09
korsuas14-Mar-06 22:09 
Generalspy while reading and writing Pin
rancher853-Sep-04 15:45
rancher853-Sep-04 15:45 
GeneralRe: spy while reading and writing Pin
Anonymous10-Feb-05 11:57
Anonymous10-Feb-05 11:57 
GeneralNo way to exit ReadDirectoryChangesW Pin
TwoBit18-Jun-04 6:15
TwoBit18-Jun-04 6:15 
GeneralRe: No way to exit ReadDirectoryChangesW [modified] Pin
Vitali Halershtein22-Jun-04 0:30
Vitali Halershtein22-Jun-04 0:30 
GeneralRe: No way to exit ReadDirectoryChangesW Pin
Nathan Lewis24-Jul-04 20:01
Nathan Lewis24-Jul-04 20:01 
Vitali Halershtein wrote:
You can use BOOL TerminateThread to terminate thread with ReadDirectoryChangesW call.

TerminateThread is not a good way to handle this! From the MSDN documentation:

TerminateThread is used to cause a thread to exit. When this occurs, the target thread has no chance to execute any user-mode code and its initial stack is not deallocated. DLLs attached to the thread are not notified that the thread is terminating. <br />
<br />
TerminateThread is a dangerous function that should only be used in the most extreme cases. You should call TerminateThread only if you know exactly what the target thread is doing, and you control all of the code that the target thread could possibly be running at the time of the termination. For example, TerminateThread can result in the following problems: <br />
<br />
   - If the target thread owns a critical section, the critical section will not be released.<br />
   - If the target thread is executing certain kernel32 calls when it is terminated, the kernel32 state for the thread's process could be inconsistent.<br />
   - If the target thread is manipulating the global state of a shared DLL, the state of the DLL could be destroyed, affecting other users of the DLL.<br />


Vitali Halershtein wrote:
You can also use ExitThread (inside the thread)

Not if the thread is blocked by the synchronous call... Smile | :)

Vitali Halershtein wrote:
And of course CloseHandle

CloseHandle has almost nothing to do with terminating a thread. I say "almost" because, quoting from MSDN: "The thread object remains in the system until the thread has terminated and all handles to it have been closed through a call to CloseHandle."

Let's look at an example:

   void func()<br />
   {<br />
      DWORD dwId = 0;<br />
      HANDLE hThread = CreateThread( NULL, 0, ThreadProc, NULL, 0, &dwID );<br />
<br />
      if( hThread )<br />
         CloseHandle( hThread );<br />
   }<br />


A common misconception is the belief that the CloseHandle call above will cause the thread to exit immediately. And I have seen many programs that never close the handle at all, resulting in a resource leak every time a new thread is created - since the resources allocated for the thread are never released until the application exits.

The reality is that by calling CloseHandle, we are allowing the resources for the thread to be freed when the thread procedure exits normally. For every successful call to CreateThread, there should be a corresponding call to CloseHandle.

So, to answer the original question: If you don't like the fact that the synchronous call to ReadDirectoryChangesW cannot be cancelled, use the asynchronous call instead. In fact, that sounds like what your application needs to do anyway. I haven't looked at it myself, but I did find a sample on MSDN that may be helpful:

   Fwatch Sample: Using ReadDirectoryChangesW API
   http://msdn.microsoft.com/library/en-us/vcsample98/html/vcsmpfwatchsamplereaddirectorychangeswapi.asp

Note: This link is valid as of 24-Jul-2004, but Microsoft tends to move things around from time to time. If the link doesn't resolve for you, try searching for "ReadDirectoryChangesW" on MSDN.
GeneralRe: No way to exit ReadDirectoryChangesW Pin
boazr22-Dec-04 1:33
boazr22-Dec-04 1:33 
GeneralRe: No way to exit ReadDirectoryChangesW Pin
Robert M Greene27-Oct-05 9:34
Robert M Greene27-Oct-05 9:34 
GeneralFILE_ACTION_RENAMED_NEW_NAME never actions Pin
Rao Kasibhotla14-May-04 2:14
Rao Kasibhotla14-May-04 2:14 
GeneralRe: FILE_ACTION_RENAMED_NEW_NAME never actions Pin
LogicSoftware4-Jun-04 4:19
LogicSoftware4-Jun-04 4:19 
GeneralRe: FILE_ACTION_RENAMED_NEW_NAME never actions Pin
Rao Kasibhotla5-Jun-04 1:48
Rao Kasibhotla5-Jun-04 1:48 
GeneralRe: FILE_ACTION_RENAMED_NEW_NAME never actions Pin
kijanka4-Nov-04 1:37
kijanka4-Nov-04 1:37 
GeneralRe: FILE_ACTION_RENAMED_NEW_NAME never actions Pin
Dalroi2625-Nov-04 10:30
Dalroi2625-Nov-04 10:30 
GeneralRe: FILE_ACTION_RENAMED_NEW_NAME never actions Pin
mousse_225-Jul-05 22:46
mousse_225-Jul-05 22:46 
GeneralMonitoring Multiple Directories. Pin
vikramlinux22-Apr-04 18:31
vikramlinux22-Apr-04 18:31 
GeneralRe: Monitoring Multiple Directories. [modified] Pin
Vitali Halershtein26-Apr-04 23:42
Vitali Halershtein26-Apr-04 23:42 
GeneralRe: Monitoring Multiple Directories. Pin
Nathan Lewis24-Jul-04 17:26
Nathan Lewis24-Jul-04 17:26 
GeneralRe: Monitoring Multiple Directories. Pin
boazr22-Dec-04 2:05
boazr22-Dec-04 2:05 
Generaloutput Pin
Anonymous28-Feb-04 20:10
Anonymous28-Feb-04 20:10 
GeneralRe: output [modified] Pin
Vitali Halershtein3-Mar-04 9:50
Vitali Halershtein3-Mar-04 9:50 
Questionwill it be possible to extract file lcoation? Pin
sysmatrix26-Nov-03 8:15
sysmatrix26-Nov-03 8:15 
AnswerRe: will it be possible to extract file lcoation? [modified] Pin
Vitali Halershtein2-Dec-03 23:46
Vitali Halershtein2-Dec-03 23:46 
Generalshobaradhakrishnan [shobaradha@rediffmail.com]File copy Pin
shobaradhakrishnan13-Nov-03 0:33
shobaradhakrishnan13-Nov-03 0:33 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.