How to implement FileSystemWatcher with X++

The CLR offers with System.IO.FileSystemWatcher a very simple to use class which can be used with X++ in order to identify new created, renamed, deleted and changed (The change of a file or folder. The types of changes include: changes to size, attributes, security settings, last write, and last access time) files in directories and even in the containing subdirectories. Unfortunately X++ does not support the CLR-events, so that the code in X++ will be a little bit more complicated as this would be the case with C#.

The following code shows you how you can use this class with X++:

    1:  public static Container SetStaticFileWatcher()
    2:  {
    3:  System.IO.FileSystemWatcher fw = new System.IO.FileSystemWatcher();
    4:  System.IO.WatcherChangeTypes watcherChangeType;
    5:  System.IO.WaitForChangedResult res;
    6:  Container cont;
    7:  str fileName;
    8:  str oldFileName;
    9:  str changeType;
   10:  ;
   11:   
   12:  //TODO: You can point to the directory you want to observe
   13:  fw.set_Path("c:\\temp");
   14:  //TODO: You can choose if you want to include subdirectories
   15:  fw.set_IncludeSubdirectories(true);
   16:  //TODO: You can define her the name-pattern
   17:  fw.set_Filter("*.txt");
   18:  //TODO: You can choose the type of action you want to observe. Have a look at MSDN…
   19:  watcherChangeType = ClrInterop::parseClrEnum('System.IO.WatcherChangeTypes', 'All');
   20:  res = fw.WaitForChanged(watcherChangeType);
   21:   
   22:  fileName = res.get_Name();
   23:   
   24:  //ChangeTypeName can be: Created, Deleted, Renamed and Changed
   25:  changeType = System.Enum::GetName(watcherChangeType.GetType(), res.get_ChangeType());
   26:   
   27:  //OldName must never be null. You'll receive a X++ error
   28:  if (changeType == 'Renamed')
   29:      oldFileName = res.get_OldName();
   30:   
   31:  cont += fileName;
   32:  cont += changeType;
   33:  cont += oldFileName;
   34:   
   35:  fw.Dispose();
   36:   
   37:  return cont;
   38:  }

And the following job helps you to execute this method:

    1:  static void FileWatcher(Args _args)
    2:  {
    3:  A1FileWatcher fileWatcher = new A1FileWatcher();
    4:  Container cont;
    5:  str fileName;
    6:  str oldFileName;
    7:  str changeType;
    8:  ;
    9:   
   10:  cont = A1FileWatcher::SetFileWatcher();
   11:   
   12:  fileName = conpeek(cont, 1);
   13:  oldFileName = conpeek(cont, 3);
   14:  changeType = conpeek(cont, 2);
   15:  }

The only problem you might face in some scenarios is the fact that the method WaitForChanged in line 20 is executed in the same thread with the rest of your code. This means that the job will wait until something happens (created, deleted, …). In order to prevent this, you need to run this method in a separate thread which I show in the following code  example:

    1:  private static container SetFileWatcher(Thread thread)
    2:  {
    3:  System.IO.FileSystemWatcher fileSystemWatcher;
    4:  System.IO.WatcherChangeTypes watcherChangeType;
    5:  System.IO.WaitForChangedResult res;
    6:  str fileName, oldFileName, changeType;
    7:  str searchPattern, searchPath, searchType;
    8:  Container cont
    9:  ;
   10:   
   11:      fileSystemWatcher = new System.IO.FileSystemWatcher();
   12:      cont =  thread.getInputParm();
   13:      searchPath = conpeek(cont, 1);
   14:      searchPattern = conpeek(cont, 2);
   15:      searchType = conpeek(cont, 3);
   16:   
   17:      System.Diagnostics.EventLog::WriteEntry(searchPath,searchPath);
   18:      fileSystemWatcher.set_Path(searchPath);
   19:      fileSystemWatcher.set_IncludeSubdirectories(true);
   20:      fileSystemWatcher.set_Filter(searchPattern);
   21:      watcherChangeType = ClrInterop::parseClrEnum('System.IO.WatcherChangeTypes', searchType);
   22:   
   23:      //Waiting for the action
   24:      res = fileSystemWatcher.WaitForChanged(watcherChangeType);
   25:      //fileSystemWatcher.Dispose();
   26:   
   27:      fileName = res.get_Name();
   28:      //ChangeTypeName can be: Created, Deleted, Renamed and Deleted
   29:      changeType = System.Enum::GetName(watcherChangeType.GetType(), res.get_ChangeType());
   30:   
   31:   
   32:      //OldName must never be null. You'll receive a X++ error
   33:      if (changeType == 'Renamed')
   34:          oldFileName = res.get_OldName();
   35:   
   36:      //TODO: add here your code
   37:      System.Diagnostics.EventLog::WriteEntry("Hello World","something to do");
   38:   
   39:      //notifies the waiting thread
   40:      thread.notify();
   41:   
   42:      return cont;
   43:  }

And here’s the code that creates the thread and executes the static method above:

    1:  static void main(Args _args)
    2:  {
    3:  Thread fileWatcherThread;
    4:  Container cont;
    5:  Container ouputCont;
    6:  ;
    7:   
    8:      fileWatcherThread = new Thread();
    9:      cont += "c:\\temp";
   10:      cont += "*.txt";
   11:      cont += "All";
   12:      if (fileWatcherThread)
   13:      {
   14:          fileWatcherThread.removeOnComplete(true);
   15:          fileWatcherThread.setInputParm(cont);
   16:          fileWatcherThread.run(classnum(A0FileWatcher), identifierstr(SetFileWatcher));
   17:          fileWatcherThread.setOutputParm(ouputCont);
   18:          //TODO: you can now here work with the retuned values in the container
   19:      }
   20:  }