■ FileSystemWatcher 클래스 : 이벤트 중복 방지하기

----------------------------------------------------------------------------------------------------


TestProject.zip


DelayedEvent.cs

 

 

using System.IO;

 

namespace TestProject

{

    /// <summary>

    /// 지연 이벤트

    /// </summary>

    public class DelayedEvent

    {

        //////////////////////////////////////////////////////////////////////////////////////////////////// Field

        ////////////////////////////////////////////////////////////////////////////////////////// Private

 

        #region Field

 

        /// <summary>

        /// 이벤트 인자

        /// </summary>

        private readonly FileSystemEventArgs eventArgs;

 

        /// <summary>

        /// 지연 여부

        /// </summary>

        private bool isDelayed;

 

        #endregion

 

        //////////////////////////////////////////////////////////////////////////////////////////////////// Property

        ////////////////////////////////////////////////////////////////////////////////////////// Public

 

        #region 이벤트 인자 - EventArgs

 

        /// <summary>

        /// 이벤트 인자

        /// </summary>

        public FileSystemEventArgs EventArgs

        {

            get

            {

                return this.eventArgs;

            }

        }

 

        #endregion

        #region 지연 여부 - IsDelayed

 

        /// <summary>

        /// 지연 여부

        /// </summary>

        public bool IsDelayed

        {

            get

            {

                return this.isDelayed;

            }

            set

            {

                this.isDelayed = value;

            }

        }

 

        #endregion

 

        //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor

        ////////////////////////////////////////////////////////////////////////////////////////// Public

 

        #region 생성자 - DelayedEvent(eventArgs)

 

        /// <summary>

        /// 생성자

        /// </summary>

        /// <param name="eventArgs">이벤트 인자</param>

        public DelayedEvent(FileSystemEventArgs eventArgs)

        {

            this.eventArgs = eventArgs;

            this.isDelayed = false;

        }

 

        #endregion

 

        //////////////////////////////////////////////////////////////////////////////////////////////////// Method

        ////////////////////////////////////////////////////////////////////////////////////////// Public

 

        #region 중복 여부 구하기 - IsDuplicate(sourceObject)

 

        /// <summary>

        /// 중복 여부 구하기

        /// </summary>

        /// <param name="sourceObject">소스 객체</param>

        /// <returns>중복 여부</returns>

        public virtual bool IsDuplicate(object sourceObject)

        {

            DelayedEvent delayedEvent = sourceObject as DelayedEvent;

 

            if(delayedEvent == null)

            {

                return false;

            }

 

            FileSystemEventArgs fileSystemEventArgs1 = this.eventArgs;

            RenamedEventArgs    renamedEventArgs1    = this.eventArgs as RenamedEventArgs;

            FileSystemEventArgs fileSystemEventArgs2 = delayedEvent.EventArgs;

            RenamedEventArgs    renamedEventArgs2    = delayedEvent.EventArgs as RenamedEventArgs;

 

            return

            (

                (

                    fileSystemEventArgs1            != null                            &&

                    fileSystemEventArgs2            != null                            &&

                    fileSystemEventArgs1.ChangeType == fileSystemEventArgs2.ChangeType &&

                    fileSystemEventArgs1.FullPath   == fileSystemEventArgs2.FullPath   &&

                    fileSystemEventArgs1.Name       == fileSystemEventArgs2.Name

                ) &&

                (

                    (renamedEventArgs1 == null && renamedEventArgs2 == null) ||

                    (

                        renamedEventArgs1             != null                          &&

                        renamedEventArgs2             != null                          &&

                        renamedEventArgs1.OldFullPath == renamedEventArgs2.OldFullPath &&

                        renamedEventArgs1.OldName     == renamedEventArgs2.OldName

                    )

                )

            ) ||

            (

                fileSystemEventArgs1            != null                          &&

                fileSystemEventArgs2            != null                          &&

                fileSystemEventArgs1.ChangeType == WatcherChangeTypes.Created    &&

                fileSystemEventArgs2.ChangeType == WatcherChangeTypes.Changed    &&

                fileSystemEventArgs1.FullPath   == fileSystemEventArgs2.FullPath &&

                fileSystemEventArgs1.Name       == fileSystemEventArgs2.Name

            );

        }

 

