WatchFileActivity Windows Workflow

Статья описывает Windows Workflow WatchFileActivity. Программа разработана на языке C#. Activity получает путь отслеживаемого каталога, фильтр типов файлов, признак контроля вложенных каталогов. Далее программа отслеживает любые действия: по созданию, переименованию, удалению файлов. При выполнении этих действий Activity их распознает и выводит сообщение. Можно доработать Activity, например, так, чтобы она посылала сообщения на электронную почту при выполнении указанных выше действий.
Представлен рабочий проект MS Visual Studio 2010 WCF + WorkFlow, в котором применяется описываемая Activity. В состав проекта входят WorkFlow, класс расширения, Windows Form клиент, который выплолнет: удаление, переименование или создание файлов.
1. Входные данные WatchFileActivity.
Входными параметрами для Activity служат:
  • Path - путь отслеживаемого каталога.
  • IncludeSubdirectories - признак, показывающий необходимость контроля вложенных каталогов по указанному пути.
  • Filter - фильтр, используемый для задания типов файлов, контролируемых в каталоге.
[RequiredArgument]
public InArgument<string> Path { get; set; }

[RequiredArgument]
public InArgument<bool> IncludeSubdirectories { get; set; }

[RequiredArgument]
public InArgument<string> Filter { get; set; }
2. Выходные данные WatchFileActivity.
Выходным аргументом Activity служит строка Result содержащая имя изменяемого обьекта.
[RequiredArgument]
public OutArgument<string> Result { get; set; }
3. Исходный текст WatchFileActivity.
Класс реализующий WatchFileActivity наследуется от класса NativeActivity и содержит методы определенные далее в тексте:
/* 09.08.2012
   1. Пример Watch пргограммы в виде WatchFileActivity.
   2. Activity отслеживает любые создание, переименование, удаления файлов.
   3. Входными параметрами для Activity являются:
        Path                   - получает путь отслеживаемого каталога.
        IncludeSubdirectories  - получает значение, показывающее  необходимость контроля вложенных каталогов по указанному пути.
        Filter                 - получает строку фильтра, используемую для определения файлов, контролируемых в каталоге.
  4. Выходным параметром является Result содержащее имя изменяемого файла.  
*/
using System;
using System.Collections.Generic;
using System.Activities;
using System.Activities.Hosting;
using System.IO;
using ClassLibrary;

namespace ActivityLibrary
{
    public class WatchFileActivity : NativeActivity
    {      
        [RequiredArgument]
        public InArgument<string> Path { get; set; }
        
        [RequiredArgument]
        public InArgument<bool> IncludeSubdirectories { get; set; }
         
        [RequiredArgument]
        public InArgument<string> Filter { get; set; }
        
        [RequiredArgument]
        public OutArgument<string> Result { get; set; }

        private FileSystemWatcher fileSystemWatcher         = null;
        private Bookmark bookmark                           = null;
        private FileSystemWatcherExtension extension        = null;
             
