Jump to content


Check out our Community Blogs

Register and join over 40,000 other developers!


Recent Status Updates

View All Updates

Photo
- - - - -

Monitor a Folder for Changes

delphi folder monitor file monitor directory change

  • Please log in to reply
2 replies to this topic

#1 Luthfi

Luthfi

    CC Leader

  • Expert Member
  • PipPipPipPipPipPipPip
  • 1320 posts
  • Programming Language:PHP, Delphi/Object Pascal, Pascal, Transact-SQL
  • Learning:C, Java, PHP

Posted 16 August 2013 - 10:32 PM

Overview
 
In some of my projects I needed to monitor certain folders for changes. They must detect file or subfolder renames, additions, content changes, even security attribute changes. To accomplish this, I was using the help of Windows api ReadDirectoryChangesW. Since the real operation involves many steps and involved separate thread (I don't want the monitoring process to block the main thread), I wrapped the monitoring codes into classes.
 
If you have used anti-virus that provides real-time virus scanner (in Windows, of course), then you already enjoyed the benefit of this monitor for folder changes. These anti-virus would monitor the root of a drive for changes (including subfolders), and for each detected change (when it's a file) they will scan the changed file.
 
And here I would like to share the codes along with explanation to their key concepts.
 
 
TFolderMon class
 
This class was designed for the "public" interface of our folder monitoring system. That is why it was declared in interface section of the containing unit (FolderMon.pas). It contains properties that needed for the monitoring process. The information are:

  • Folder to be monitored
  • Kind of changes to be monitored
  • Whether it is wanted to also monitor the subfolder(s)
  • Whether the monitor is active

TFolderMonWorker
 
This class is descendant of TThread, and since it is not designed to be available to "public", it is declared in the implementation section of the FolderMon.pas unit.
 
Being a descendant of TThread, the thread's behavior was coded by overriding Execute method. The pseudocode of the overriding Execute would be:
 








procedure Execute;
begin
  SetUp;
  while ThreadNotTerminated do
  begin
    if ReadDirectoryChanges then
      for each change do
        NotifyChange;
  end;
  TearDown;
end;

 
However, in the actual implemention we do the SetUp part in the thread's constructor. This is to minimize the possibility of read/write clash. Thread's constructor will be executed in the caller's thread context. In our case, this fact makes it safe for our thread to access information from it's corresponding TFolderMon instance. For example, our thread needs monitored folder name information which is presented as string. And I believe you already know that string access is not thread-safe. The thread actually does not need the folder name to monitor changes. It only needs folder handle obtained through the folder name. Once this handle is obtained, the folder name is no longer relevant. So moving the SetUp part to constructore will be good.
 
Full implementation would be like shown below.
 



constructor TFolderMonWorker.Create(AOwner: TFolderMon);
begin
  Owner := AOwner;
  if Owner=nil then
    raise Exception.Create('Reference to TFolderMon instance must be specified');

  inherited Create(False);
  FreeOnTerminate := True;
  SetUp;
end;


procedure TFolderMonWorker.SetUp;
var
  i: TChangeType;
begin
  FFolder := CreateFile(PChar(Owner.Folder)
                        , FILE_LIST_DIRECTORY or GENERIC_READ
                        , FILE_SHARE_READ or FILE_SHARE_WRITE or FILE_SHARE_DELETE
                        , nil
                        , OPEN_EXISTING
                        , FILE_FLAG_BACKUP_SEMANTICS
                        , 0);

  FMonFilter := 0;
  for i := Low(TChangeType) to High(TChangeType) do
    if i in Owner.MonitoredChanges then
      FMonFilter := FMonFilter or NOTIFY_FILTERS[i];
end;



procedure TFolderMonWorker.Execute;
const
  cBufSize = 32 * 1024;  // 32k
var
  B: Pointer;
  vCount: DWord;
  vOffset: DWord;
  vFileInfo: PFILE_NOTIFY_INFORMATION;
begin
  GetMem(B, cBufSize);
  try
    while not Terminated do
    begin
      if Owner=nil then
        Exit;

      if ReadDirectoryChangesW(FFolder
                               , B
                               , cBufSize
                               , Owner.MonitorSubFolders
                               , FMonFilter
                               , @vCount
                               , nil
                               , nil
                              )
         and (vCount > 0) then
      begin
        if Owner=nil then
          Exit;

        vFileInfo := B;
        repeat
          vOffset := vFileInfo.NextEntryOffset;

          FFolderItemInfo.Name := WideCharLenToString(@vFileInfo^.FileName, vFileInfo^.FileNameLength);
          SetLength(FFolderItemInfo.Name, vFileInfo^.FileNameLength div 2);
          case vFileInfo^.Action of
            FILE_ACTION_ADDED           : FFolderItemInfo.Action := faNew;
            FILE_ACTION_REMOVED         : FFolderItemInfo.Action := faRemoved;
            FILE_ACTION_MODIFIED        : FFolderItemInfo.Action := faModified;
            FILE_ACTION_RENAMED_OLD_NAME: FFolderItemInfo.Action := faRenamedOld;
            FILE_ACTION_RENAMED_NEW_NAME: FFolderItemInfo.Action := faRenamedNew;
          end;
          Synchronize(DoFolderItemChange);
          PByte(vFileInfo) := PByte(DWORD(vFileInfo) + vOffset);
        until vOffset=0;
      end;
    end;
  finally
    TearDown;
    FreeMem(B, cBufSize);
  end;
end;



procedure TFolderMonWorker.TearDown;
begin
  CloseHandle(FFolder);
end;

 
Note that :

  • We call TearDown in the thread's own context, since beside our thread there is none interested with the folder handle. So it's safe.
  • Owner refers to instance of TFolderMon that created the thread. Through this reference the thread know the correct event to raise.

 

Demo Project

 

For testing TFolderMon, I have written a simple demo project. This demo project uses an instance of TFolderMon to monitor a folder and provides some ways to add, delete, and change some files of the monitored folder.

 

Running the Demo Project

 

  1. Upon running the demo project, you will get like shown below.

    FolderChangeMonitorDemo_RunTime_000.png
  2. To actually monitor a folder, we need to activate TFolderMon. Click on the Activate button, to start monitor the selected folder.

    FolderChangeMonitorDemo_RunTime_010_Activated.png
  3. Let's add some new files to the monitored folder. Click on the Add random text file a few times. Each click will add a text file to the monitored folder. The name and content of the file will be random.

    FolderChangeMonitorDemo_RunTime_030_SomeFilesAdded.png
  4. Now let's delete a file. Click on the Delete random text file button.

    FolderChangeMonitorDemo_RunTime_040_AFileGotDeleted.png
  5. Let's modify the content of a file. Click on Change content/size of random text file button. It will randomly pick a text file inside the monitored folder and generate random content for it. And you will get something like shown below.

    FolderChangeMonitorDemo_RunTime_050_AFileGotModified.png
  6. The last test is to try rename a file. Click on Rename a random text file button. A random text file in the monitored folder will be selected and then renamed into random name. And you will get something like shown below.

    FolderChangeMonitorDemo_RunTime_060_AFileGotRenamed.png

That's it. It's easy to monitor a folder for changes. Full source code of FolderMon.pas unit and the demo project can be downloaded here: Attached File  FolderChangeMonitor_Demo.zip   991.79KB   3863 downloads. Feel free to use it for any use.

 

Cheers!


  • 1

#2 danilomibr

danilomibr

    CC Lurker

  • Just Joined
  • Pip
  • 1 posts

Posted 13 October 2015 - 10:02 AM

Congratulations! Very simple and complete sample! Thanks!


  • 0

#3 JeffLott

JeffLott

    CC Lurker

  • Just Joined
  • Pip
  • 1 posts

Posted 12 January 2016 - 02:44 PM

Perfect. Just what I was looking for. Thanks!


  • 0





Also tagged with one or more of these keywords: delphi, folder monitor, file monitor, directory change

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download