        #endregion

    }

}

 

 

SafeFileSystemWatcher.cs

 

 

using System;

using System.Collections;

using System.ComponentModel;

using System.IO;

using System.Threading;

using System.Timers;

 

namespace TestProject

{

    /// <summary>

    /// 안전 파일 시스템 감시자

    /// </summary>

    public class SafeFileSystemWatcher

    {

        //////////////////////////////////////////////////////////////////////////////////////////////////// Event

        ////////////////////////////////////////////////////////////////////////////////////////// Public

 

        #region 생성시 - Created

 

        /// <summary>

        /// 생성시

        /// </summary>

        public event FileSystemEventHandler Created;

 

        #endregion

        #region 변경시 - Changed

 

        /// <summary>

        /// 변경시

        /// </summary>

        public event FileSystemEventHandler Changed;

 

        #endregion

        #region 삭제시 - Deleted

 

        /// <summary>

        /// 삭제시

        /// </summary>

        public event FileSystemEventHandler Deleted;

 

        #endregion

        #region 명칭 변경시 - Renamed

 

        /// <summary>

        /// 명칭 변경시

        /// </summary>

        public event RenamedEventHandler Renamed;

 

        #endregion

        #region 에러시 - Error

 

        /// <summary>

        /// 에러시

        /// </summary>

        public event ErrorEventHandler Error;

 

        #endregion

 

        //////////////////////////////////////////////////////////////////////////////////////////////////// Field

        ////////////////////////////////////////////////////////////////////////////////////////// Private

 

        #region Field

 

        /// <summary>

        /// 감시자

        /// </summary>

        private readonly FileSystemWatcher watcher;

 

        /// <summary>

        /// 잠금 객체

        /// </summary>

        private readonly object lockObject = new object();

 

        /// <summary>

        /// 이벤트 배열 리스트

        /// </summary>

        private ArrayList eventArrayList;

 

        /// <summary>

        /// 타이머

        /// </summary>

        private System.Timers.Timer timer;

 

        /// <summary>

        /// 통합 간격 (단위 : 밀리초)

        /// </summary>

        private int consolidationInterval = 1000;

 

        #endregion

 

        //////////////////////////////////////////////////////////////////////////////////////////////////// Property

        ////////////////////////////////////////////////////////////////////////////////////////// Public

 

        #region 이벤트 발생 활성화 여부 - EnableRaisingEvents

 

        /// <summary>

        /// 이벤트 발생 활성화 여부

        /// </summary>

        public bool EnableRaisingEvents

        {

            get

            {

                return this.watcher.EnableRaisingEvents;

            }

            set

            {

                this.watcher.EnableRaisingEvents = value;

 

                if(value)

                {

                    this.timer.Start();

                }

                else

                {

                    this.timer.Stop();

 

                    this.eventArrayList.Clear();

                }

            }

        }

 

        #endregion

        #region 필터 - Filter

 

        /// <summary>

        /// 필터

        /// </summary>

        public string Filter

        {

            get

            {

                return this.watcher.Filter;

            }

            set

            {

                this.watcher.Filter = value;

            }

        }

 

        #endregion

        #region 하위 디렉토리 포함 여부 - IncludeSubdirectories

 

        /// <summary>

        /// 하위 디렉토리 포함 여부

        /// </summary>

        public bool IncludeSubdirectories

        {

            get

            {

                return this.watcher.IncludeSubdirectories;

            }

            set

            {

                this.watcher.IncludeSubdirectories = value;

            }

        }

 

        #endregion

        #region 내부 버퍼 크기 - InternalBufferSize

 

        /// <summary>

        /// 내부 버퍼 크기

        /// </summary>

        /// <remarks>디폴트 크기 : 8KB</remarks>

        public int InternalBufferSize

        {

            get

            {

                return this.watcher.InternalBufferSize;

            }

            set

            {

                this.watcher.InternalBufferSize = value;

            }

        }

 

        #endregion

        #region 통지 필터 - NotifyFilter

 

        /// <summary>

        /// 통지 필터

        /// </summary>

        public NotifyFilters NotifyFilter

        {

            get

            {

                return this.watcher.NotifyFilter;

            }

            set

            {

                this.watcher.NotifyFilter = value;

            }

        }

 