        protected override void Execute(NativeActivityContext context)
        {                      
           try
            {
                // Create FileSystemWatcher
                fileSystemWatcher = new FileSystemWatcher(context.GetValue(Path));
                fileSystemWatcher.IncludeSubdirectories = context.GetValue(IncludeSubdirectories);
                fileSystemWatcher.Filter = context.GetValue(Filter);   

                // Create a bookmark and get a link to it
                bookmark = context.CreateBookmark(ResumeFileCreatedBookmark,  BookmarkOptions.MultipleResume);

                // Get a reference to the class extension FileSystemWatcherExtension
                 extension =  context.GetExtension<FileSystemWatcherExtension>();

                // Let's get the two links - the Watcher and the Bookmark function in Start
                extension.Start(fileSystemWatcher, bookmark);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }

        protected override bool CanInduceIdle
        {
            get { return true; }
        }

        // Call to create/estore bookmarks
        public void ResumeFileCreatedBookmark(NativeActivityContext context, Bookmark bookmark, object obj)
        {
           Result.Set(context, (string)obj);
        }
    }
}
4. Класс расширения для WatchFileActivity.
С Activity используется класс FileSystemWatcherExtension, который содержит обработчики событий:
/*  09.08.2012
   Пример класса FileSystemWatcherExtension для WatchFileActivity.
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Activities;
using System.Activities.Hosting;
using System.IO;

namespace ClassLibrary
{
    public class FileSystemWatcherExtension : IWorkflowInstanceExtension
    {
        WorkflowInstanceProxy instance;
        Bookmark bookmark;

        public void SetInstance(WorkflowInstanceProxy instance)
        {
            this.instance = instance;
        }

        IEnumerable<object> IWorkflowInstanceExtension.GetAdditionalExtensions()
        {
            yield break;
        }

        public void Start(FileSystemWatcher fileSystemWatcher, Bookmark bookmark)
        {
            // Get a reference to a bookmark
            this.bookmark = bookmark;

            // Create a handler to create, rename, delete files
            fileSystemWatcher.Created += new FileSystemEventHandler(FileCreated);
            fileSystemWatcher.Renamed += new RenamedEventHandler(FileRename);
            fileSystemWatcher.Deleted += new FileSystemEventHandler(FileCreated);

           // Enable the reaction to changes to files and directories
            fileSystemWatcher.EnableRaisingEvents = true;

            Console.WriteLine("Start Watcher ...");
        }

        // 1. Handler when a file is created, that is, at first he was going to work!
        void FileCreated(object sender, FileSystemEventArgs e)
        {
            instance.BeginResumeBookmark(bookmark, e.FullPath, CompleteResume, null);
            Console.WriteLine("File Created: " + e.FullPath + " " + e.ChangeType);
        }

        // Handler when a file is renamed.
        void FileRename(object sender, RenamedEventArgs e)
        {
            instance.BeginResumeBookmark(bookmark, e.FullPath, CompleteResume, null);
            Console.WriteLine("Rename File:" + e.FullPath + " " + e.OldFullPath);
        }

        // Handler when a file is deleted.
        void FileDeleted(object sender, FileSystemEventArgs e)
        {
            instance.BeginResumeBookmark(bookmark, e.FullPath, CompleteResume, null);
            Console.WriteLine("Deleted File: " + e.FullPath + " " + e.ChangeType);
        }

        // 2. The second phase at the end of a bookmark
        void CompleteResume(IAsyncResult ar)
        {
            var result = instance.EndResumeBookmark(ar);
        }
    }    
}
5. Клиент для WatchFileActivity.
Ниже приведен пример клиента, который позволяет вносить изменения в файловую систему. Он используется только для отладки. В реальной системе компьютеры сервера и клиента чаще всего различные.
/* 09.08.2012
    Клиент для работы с Activity WatchFile
    Выполняет действия :
    1. Создание файла, если такой файл существует - сначала удаление.
    2. Переименовывает файл методом копирования.
    3. Вся логика по работе с изменением файлов сосредоточена здесь,
    Activity выполняет функции: закладки, отлавливает все события, выводит сообщения.
*/
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using ClientTestWatchActivity;
using System.IO;

namespace ClientTestWatchActivity
{
    public partial class Form1 : Form
    {
        FileStream file;

        public Form1()
        {
            InitializeComponent();
        }

        // Создание и удаление файла.
        private void button1_Click(object sender, EventArgs e)
        {
            try
            {
                // Считаем имя создаваемого файла
                string fileName = textBox2.Text;
                // Имя не пустое?
                if (String.IsNullOrEmpty(fileName)) throw new ArgumentOutOfRangeException("Name File: ", "Empty Value");
                
                // Проверим, нет ли такого файла?
                const string caption = "Delete File";
                if (File.Exists(fileName))
                {
                    var result = MessageBox.Show(fileName, caption, MessageBoxButtons.YesNo, MessageBoxIcon.Question);
                    // Если файл удалять - удалим
                    if (result == DialogResult.Yes) File.Delete(fileName);
                }

                file = File.Open(fileName, FileMode.Create);
                byte[] bytes = UnicodeEncoding.Unicode.GetBytes(textBox1.Text);
                file.Write(bytes, 0, bytes.Length);
                file.Flush();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString(), "Error ");
            }
            finally
            {
                if(!(file == null))
                file.Close();
            }
        }

        // Переименование файла
        private void button2_Click(object sender, EventArgs e)
        {
            try
            {
                // Считаем имя файла который надо переименовать
                string path = textBox2.Text;
                // Считаем новое имя файла
                string newPath = textBox3.Text;

                // Певое Имя не пустое?
                if (String.IsNullOrEmpty(path)) throw new ArgumentOutOfRangeException("Name File: ", "Empty Value");
                // Второе имя не пустое?
                if (String.IsNullOrEmpty(newPath)) throw new ArgumentOutOfRangeException("New Name File: ", "Empty Value");

                // Проверим, нет ли первого файла, который надо удалить?
                const string caption = "Delete File";
                if (File.Exists(newPath))
                {
                    var result = MessageBox.Show(newPath, caption, MessageBoxButtons.YesNo, MessageBoxIcon.Question);
                    // Если файл удалять - удалим
                    if (result == DialogResult.Yes) File.Delete(newPath);
                }

                File.Move(path, newPath);
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString(), "Error ");
            }
            finally
            {
                if (!(file == null))
                    file.Close();
            }
        }

        // Включение - выключение кнопок.
        private void checkBox1_CheckedChanged(object sender, EventArgs e)
        {
            if (checkBox1.Checked == true)
            {
                textBox3.Enabled = true;
                button1.Enabled = false;
                button2.Enabled = true;
            }
            else
            {
                textBox3.Enabled = false;
                button1.Enabled = true;
                button2.Enabled = false;
            }
        }
    }
}

Visual Studio 2010 проект содержащий: Activity, Workflow и Windows Form клиент можно загрузить.
Евгений Вересов.
09.08.2012 года.