Spying a File System

WEBINAR: On-demand webcast

How to Boost Database Development Productivity on Linux, Docker, and Kubernetes with Microsoft SQL Server 2017 REGISTER >



Click here for a larger image.

Describes how to create the basis for a file system spy application

Introduction

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

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

  • FindFirstChangeNotification
  • FindNextChangeNotification
  • FindCloseChangeNotification
  • ReadDirectoryChangesW

This allows you to create a watchdog or spying application.

How to Create the Application

First of all, a spy application should call FindFirtsChangeNotification to create an event handler to monitor changes specified as the function's parameters.

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

This function allows us to handle the following types of notifications:

Notification Description
FILE_NOTIFY_CHANGE_FILE_NAME File creating and deleting, and file name changing
FILE_NOTIFY_CHANGE_DIR_NAME Directory creating and 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 FindFirtsChangeNotification can be passed as a parameter in to WaitForSingleObject. When the specified event occurs, the application can do various actions, such as antivirus starting, adding a record to the log file, and so on. Note that this function does not detect changes; it only creates a synchronization event and marks it if changes are made. After our spy application handling changes, it should call FindNextChangeNotification to continue monitoring, or FindCloseChangeNotification to finish it.

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

Filter Description
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

Please pay attention to the following code in the demo project:

void ThreadRoute( void* arg )
  {
  HANDLE file;
  file = FindFirstChangeNotification("c:\\Program Files",
                                     FALSE, (DWORD)
                                     ((Param*)arg)->parameter);
  WaitForSingleObject(file, INFINITE);

  CTime tm = CTime::GetCurrentTime();
  m_Sec.Lock();
  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();
    int item = pList->InsertItem(pList->GetItemCount(),
               ((Param*)arg)->message);
    pList->SetItemText(item, 1, tm.Format("%Y/%m/%d/ - %H:%M:%S"));
    m_Sec.Unlock();
    }
  }

and the fragment that uses 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 demo application starts separate threads to monitor all possible changes in the C:\\Program Files directory and shows occurred notifications and their date/time in the List control. The demo applicaton also shows 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 spaciousness for demo usage.

Downloads

Download demo project - 30 Kb


About the Author

Vitali Halershtein

I'm a professional Software Developer with more than 10 years experience. Certifications: MCSD, MCP, Brainbench, Masters Degree in Computer Science. I am open for contacts and interesting ideas. You can write me at: vitali_eh@yahoo.com