        #endregion

        #region 경로 - Path

 

        /// <summary>

        /// 경로

        /// </summary>

        public string Path

        {

            get

            {

                return this.watcher.Path;

            }

            set

            {

                this.watcher.Path = value;

            }

        }

 

        #endregion

        #region 동기화 객체 - SynchronizingObject

 

        /// <summary>

        /// 동기화 객체

        /// </summary>

        public ISynchronizeInvoke SynchronizingObject

        {

            get

            {

                return this.watcher.SynchronizingObject;

            }

            set

            {

                this.watcher.SynchronizingObject = value;

            }

        }

 

        #endregion

        #region 통합 간격 - ConsolidationInterval

 

        /// <summary>

        /// 통합 간격

        /// </summary>

        public int ConsolidationInterval

        {

            get

            {

                return this.consolidationInterval;

            }

            set

            {

                this.consolidationInterval = value;

 

                this.timer.Interval = value;

            }

        }

 

        #endregion

 

        //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor

        ////////////////////////////////////////////////////////////////////////////////////////// Public

 

        #region 생성자 - SafeFileSystemWatcher()

 

        /// <summary>

        /// 생성자

        /// </summary>

        public SafeFileSystemWatcher()

        {

            this.watcher = new FileSystemWatcher();

 

            Initialize();

        }

 

        #endregion

        #region 생성자 - SafeFileSystemWatcher(directoryPath)

 

        /// <summary>

        /// 생성자

        /// </summary>

        /// <param name="directoryPath">디렉토리 경로</param>

        public SafeFileSystemWatcher(string directoryPath)

        {

            this.watcher = new FileSystemWatcher(directoryPath);

 

            Initialize();

        }

 

        #endregion

        #region 생성자 - SafeFileSystemWatcher(directoryPath, filter)

 

        /// <summary>

        /// 생성자

        /// </summary>

        /// <param name="directoryPath">디렉토리 경로</param>

        /// <param name="filter">필터</param>

        public SafeFileSystemWatcher(string directoryPath, string filter)

        {

            this.watcher = new FileSystemWatcher(directoryPath, filter);

 

            Initialize();

        }

 

        #endregion

 

        //////////////////////////////////////////////////////////////////////////////////////////////////// Method

        ////////////////////////////////////////////////////////////////////////////////////////// Public

 

        #region 초기화 시작하기 - BeginInit()

 

        /// <summary>

        /// 초기화 시작하기

        /// </summary>

        public void BeginInit()

        {

            this.watcher.BeginInit();

        }

 

        #endregion

        #region 초기화 종료하기 - EndInit()

 

        /// <summary>

        /// 초기화 종료하기

        /// </summary>

        public void EndInit()

        {

            this.watcher.EndInit();

        }

 

        #endregion

        #region 리소스 해제하기 - Dispose()

 

        /// <summary>

        /// 리소스 해제하기

        /// </summary>

        public void Dispose()

        {

            Uninitialize();

        }

 

        #endregion

 

        #region 변경시 대기하기 - WaitForChanged(changeType)

 

        /// <summary>

        /// 변경시 대기하기

        /// </summary>

        /// <param name="changeType">변경 타입</param>

        /// <returns>변경시 대기 결과</returns>

        public WaitForChangedResult WaitForChanged(WatcherChangeTypes changeType)

        {

            throw new NotImplementedException();

        }

 

        #endregion

        #region 변경시 대기하기 - WaitForChanged(changeType, timeout)

 

        /// <summary>

        /// 변경시 대기하기

        /// </summary>

        /// <param name="changeType">변경 타입</param>

        /// <param name="timeout">타임아웃</param>

        /// <returns>변경시 대기 결과</returns>

        public WaitForChangedResult WaitForChanged(WatcherChangeTypes changeType, int timeout)

        {

            throw new NotImplementedException();

        }

 

        #endregion

 

        ////////////////////////////////////////////////////////////////////////////////////////// Protected

 

        #region 생성시 이벤트 발생시키기 - FireCreatedEvent(e)

 

        /// <summary>

        /// 생성시 이벤트 발생시키기

        /// </summary>

        /// <param name="e">이벤트 인자</param>

