Spying a File System

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.

Comments
PocketPC/WindowsMobile
Posted by badzio on 10/23/2008 03:44amIs similar solution for PocketPC/WindowsMobile (but not based on CF .Net)?
ReplyI'm Can't change file name
Posted by baby999 on 04/23/2008 06:10amI'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 );ReplyFolder locking & Password protection code
Posted by c0mgu1 on 08/09/2005 06:59amI need a sample code for setting a password for accessing a folder & it contined subfolders/files plz help me
Replyi need notification message before event occure
Posted by c0mgu1 on 08/09/2005 06:57amHere we are getting notification message after the event happence like after file deletion, but how to get an event before deletion...
Replyhow can i get the last filename after rename?
Posted by yqzq on 07/15/2005 05:54amhow 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?
Replyhow can i get the last filename after rename?
Posted by yqzq on 07/15/2005 05:47amhow 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?
ReplyBeware if you use snippets from this sample code.
Posted by HSmith on 05/07/2005 08:43amThe 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'; ---ReplyThe file changed can't be renamed?
Posted by leaflau on 09/14/2004 10:54pm-
-
-
-
-
-
-
-
ReplyUpate
Posted by Don Luis Perenna on 06/19/2005 09:06amGood Code, but it could be a good thing to update it . I had the same prb than leaflau...
ReplyThanks
Posted by Vitali on 09/16/2004 07:30amThank you too for the good question.
ReplyI c
Posted by leaflau on 09/16/2004 05:25amI'm sorry for my mistake.I missed out another thread in my program. Thanks.
ReplyJust add FILE_SHARE_WRITE to allow rename files
Posted by Vitali on 09/16/2004 04:00amYou wrote:
Replyfor 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.
It's not the case i refered
Posted by leaflau on 09/15/2004 12:50pmAssume 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?
Replyok, no problems
Posted by Vitali on 09/15/2004 06:18amPlease 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.
ReplyThat is
Posted by leaflau on 09/15/2004 04:59amfor 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!
ReplyWhat does you mean?
Posted by Vitali on 09/15/2004 03:56amPlease describe what exactly you mean when writing this question?
ReplyHow to spy a UNIX directory shared by Samba?
Posted by Dennis Lin on 07/27/2004 11:21amThank 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?
-
-
-
-
-
Replybut the net connection is ok
Posted by bearchen on 06/06/2005 10:36pmI can browse the content of the target folder,so the network is ok.But the function does not work.
ReplySee what MSDN say
Posted by Vitali on 05/11/2005 04:14amError code 59 - An unexpected network error occurred. ERROR_UNEXP_NET_ERR.
Replyerror code
Posted by bearchen on 05/10/2005 05:27amIn my case,the error code is 59.
Replyerror code
Posted by bearchen on 05/10/2005 04:27amIn my case,the error code is 59.
ReplyDetermine error at the first
Posted by Vitali on 08/11/2004 04:58amThank 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.
ReplyProcess which did the modification?
Posted by Legacy on 12/02/2003 12:00amOriginally posted by: David
Hi !
Is it possible to retrieve the PID of the process which carried out the modification ?
Thanks in advance,
David
ReplyLoading, Please Wait ...