Comments

  • PocketPC/WindowsMobile

    Posted by badzio on 10/23/2008 03:44am

    Is similar solution for PocketPC/WindowsMobile (but not based on CF .Net)?

    Reply
  • I'm Can't change file name

    Posted by baby999 on 04/23/2008 06:10am

    I'm Can't change file name 
    
    void ThreadRoute1( void* arg )	
    	{
      USES_CONVERSION;
      TCHAR szDir[MAX_PATH];
      GetCurrentDirectory(sizeof(szDir)/sizeof(TCHAR),szDir); 
    
      HANDLE hDir = CreateFile( CString(szDir), // 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
      );
    Should change to 
    
    void ThreadRoute1( void* arg )	
    	{
      USES_CONVERSION;
      TCHAR szDir[MAX_PATH];
      GetCurrentDirectory(sizeof(szDir)/sizeof(TCHAR),szDir); 
    
      HANDLE hDir = CreateFile( CString(szDir), // pointer to the file name
        FILE_LIST_DIRECTORY,                // access (read/write) mode
        FILE_SHARE_WRITE|FILE_SHARE_READ|FILE_SHARE_DELETE,  // share mode //add  by xiaobb 
        NULL,                               // security descriptor
        OPEN_EXISTING,                      // how to create
        FILE_FLAG_BACKUP_SEMANTICS,         // file attributes
        NULL                                // file with attributes to copy
      );

    Reply
  • Folder locking & Password protection code

    Posted by c0mgu1 on 08/09/2005 06:59am

    I need a sample code for setting a password for accessing a folder & it contined subfolders/files plz help me

    Reply
  • i need notification message before event occure

    Posted by c0mgu1 on 08/09/2005 06:57am

    Here we are getting notification message after the event happence like after file deletion, but how to get an event before deletion...

    Reply
  • how can i get the last filename after rename?

    Posted by yqzq on 07/15/2005 05:54am

    how can i get the last filename after rename? when i rename the folder,like "AAA" to "BBB" but i can only get the filename from Buffer[i] is "AAA" why?

    Reply
  • how can i get the last filename after rename?

    Posted by yqzq on 07/15/2005 05:47am

    how can i get the last filename after rename? when i rename the folder,like "AAA" to "BBB" but i can only get the filename from Buffer[i] is "AAA" why?

    Reply
  • Beware if you use snippets from this sample code.

    Posted by HSmith on 05/07/2005 08:43am

    The following segment of code is invalid. I used this snippet of code in my application and it took quite some time to track down the defect. I have found other samples on the net that use they same method, they are also incorrect.
    
        do
          {
          m_Sec.Lock();
          int item = pList1->InsertItem(pList1->GetItemCount(), CString(Buffer[i].FileName).Left(Buffer[i].FileNameLength / 2) + " - " + helper_txt );
    		  pList1->SetItemText(item, 1, tm.Format("%Y/%m/%d/ - %H:%M:%S"));
          i++;
          m_Sec.Unlock();
          }
        while (!Buffer[i].NextEntryOffset);
    
    In the loop, Buffer[i] is accessed. This is only valid for the first record returned (and yes, multiple records are returned sometimes). 
    
    The list of file notification records cannot be accessed as an array, you should use a pointer instead. When advancing to the next record, you need to add pBuffer->NextEntryOffset to the pointer to access the next record.
    
    See the definition of:
    
    typedef struct _FILE_NOTIFY_INFORMATION {
       DWORD NextEntryOffset;
       DWORD Action;
       DWORD FileNameLength;
       WCHAR FileName[1];
    } FILE_NOTIFY_INFORMATION;
    
    Also note that the string returned is not null terminated. The file length is returned in bytes, not character counts. You should perform a wide character string copy as follows:
    
    wchar_t WideStringFileName[MAX_STRING_LENGTH * sizeof(wchar_t)];
    
    // The filenames returned are not null terminated. Copy the characters and null terminate the string.
    
    if (pBuffer->FileNameLength < sizeof(WideStringFileName)) {
      // Copy the filename.
      wcsncpy(WideStringFileName,pBuffer->FileName,pBuffer->FileNameLength/sizeof(wchar_t));
      // Null Terminate the string.
      WideStringFileName[pBuffer->FileNameLength/sizeof(wchar_t)]=L'\0';
     }
     else
       WideStringFileName[0]=L'\0';
    
    ---

    Reply
  • The file changed can't be renamed?

    Posted by leaflau on 09/14/2004 10:54pm

    What results this "bug"?

    • Upate

      Posted by Don Luis Perenna on 06/19/2005 09:06am

      Good Code, but it could be a good thing to update it . I had the same prb than leaflau...

      Reply
    • Thanks

      Posted by Vitali on 09/16/2004 07:30am

      Thank you too for the good question.

      Reply
    • I c

      Posted by leaflau on 09/16/2004 05:25am

      I'm sorry for my mistake.I missed out another thread in my program. Thanks.

      Reply
    • Just add FILE_SHARE_WRITE to allow rename files

      Posted by Vitali on 09/16/2004 04:00am

      You wrote:
      for example, a new file was created in the folder i spying. I found this event, but unable to rename the file anyway now. Just have a try!
      Solution:
      Just use FILE_SHARE_READ|FILE_SHARE_DELETE|FILE_SHARE_WRITE
      instead FILE_SHARE_READ|FILE_SHARE_DELETE to solve it.

      Reply
    • It's not the case i refered

      Posted by leaflau on 09/15/2004 12:50pm

      Assume that your program is running and spying the folder "c:\Program Files" all along. And now, create a new file in this folder, not through your program, but windows Explorer, then try to rename it. No problem?

      Reply
    • ok, no problems

      Posted by Vitali on 09/15/2004 06:18am

      Please pay attention on the following code fragment:

      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
      );

      in the demo project I was specify only FILE_SHARE_READ|FILE_SHARE_DELETE

      To solve this problem in yours projects just add FILE_SHARE_WRITE.

      Hope this will helpful.

      Reply
    • That is

      Posted by leaflau on 09/15/2004 04:59am

      for example, a new file was created in the folder i spying. I found this event, but unable to rename the file anyway now. Just have a try!

      Reply
    • What does you mean?

      Posted by Vitali on 09/15/2004 03:56am

      Please describe what exactly you mean when writing this question?

      Reply
    Reply
  • How to spy a UNIX directory shared by Samba?

    Posted by Dennis Lin on 07/27/2004 11:21am

    Thank you for your FileSpy. It is very useful for me to increase the efficiency of data processing. But I find ReadDirectoryChangesW() does not work if I use network drive to connect a directory existing on UNIX system and shared out by Samba. Do you have any idea how to make it work?

    • but the net connection is ok

      Posted by bearchen on 06/06/2005 10:36pm

      I can browse the content of the target folder,so the network is ok.But the function does not work.

      Reply
    • See what MSDN say

      Posted by Vitali on 05/11/2005 04:14am

      Error code 59 - An unexpected network error occurred. ERROR_UNEXP_NET_ERR.

      Reply
    • error code

      Posted by bearchen on 05/10/2005 05:27am

      In my case,the error code is 59.

      Reply
    • error code

      Posted by bearchen on 05/10/2005 04:27am

      In my case,the error code is 59.

      Reply
    • Determine error at the first

      Posted by Vitali on 08/11/2004 04:58am

      Thank you for the feedback. At the first the reason of the problem need to be determine. Just try to call GetLastError after each call of ReadDirectoryChanges to obtain error code.

      Reply
    Reply
  • Process which did the modification?

    Posted by Legacy on 12/02/2003 12:00am

    Originally posted by: David

    Hi !

    Is it possible to retrieve the PID of the process which carried out the modification ?

    Thanks in advance,

    David

    Reply
  • Loading, Please Wait ...

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • As all sorts of data becomes available for storage, analysis and retrieval - so called 'Big Data' - there are potentially huge benefits, but equally huge challenges...
  • The agile organization needs knowledge to act on, quickly and effectively. Though many organizations are clamouring for "Big Data", not nearly as many know what to do with it...
  • Cloud-based integration solutions can be confusing. Adding to the confusion are the multiple ways IT departments can deliver such integration...

Most Popular Programming Stories

More for Developers

RSS Feeds

Thanks for your registration, follow us on our social networks to keep up-to-date