        protected void FireCreatedEvent(FileSystemEventArgs e)

        {

            Created?.Invoke(this, e);

        }

 

        #endregion

        #region 변경시 이벤트 발생시키기 - FireChangedEvent(e)

 

        /// <summary>

        /// 변경시 처리하기

        /// </summary>

        /// <param name="e">이벤트 인자</param>

        protected void FireChangedEvent(FileSystemEventArgs e)

        {

            Changed?.Invoke(this, e);

        }

 

        #endregion

        #region 삭제시 이벤트 발생시키기 - FireDeletedEvent(e)

 

        /// <summary>

        /// 삭제시 이벤트 발생시키기

        /// </summary>

        /// <param name="e">이벤트 인자</param>

        protected void FireDeletedEvent(FileSystemEventArgs e)

        {

            Deleted?.Invoke(this, e);

        }

 

        #endregion

        #region 명칭 변경시 이벤트 발생시키기 - FireRenamedEvent(e)

 

        /// <summary>

        /// 명칭 변경시 이벤트 발생시키기

        /// </summary>

        /// <param name="e">이벤트 인자</param>

        protected void FireRenamedEvent(RenamedEventArgs e)

        {

            Renamed?.Invoke(this, e);

        }

 

        #endregion

        #region 에러시 이벤트 발생시키기 - FireErrorEvent(e)

 

        /// <summary>

        /// 에러시 이벤트 발생시키기

        /// </summary>

        /// <param name="e">이벤트 인자</param>

        protected void FireErrorEvent(ErrorEventArgs e)

        {

            Error?.Invoke(this, e);

        }

 

        #endregion

 

        #region 이벤트 발생시키기 - FireEvent(queue)

 

        /// <summary>

        /// 이벤트 발생시키기

        /// </summary>

        /// <param name="queue"></param>

        protected void FireEvent(Queue queue)

        {

            if((queue != null) && (queue.Count > 0))

            {

                DelayedEvent delayedEvent;

 

                while(queue.Count > 0)

                {

                    delayedEvent = queue.Dequeue() as DelayedEvent;

 

                    switch(delayedEvent.EventArgs.ChangeType)

                    {

                        case WatcherChangeTypes.Created : FireCreatedEvent(delayedEvent.EventArgs                    ); break;

                        case WatcherChangeTypes.Changed : FireChangedEvent(delayedEvent.EventArgs                    ); break;

                        case WatcherChangeTypes.Deleted : FireDeletedEvent(delayedEvent.EventArgs                    ); break;

                        case WatcherChangeTypes.Renamed : FireRenamedEvent(delayedEvent.EventArgs as RenamedEventArgs); break;

                    }

                }

            }

        }

 

        #endregion

        

        ////////////////////////////////////////////////////////////////////////////////////////// Private

        //////////////////////////////////////////////////////////////////////////////// Event

 

        #region 감시자 생성시 처리하기 - watcher_Created(sender, e)

 

        /// <summary>

        /// 감시자 생성시 처리하기

        /// </summary>

        /// <param name="sender">이벤트 발생자</param>

        /// <param name="e">이벤트 인자</param>

        private void watcher_Created(object sender, FileSystemEventArgs e)

        {

            this.eventArrayList.Add(new DelayedEvent(e));

        }

 

        #endregion

        #region 감시자 명칭 변경시 처리하기 - watcher_Renamed(sender, e)

 

        /// <summary>

        /// 감시자 명칭 변경시 처리하기

        /// </summary>

        /// <param name="sender">이벤트 발생자</param>

        /// <param name="e">이벤트 인자</param>

        private void watcher_Renamed(object sender, RenamedEventArgs e)

        {

            this.eventArrayList.Add(new DelayedEvent(e));

        }

 

        #endregion

        #region 감시자 에러시 처리하기 - watcher_Error(sender, e)

 

        /// <summary>

        /// 감시자 에러시 처리하기

        /// </summary>

        /// <param name="sender">이벤트 발생자</param>

        /// <param name="e">이벤트 인자</param>

        private void watcher_Error(object sender, ErrorEventArgs e)

        {

            FireErrorEvent(e);

        }

 

        #endregion

 

        #region 타이머 경과시 처리하기 - timer_Elapsed(sender, e)

 

        /// <summary>

        /// 타이머 경과시 처리하기

        /// </summary>

        /// <param name="sender">이벤트 발생자</param>

        /// <param name="e">이벤트 인자</param>

        private void timer_Elapsed(object sender, ElapsedEventArgs e)

        {

            Queue eventQueue = null;

 

            if(Monitor.TryEnter(this.lockObject))

            {

                try

                {

                    eventQueue = new Queue(32);

 

                    lock(this.eventArrayList.SyncRoot)

                    {

                        DelayedEvent currentEvent;

 

                        for(int i = 0; i < this.eventArrayList.Count; i++)

                        {

                            currentEvent = this.eventArrayList[i] as DelayedEvent;

 

                            if(currentEvent.IsDelayed)

                            {

                                for(int j = i + 1; j < this.eventArrayList.Count; j++)

                                {

                                    if(currentEvent.IsDuplicate(this.eventArrayList[j]))

                                    {

                                        this.eventArrayList.RemoveAt(j);

 

                                        j--;

                                    }

                                }

 

                                bool raiseEvent = true;

 

                                if(currentEvent.EventArgs.ChangeType == WatcherChangeTypes.Created ||

                                    currentEvent.EventArgs.ChangeType == WatcherChangeTypes.Changed)

                                {

                                    FileStream stream = null;

 

                                    try

                                    {

                                        stream = File.Open(currentEvent.EventArgs.FullPath, FileMode.Open, FileAccess.Read, FileShare.None);

                                    }

                                    catch(IOException)

                                    {

                                        raiseEvent = false;

                                    }

                                    finally

                                    {

                                        if(stream != null)

                                        {

                                            stream.Close();

                                        }

                                    }

                                }

 

                                if(raiseEvent)

                                {

                                    eventQueue.Enqueue(currentEvent);

 

                                    this.eventArrayList.RemoveAt(i);

 

                                    i--;

                                }

                            }

                            else

                            {

                                currentEvent.IsDelayed = true;

                            }

                        }

                    }

                }

                finally

                {

                    Monitor.Exit(this.lockObject);

                }

            }

 

            FireEvent(eventQueue);

        }

 

        #endregion

 

        //////////////////////////////////////////////////////////////////////////////// Function

 

        #region 초기화 하기 - Initialize()

 

        /// <summary>

        /// 초기화 하기

        /// </summary>

        private void Initialize()

        {

            this.eventArrayList = ArrayList.Synchronized(new ArrayList(32));

 

            this.watcher.Created += watcher_Created;

            this.watcher.Changed += watcher_Created;

            this.watcher.Deleted += watcher_Created;

            this.watcher.Error   += watcher_Error;

            this.watcher.Renamed += watcher_Renamed;

 

            this.timer = new System.Timers.Timer(this.consolidationInterval);

 

            this.timer.Elapsed += timer_Elapsed;

 

            this.timer.AutoReset = true;

            this.timer.Enabled   = this.watcher.EnableRaisingEvents;

        }

 

        #endregion

        #region 초기화 취소하기 - Uninitialize()

 

        /// <summary>

        /// 초기화 취소하기

        /// </summary>

        private void Uninitialize()

        {

            if(this.watcher != null)

            {

                this.watcher.Dispose();

            }

 

            if(this.timer != null)

            {

                this.timer.Dispose();

            }

        }

 

        #endregion

    }

}

 

 

Program.cs

 

 

using System;

using System.IO;

 

namespace TestProject

{

    /// <summary>

    /// 프로그램

    /// </summary>

    class Program

    {

        //////////////////////////////////////////////////////////////////////////////////////////////////// Method

        ////////////////////////////////////////////////////////////////////////////////////////// Static

        //////////////////////////////////////////////////////////////////////////////// Private

        ////////////////////////////////////////////////////////////////////// Event

 

        #region 감시자 생성시 처리하기 - watcher_Created(sender, e)

 

        /// <summary>

        /// 감시자 생성시 처리하기

        /// </summary>

        /// <param name="sender">이벤트 발생자</param>

        /// <param name="e">이벤트 인자</param>

        private static void watcher_Created(object sender, FileSystemEventArgs e)

        {

            WriteLog("CREATED", e);

        }

 

        #endregion

        #region 감시자 변경시 처리하기 - watcher_Changed(sender, e)

 

        /// <summary>

        /// 감시자 변경시 처리하기

        /// </summary>

        /// <param name="sender">이벤트 발생자</param>

        /// <param name="e">이벤트 인자</param>

        private static void watcher_Changed(object sender, FileSystemEventArgs e)

        {

            WriteLog("CHANGED", e);

        }

 

        #endregion

        #region 감시자 삭제시 처리하기 - watcher_Deleted(sender, e)

 

        /// <summary>

        /// 감시자 삭제시 처리하기

        /// </summary>

        /// <param name="sender">이벤트 발생자</param>

        /// <param name="e">이벤트 인자</param>

        private static void watcher_Deleted(object sender, FileSystemEventArgs e)

        {

            WriteLog("DELETED", e);

        }

 

        #endregion

        #region 감시자 명칭 변경시 처리하기 - watcher_Renamed(sender, e)

 

        /// <summary>

        /// 감시자 명칭 변경시 처리하기

        /// </summary>

        /// <param name="sender">이벤트 발생자</param>

        /// <param name="e">이벤트 인자</param>

        private static void watcher_Renamed(object sender, RenamedEventArgs e)

        {

            WriteLog("RENAMED", e);

        }

 

        #endregion

 

        ////////////////////////////////////////////////////////////////////// Function

 

        #region 로그 작성하기 - WriteLog(message, e)

 

        /// <summary>

        /// 로그 작성하기

        /// </summary>

        /// <param name="message">메시지</param>

        /// <param name="e">이벤트 인자</param>

        private static void WriteLog(string message, FileSystemEventArgs e)

        {

            string timeStamp = DateTime.Now.ToString("HH:mm:ss");

 

            Console.WriteLine($"[{timeStamp}] {message}");

            Console.WriteLine("--------------------------------------------------");

            Console.WriteLine($"CHANGE TYPE : {e.ChangeType}");

            Console.WriteLine($"FILE NAME   : {e.Name      }");

            Console.WriteLine($"FULL PATH   : {e.FullPath  }");

            Console.WriteLine();

        }

 

        #endregion

        #region 로그 작성하기 - WriteLog(message, e)

 

        /// <summary>

        /// 로그 작성하기

        /// </summary>

        /// <param name="message">메시지</param>

        /// <param name="e">이벤트 인자</param>

        private static void WriteLog(string message, RenamedEventArgs e)

        {

            string timeStamp = DateTime.Now.ToString("HH:mm:ss");

 

            Console.WriteLine($"[{timeStamp}] {message}");

            Console.WriteLine("--------------------------------------------------");

            Console.WriteLine($"CHANGE TYPE   : {e.ChangeType }");

            Console.WriteLine($"FILE NAME     : {e.Name       }");

            Console.WriteLine($"FULL PATH     : {e.FullPath   }");

            Console.WriteLine($"OLD FILE NAME : {e.OldName    }");

            Console.WriteLine($"OLD FULL PATH : {e.OldFullPath}");

            Console.WriteLine();

        }

 

        #endregion

 

        #region 프로그램 시작하기 - Main()

 

        /// <summary>

        /// 프로그램 시작하기

        /// </summary>

        private static void Main()

        {

            SafeFileSystemWatcher watcher = new SafeFileSystemWatcher(@"C:\TEMP");

 

            watcher.NotifyFilter = NotifyFilters.FileName | NotifyFilters.CreationTime | NotifyFilters.Size | NotifyFilters.LastAccess | 

                NotifyFilters.LastWrite;

 

            watcher.IncludeSubdirectories = true;

            watcher.EnableRaisingEvents   = true;

 

            watcher.Created += watcher_Created;

            watcher.Deleted += watcher_Deleted;

            watcher.Changed += watcher_Changed;

            watcher.Renamed += watcher_Renamed;

 

            Console.WriteLine("프로그램을 종료하기 위해서 아무 키나 눌러 주시기 바랍니다.");

 

            Console.ReadKey(true);

        }

 

        #endregion

    }

}

 

----------------------------------------------------------------------------------------------------

Posted by 사용자 icodebroker
TAG

댓글을 달아 주세요