summaryrefslogtreecommitdiff
path: root/IvyFileMon
diff options
context:
space:
mode:
authorfcolin2007-02-01 13:29:31 +0000
committerfcolin2007-02-01 13:29:31 +0000
commitafe2e7dfc1388cad991e8d38dda7d648c137aa52 (patch)
tree92bf63d2b2b34a805927aa294c7c51912638f66a /IvyFileMon
parent0be65f8a110ee9bf5da9c93e0bd5b5b62b3bad0c (diff)
parent04c263c314499e38d64af9d4a1aa5e2b8d9d5ead (diff)
downloadivy-cplusplus-afe2e7dfc1388cad991e8d38dda7d648c137aa52.zip
ivy-cplusplus-afe2e7dfc1388cad991e8d38dda7d648c137aa52.tar.gz
ivy-cplusplus-afe2e7dfc1388cad991e8d38dda7d648c137aa52.tar.bz2
ivy-cplusplus-afe2e7dfc1388cad991e8d38dda7d648c137aa52.tar.xz
modif struct svnwindows@3001
Diffstat (limited to 'IvyFileMon')
-rw-r--r--IvyFileMon/C++/Bus/IvyFileMon/IvyFileMon.vssccbin0 -> 46 bytes
-rw-r--r--IvyFileMon/DelayedDirectoryChangeHandler.cpp1414
-rw-r--r--IvyFileMon/DelayedDirectoryChangeHandler.h336
-rw-r--r--IvyFileMon/DirectoryChanges.cpp2026
-rw-r--r--IvyFileMon/DirectoryChanges.h493
-rw-r--r--IvyFileMon/HistoryEdit.cpp82
-rw-r--r--IvyFileMon/HistoryEdit.h65
-rw-r--r--IvyFileMon/InstIvyFileMon/InstIvyFileMon.vdproj795
-rw-r--r--IvyFileMon/InstIvyFileMon/InstIvyFileMon.vdproj.vspscc10
-rw-r--r--IvyFileMon/IvyFileMon.cpp65
-rw-r--r--IvyFileMon/IvyFileMon.h31
-rw-r--r--IvyFileMon/IvyFileMon.rc215
-rw-r--r--IvyFileMon/IvyFileMon.sln53
-rw-r--r--IvyFileMon/IvyFileMon.vcproj207
-rw-r--r--IvyFileMon/IvyFileMon.vcproj.vspscc10
-rw-r--r--IvyFileMon/IvyFileMon.vsscc0
-rw-r--r--IvyFileMon/IvyFileMon.vssscc10
-rw-r--r--IvyFileMon/IvyFileMonDlg.cpp281
-rw-r--r--IvyFileMon/IvyFileMonDlg.h55
-rw-r--r--IvyFileMon/ParseCmdLine.cpp76
-rw-r--r--IvyFileMon/ParseCmdLine.h37
-rw-r--r--IvyFileMon/ReadMe.txt87
-rw-r--r--IvyFileMon/res/IvyFileMon.icobin0 -> 21630 bytes
-rw-r--r--IvyFileMon/res/IvyFileMon.manifest22
-rw-r--r--IvyFileMon/res/IvyFileMon.rc213
-rw-r--r--IvyFileMon/resource.h28
-rw-r--r--IvyFileMon/stdafx.cpp7
-rw-r--r--IvyFileMon/stdafx.h42
28 files changed, 6460 insertions, 0 deletions
diff --git a/IvyFileMon/C++/Bus/IvyFileMon/IvyFileMon.vsscc b/IvyFileMon/C++/Bus/IvyFileMon/IvyFileMon.vsscc
new file mode 100644
index 0000000..8fdac15
--- /dev/null
+++ b/IvyFileMon/C++/Bus/IvyFileMon/IvyFileMon.vsscc
Binary files differ
diff --git a/IvyFileMon/DelayedDirectoryChangeHandler.cpp b/IvyFileMon/DelayedDirectoryChangeHandler.cpp
new file mode 100644
index 0000000..1c4daf0
--- /dev/null
+++ b/IvyFileMon/DelayedDirectoryChangeHandler.cpp
@@ -0,0 +1,1414 @@
+// DelayedDirectoryChangeHandler.cpp: implementation of the CDelayedDirectoryChangeHandler2 class.
+//
+//////////////////////////////////////////////////////////////////////
+
+#include "stdafx.h"
+#include "DirectoryChanges.h"
+#include "DelayedDirectoryChangeHandler.h"
+#include <process.h>//for _beginthreadex
+
+#include <shlwapi.h> // for PathMatchSpec
+#pragma comment( lib, "shlwapi.lib") // function
+
+
+#ifdef _DEBUG
+#undef THIS_FILE
+static char THIS_FILE[]=__FILE__;
+#define new DEBUG_NEW
+#endif
+
+#define UWM_DELAYED_DIRECTORY_NOTIFICATION (WM_APP+1024)
+
+
+HINSTANCE GetInstanceHandle()
+{
+ return (HINSTANCE)GetModuleHandle(NULL);
+ // ASSERT( AfxGetInstanceHandle() == (HINSTANCE)GetModuleHandle(NULL) ); <-- true for building .exe's
+ //NOTE: In Dll's using shared MFC, AfxGetInstanceHandle() != (HINSTANCE)GetModuleHandle(NULL)...
+ //don't know if this is the case for dll's using static MFC
+}
+static inline bool IsEmptyString(LPCTSTR sz)
+{
+ return (bool)(sz==NULL || *sz == 0);
+}
+/*********************************************************
+ PathMatchSpec() requires IE 4.0 or greater on NT...
+ if running on NT 4.0 w/ out IE 4.0, then uses this function instead.
+
+ Based on code by Jack Handy:
+ http://www.codeproject.com/string/wildcmp.asp
+
+ Changed slightly to match the PathMatchSpec signature, be unicode compliant & to ignore case by myself.
+
+*********************************************************/
+
+#define _TESTING_WILDCMP_ONLY_
+
+BOOL STDAPICALLTYPE wildcmp(LPCTSTR string, LPCTSTR wild )
+{
+ const TCHAR *cp, *mp;
+ cp = mp = NULL;
+
+ while ((*string) && (*wild != _T('*')))
+ {
+ if ((_toupper(*wild) != _toupper(*string)) && (*wild != _T('?')))
+ {
+ return FALSE;
+ }
+ wild++;
+ string++;
+ }
+
+ while (*string)
+ {
+ if (*wild == _T('*'))
+ {
+ if (!*++wild)
+ {
+ return TRUE;
+ }
+ mp = wild;
+ cp = string+1;
+ }
+ else
+ if ((_toupper(*wild) == _toupper(*string)) || (*wild == _T('?')))
+ {
+ wild++;
+ string++;
+ }
+ else
+ {
+ wild = mp;
+ string = cp++;
+ }
+ }
+
+ while (*wild == _T('*'))
+ {
+ wild++;
+ }
+ return (!*wild)? TRUE : FALSE;
+}
+
+//////////////////////////////////////////////////////////////////////////
+//
+//CDirChangeNotification member functions:
+//
+CDirChangeNotification::CDirChangeNotification(CDelayedDirectoryChangeHandler * pDelayedHandler, DWORD dwPartialPathOffset)
+:m_pDelayedHandler( pDelayedHandler )
+,m_szFileName1(NULL)
+,m_szFileName2(NULL)
+,m_dwError(0UL)
+,m_dwPartialPathOffset(dwPartialPathOffset)
+{
+ ASSERT( pDelayedHandler );
+}
+
+CDirChangeNotification::~CDirChangeNotification()
+{
+ if( m_szFileName1 ) free(m_szFileName1), m_szFileName1 = NULL;
+ if( m_szFileName2 ) free(m_szFileName2), m_szFileName2 = NULL;
+}
+
+void CDirChangeNotification::DispatchNotificationFunction()
+{
+ ASSERT( m_pDelayedHandler );
+ if( m_pDelayedHandler )
+ m_pDelayedHandler->DispatchNotificationFunction( this );
+}
+
+void CDirChangeNotification::PostOn_FileAdded(LPCTSTR szFileName)
+{
+ ASSERT( szFileName );
+ m_eFunctionToDispatch = eOn_FileAdded;
+ m_szFileName1 = _tcsdup( szFileName) ;
+ //
+ // post the message so it'll be dispatch by another thread.
+ PostNotification();
+
+}
+void CDirChangeNotification::PostOn_FileRemoved(LPCTSTR szFileName)
+{
+ ASSERT( szFileName );
+ m_eFunctionToDispatch = eOn_FileRemoved;
+ m_szFileName1 = _tcsdup( szFileName) ;
+ //
+ // post the message so it'll be dispatched by another thread.
+ PostNotification();
+
+}
+void CDirChangeNotification::PostOn_FileNameChanged(LPCTSTR szOldName, LPCTSTR szNewName)
+{
+ ASSERT( szOldName && szNewName );
+
+ m_eFunctionToDispatch = eOn_FileNameChanged;
+ m_szFileName1 = _tcsdup( szOldName) ;
+ m_szFileName2 = _tcsdup( szNewName) ;
+ //
+ // post the message so it'll be dispatched by another thread.
+ PostNotification();
+
+}
+
+void CDirChangeNotification::PostOn_FileModified(LPCTSTR szFileName)
+{
+ ASSERT( szFileName );
+
+ m_eFunctionToDispatch = eOn_FileModified;
+ m_szFileName1 = _tcsdup( szFileName );
+ //
+ // post the message so it'll be dispatched by another thread.
+ PostNotification();
+}
+
+void CDirChangeNotification::PostOn_ReadDirectoryChangesError(DWORD dwError, LPCTSTR szDirectoryName)
+{
+ ASSERT( szDirectoryName );
+
+ m_eFunctionToDispatch = eOn_ReadDirectoryChangesError;
+ m_dwError = dwError;
+ m_szFileName1 = _tcsdup(szDirectoryName);
+ //
+ // post the message so it'll be dispatched by the another thread.
+ PostNotification();
+
+}
+
+void CDirChangeNotification::PostOn_WatchStarted(DWORD dwError, LPCTSTR szDirectoryName)
+{
+ ASSERT( szDirectoryName );
+
+ m_eFunctionToDispatch = eOn_WatchStarted;
+ m_dwError = dwError;
+ m_szFileName1 = _tcsdup(szDirectoryName);
+
+ PostNotification();
+}
+
+void CDirChangeNotification::PostOn_WatchStopped(LPCTSTR szDirectoryName)
+{
+ ASSERT( szDirectoryName );
+
+ m_eFunctionToDispatch = eOn_WatchStopped;
+ m_szFileName1 = _tcsdup(szDirectoryName);
+
+ PostNotification();
+}
+
+void CDirChangeNotification::PostNotification()
+{
+ ASSERT( m_pDelayedHandler );
+ if( m_pDelayedHandler )
+ m_pDelayedHandler->PostNotification( this );
+}
+
+static LRESULT CALLBACK DelayedNotificationWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
+//
+// This is the wndproc for the notification window
+//
+// it's here to dispatch the notifications to the client
+//
+{
+ if( message == UWM_DELAYED_DIRECTORY_NOTIFICATION )
+ {
+ CDirChangeNotification * pNotification = reinterpret_cast<CDirChangeNotification*>(lParam);
+ ASSERT( pNotification );
+ if( pNotification )
+ {
+ DWORD dwEx(0);
+ __try{
+ pNotification->DispatchNotificationFunction();
+ }
+ __except(dwEx = GetExceptionCode(), EXCEPTION_EXECUTE_HANDLER){
+ //An exception was raised:
+ //
+ // Likely cause: there was a problem creating the CDelayedDirectoryChangeHandler::m_hWatchStoppedDispatchedEvent object
+ // and the change handler object was deleted before the notification could be dispatched to this function.
+ //
+ // or perhaps, somebody's implementation of an overridden function caused an exception
+ TRACE(_T("Following exception occurred: %d -- File: %s Line: %d\n"), dwEx, _T(__FILE__), __LINE__);
+ }
+ }
+
+ return 0UL;
+ }
+ else
+ return DefWindowProc(hWnd,message,wParam,lParam);
+}
+
+//////////////////////////////////////////////////////////////////////
+// Construction/Destruction
+//////////////////////////////////////////////////////////////////////
+//
+//CDelayedNotificationWindow static member vars:
+//
+long CDelayedNotificationWindow::s_nRefCnt = 0L;
+HWND CDelayedNotificationWindow::s_hWnd = NULL;
+BOOL CDelayedNotificationWindow::s_bRegisterWindow = FALSE;
+//
+//
+long CDelayedNotificationWindow::AddRef()//creates window for first time if necessary
+{
+ if( InterlockedIncrement(&s_nRefCnt) == 1
+ || !::IsWindow( s_hWnd ) )
+ {
+ TRACE(_T("CDelayedNotificationWindow -- Creating the notification window\n"));
+ VERIFY( CreateNotificationWindow() );
+ }
+ return s_nRefCnt;
+}
+
+long CDelayedNotificationWindow::Release()//destroys window for last time if necessary
+{
+ long nRefCnt = -1;
+ if( (nRefCnt = InterlockedDecrement(&s_nRefCnt)) == 0 )
+ {
+ //no body else using the window so destroy it?
+ TRACE(_T("CDelayedNotificationWindow -- Destroying the notification window\n"));
+ DestroyWindow( s_hWnd );
+ s_hWnd = NULL;
+ }
+ return nRefCnt;
+}
+BOOL CDelayedNotificationWindow::RegisterWindowClass(LPCTSTR szClassName)
+//
+// registers our own window class to use as the hidden notification window.
+//
+{
+ WNDCLASS wc = {0};
+
+ wc.style = 0;
+ wc.hInstance = GetInstanceHandle();
+ wc.lpszClassName = szClassName;
+ wc.hbrBackground = (HBRUSH)GetStockObject( WHITE_BRUSH );
+ wc.lpfnWndProc = DelayedNotificationWndProc;
+
+ ATOM ant = RegisterClass( &wc );
+ if( ant == NULL )
+ {
+ TRACE(_T("CDirChangeNotification::RegisterWindowClass - RegisterClass failed: %d\n"), GetLastError());
+ }
+ return (BOOL)(ant!= NULL);
+
+}
+
+BOOL CDelayedNotificationWindow::CreateNotificationWindow()
+//
+// Create the hidden notification windows.
+//
+{
+ TCHAR szClassName[] = _T("Delayed_Message_Sender");
+ if( !s_bRegisterWindow )
+ s_bRegisterWindow = RegisterWindowClass(szClassName);
+ s_hWnd = CreateWindowEx(0, szClassName, _T("DelayedWnd"),0,0,0,0,0, NULL, 0,
+ GetInstanceHandle(), NULL);
+ if( s_hWnd == NULL )
+ {
+ TRACE(_T("Unable to create notification window! GetLastError(): %d\n"), GetLastError());
+ TRACE(_T("File: %s Line: %d\n"), _T(__FILE__), __LINE__);
+ }
+
+ return (BOOL)(s_hWnd != NULL);
+}
+void CDelayedNotificationWindow::PostNotification(CDirChangeNotification * pNotification)
+//
+// Posts a message to a window created in the main
+// thread.
+// The main thread catches this message, and dispatches it in
+// the context of the main thread.
+//
+{
+ ASSERT( pNotification );
+ ASSERT( s_hWnd );
+ ASSERT( ::IsWindow( s_hWnd ) );
+
+ PostMessage(s_hWnd,
+ UWM_DELAYED_DIRECTORY_NOTIFICATION,
+ 0,
+ reinterpret_cast<LPARAM>( pNotification ));
+
+// if you don't want the notification delayed,
+//
+// if( false )
+// {
+// pNotification->DispatchNotificationFunction();
+// }
+}
+
+/////////////////////////////////////////////////////////
+// CDelayedNoticationThread
+//
+long CDelayedNotificationThread::s_nRefCnt = 0L;
+HANDLE CDelayedNotificationThread::s_hThread = NULL;
+DWORD CDelayedNotificationThread::s_dwThreadID = 0UL;
+
+void CDelayedNotificationThread::PostNotification(CDirChangeNotification * pNotification)
+{
+ ASSERT( s_hThread != NULL );
+ ASSERT( s_dwThreadID != 0 );
+
+ if(
+ !PostThreadMessage(s_dwThreadID,
+ UWM_DELAYED_DIRECTORY_NOTIFICATION,
+ 0,
+ reinterpret_cast<LPARAM>(pNotification))
+ )
+ {
+ //Note, this can sometimes fail.
+ //Will fail if: s_dwThreadID references a invalid thread id(the thread has died for example)
+ // OR will fail if the thread doesn't have a message queue.
+ //
+ // This was failing because the thread had not been fully started by the time PostThreadMessage had been called
+ //
+ //Note: if this fails, it creates a memory leak because
+ //the CDirChangeNotification object that was allocated and posted
+ //to the thread is actually never going to be dispatched and then deleted.... it's
+ //hanging in limbo.....
+
+ //
+ // The fix for this situation was to force the thread that starts
+ // this worker thread to wait until the worker thread was fully started before
+ // continueing. accomplished w/ an event... also.. posting a message to itself before signalling the
+ // 'spawning' thread that it was started ensured that there was a message pump
+ // associated w/ the worker thread by the time PostThreadMessage was called.
+ TRACE(_T("PostThreadMessage() failed while posting to thread id: %d! GetLastError(): %d%s\n"), s_dwThreadID, GetLastError(), GetLastError() == ERROR_INVALID_THREAD_ID? _T("(ERROR_INVALID_THREAD_ID)") : _T(""));
+ }
+}
+
+bool CDelayedNotificationThread::StartThread()
+{
+ TRACE(_T("CDelayedNotificationThread::StartThread()\n"));
+ ASSERT( s_hThread == NULL
+ && s_dwThreadID == 0 );
+ s_hThread = (HANDLE)_beginthreadex(NULL,0,
+ ThreadFunc, this, 0, (UINT*) &s_dwThreadID);
+ if( s_hThread )
+ WaitForThreadStartup();
+
+ return s_hThread == NULL ? false : true;
+
+}
+
+bool CDelayedNotificationThread::StopThread()
+{
+ TRACE(_T("CDelayedNotificationThread::StopThread()\n"));
+ if( s_hThread != NULL
+ && s_dwThreadID != 0 )
+ {
+ PostThreadMessage(s_dwThreadID, WM_QUIT, 0,0);
+
+ WaitForSingleObject(s_hThread, INFINITE);
+ CloseHandle(s_hThread);
+ s_hThread = NULL;
+ s_dwThreadID = 0UL;
+ return true;
+ }
+ return true;//already shutdown
+}
+
+UINT __stdcall CDelayedNotificationThread::ThreadFunc(LPVOID lpvThis)
+{
+ //UNREFERENCED_PARAMETER( lpvThis );
+ //
+ // Implements a simple message pump
+ //
+ CDelayedNotificationThread * pThis = reinterpret_cast<CDelayedNotificationThread*>(lpvThis);
+ ASSERT( pThis );
+
+ //
+ // Insure that this thread has a message queue by the time another
+ // thread gets control and tries to use PostThreadMessage
+ // problems can happen if someone tries to use PostThreadMessage
+ // in between the time pThis->SignalThreadStartup() is called,
+ // and the first call to GetMessage();
+
+ ::PostMessage(NULL, WM_NULL, 0,0);//if this thread didn't have a message queue before this, it does now.
+
+
+ //
+ //
+ // Signal that this thread has started so that StartThread can continue.
+ //
+ if( pThis ) pThis->SignalThreadStartup();
+
+ TRACE(_T("CDelayedNotificationThread::ThreadFunc() ThreadID: %d -- Starting\n"), GetCurrentThreadId());
+ MSG msg;
+ do{
+ while( GetMessage(&msg, NULL, 0,0) )//note GetMessage() can return -1, but only if i give it a bad HWND.(HWND for another thread for example)..i'm not giving an HWND, so no problemo here.
+ {
+ if( msg.message == UWM_DELAYED_DIRECTORY_NOTIFICATION )
+ {
+ CDirChangeNotification * pNotification =
+ reinterpret_cast<CDirChangeNotification *>( msg.lParam );
+ DWORD dwEx(0UL);
+
+ __try{
+ if( pNotification )
+ pNotification->DispatchNotificationFunction();
+ }
+ __except(dwEx = GetExceptionCode(), EXCEPTION_EXECUTE_HANDLER){
+ //An exception was raised:
+ //
+ // Likely causes:
+ // * There was a problem creating the CDelayedDirectoryChangeHandler::m_hWatchStoppedDispatchedEvent object
+ // and the change handler object was deleted before the notification could be dispatched to this function.
+ //
+ // * Somebody's implementation of an overridden virtual function caused an exception
+ TRACE(_T("The following exception occurred: %d -- File: %s Line: %d\n"), dwEx, _T(__FILE__), __LINE__);
+ }
+ }
+ else
+ if( msg.message == WM_QUIT )
+ {
+ break;
+ }
+ }
+ }while( msg.message != WM_QUIT );
+ TRACE(_T("CDelayedNotificationThread::ThreadFunc() exiting. ThreadID: %d\n"), GetCurrentThreadId());
+ return 0;
+}
+
+long CDelayedNotificationThread::AddRef()
+{
+ if( InterlockedIncrement(&s_nRefCnt) == 1 )
+ {
+ VERIFY( StartThread() );
+ }
+ return s_nRefCnt;
+}
+long CDelayedNotificationThread::Release()
+{
+ if( InterlockedDecrement(&s_nRefCnt) <= 0 )
+ {
+ s_nRefCnt = 0;
+ VERIFY( StopThread() );
+ }
+ return s_nRefCnt;
+}
+
+///////////////////////////////////////////////////////
+//static member data for CDelayedDirectoryChangeHandler
+HINSTANCE CDelayedDirectoryChangeHandler::s_hShlwapi_dll = NULL;//for the PathMatchSpec() function
+BOOL CDelayedDirectoryChangeHandler::s_bShlwapi_dllExists = TRUE;
+long CDelayedDirectoryChangeHandler::s_nRefCnt_hShlwapi = 0L;
+FUNC_PatternMatchSpec CDelayedDirectoryChangeHandler::s_fpPatternMatchSpec = wildcmp;//default
+///////////////////////////////////////////////////////
+//construction destruction
+CDelayedDirectoryChangeHandler::CDelayedDirectoryChangeHandler(CDirectoryChangeHandler * pRealHandler, bool bAppHasGUI, LPCTSTR szIncludeFilter, LPCTSTR szExcludeFilter, DWORD dwFilterFlags)
+: m_pDelayNotifier( NULL )
+ ,m_pRealHandler( pRealHandler )
+ ,m_szIncludeFilter(NULL)
+ ,m_szExcludeFilter(NULL)
+ ,m_dwFilterFlags( dwFilterFlags )
+ ,m_dwPartialPathOffset( 0UL )
+ ,m_hWatchStoppedDispatchedEvent(NULL)
+ ,m_nNumIncludeFilterSpecs(0)
+ ,m_nNumExcludeFilterSpecs(0)
+{
+
+
+ ASSERT( m_pRealHandler );
+
+ InitializePathMatchFunc( szIncludeFilter, szExcludeFilter );
+
+ //
+ // See that we're
+ //
+
+
+ m_hWatchStoppedDispatchedEvent = ::CreateEvent(NULL, FALSE, FALSE, NULL);//AUTO-RESET, not initially signalled
+ ASSERT( m_hWatchStoppedDispatchedEvent );
+
+ if( bAppHasGUI )
+ {
+ //
+ // The value true was passed to the CDirectoryChangeWatcher constructor.
+ // It's assumed that your app has a gui, that is, it implements
+ // a message pump. To delay the notification to another thread,
+ // we'll use a hidden notification window.
+ //
+ m_pDelayNotifier = new CDelayedNotificationWindow();
+ }
+ else
+ {
+ // The value 'false' was passed to the CDirectoryChangeWatcher constructor.
+ //
+ // Your app has no message pump... use a class that implements one for you
+ // in a worker thread.
+ //
+ // Notifications will be executed in this worker thread.
+ //
+ m_pDelayNotifier = new CDelayedNotificationThread();
+ }
+}
+
+CDelayedDirectoryChangeHandler::~CDelayedDirectoryChangeHandler()
+{
+ if( m_pRealHandler )
+ delete m_pRealHandler, m_pRealHandler = NULL;
+ if( m_pDelayNotifier )
+ delete m_pDelayNotifier, m_pDelayNotifier = NULL;
+
+ if( m_hWatchStoppedDispatchedEvent )
+ CloseHandle(m_hWatchStoppedDispatchedEvent), m_hWatchStoppedDispatchedEvent = NULL;
+
+ if( m_szIncludeFilter ){
+ if( m_nNumIncludeFilterSpecs == 1 )
+ free(m_szIncludeFilter);
+ else
+ {
+ TCHAR ** ppTmp = (TCHAR**)m_szIncludeFilter;
+ for(int i(0); i < m_nNumIncludeFilterSpecs; ++i)
+ {
+ free( *ppTmp++ );
+ }
+ free( m_szIncludeFilter );
+ }
+ m_szIncludeFilter = NULL;
+ m_nNumIncludeFilterSpecs;
+ }
+ if( m_szExcludeFilter ) {
+ if( m_nNumExcludeFilterSpecs == 1 )
+ free(m_szExcludeFilter);
+ else{
+ TCHAR ** ppTmp = (TCHAR**)m_szExcludeFilter;
+ for(int i(0); i < m_nNumExcludeFilterSpecs; ++i)
+ {
+ free( *ppTmp++ );
+ }
+ free( m_szExcludeFilter );
+ }
+ m_szExcludeFilter = NULL;
+ m_nNumExcludeFilterSpecs = 0;
+ }
+
+ UninitializePathMatchFunc();
+}
+
+BOOL CDelayedDirectoryChangeHandler::_PathMatchSpec(LPCTSTR szPath, LPCTSTR szPattern)
+{
+ if( s_fpPatternMatchSpec )
+ {
+ return s_fpPatternMatchSpec(szPath, szPattern);
+ }
+ ASSERT( FALSE );
+ return TRUE;//everything matches.
+}
+
+BOOL CDelayedDirectoryChangeHandler::InitializePathMatchFunc(LPCTSTR szIncludeFilter, LPCTSTR szExcludeFilter)
+//
+//
+// To support the Include and Exclude filters, the PathMatchSpec function is used.
+// PathMatchSpec is only supported on NT4.0 if IE 4.0 is installed.
+//
+// for the case where this code is running on NT 4.0 w/out IE 4.0, we use
+// a different function: wildcmp ()
+//
+//
+// This function attempts to load shlwapi.dll dynamically and find the PathMatchSpec function.
+//
+// if the function PathMatchSpec can't be found, the function pointer s_fpPathMatchSpec is set to wildcmp.
+//
+//
+// Note: wildcmp doesn't support multiple file specs separated by a semi-colon
+// as PathMatchSpec does.... we'll support it by parsing them
+// when we want to test the filters, we'll call the pattern matching functions multiple times...
+//
+{
+
+ //
+ // Copy the include/exclude filters if specified...
+ //
+ //
+ if( IsEmptyString(szIncludeFilter)
+ && IsEmptyString(szExcludeFilter) )
+ {
+ return TRUE;//both the include && exclude filters aren't specified
+ //no need to initialize the pattern matching function....
+ //if filters are never used, then
+ //one less dll is loaded.
+ }
+
+#ifdef _TESTING_WILDCMP_ONLY_
+ s_hShlwapi_dll = NULL;
+ s_bShlwapi_dllExists = FALSE;
+ return InitializePatterns(szIncludeFilter, szExcludeFilter);
+#endif
+
+
+ if( s_hShlwapi_dll != NULL )
+ {
+ ASSERT( s_fpPatternMatchSpec != NULL );
+ InterlockedIncrement(&s_nRefCnt_hShlwapi);
+
+ return InitializePatterns(szIncludeFilter, szExcludeFilter);
+ }
+ else{
+ if( s_bShlwapi_dllExists == TRUE )//either the dll exists, or we haven't tried loading it yet...
+ {
+ //The pattern match function hasn't been initialized yet....
+ //
+
+ s_hShlwapi_dll = ::LoadLibrary(_T("Shlwapi.dll"));
+ if( s_hShlwapi_dll == NULL )
+ {
+ s_bShlwapi_dllExists = FALSE;//don't try loading this dll again.
+ s_fpPatternMatchSpec = wildcmp;//even though it's set buy default, and this code will only get here once, set it just for fun.
+
+ return InitializePatterns(szIncludeFilter, szExcludeFilter);
+
+ }
+ else
+ {
+ //Shlwapi.dll was found....check it for PathMatchSpec()
+#ifdef UNICODE
+ s_fpPatternMatchSpec = (FUNC_PatternMatchSpec)::GetProcAddress(s_hShlwapi_dll, "PathMatchSpecW");
+#else
+ s_fpPatternMatchSpec = (FUNC_PatternMatchSpec)::GetProcAddress(s_hShlwapi_dll, "PathMatchSpecA");
+#endif
+
+ if( s_fpPatternMatchSpec != NULL )
+ {
+ //UsesRealPathMatchSpec() will now return true.
+ //we're on NT w/ IE 4.0 or greater...(or Win2k/XP)
+ InterlockedIncrement(&s_nRefCnt_hShlwapi);
+ return InitializePatterns(szIncludeFilter, szExcludeFilter);
+ }
+ else
+ {
+ //we found shlwapi.dll, but it didn't have PathMatchSpec()
+ ::FreeLibrary( s_hShlwapi_dll );
+ s_hShlwapi_dll = NULL;
+ s_bShlwapi_dllExists = FALSE;
+
+ //instead of using PathMatchSpec()
+ //we'll use wildcmp()
+ s_fpPatternMatchSpec = wildcmp;
+ //UsesRealPathMatchSpec() will now return false w/out asserting.
+
+ return InitializePatterns(szIncludeFilter, szExcludeFilter);
+ }
+ }
+
+ }
+ }
+ return (s_fpPatternMatchSpec != NULL);
+}
+
+BOOL CDelayedDirectoryChangeHandler::InitializePatterns(LPCTSTR szIncludeFilter, LPCTSTR szExcludeFilter)
+{
+ ASSERT( !IsEmptyString(szIncludeFilter) //one of these must have something in it,
+ || !IsEmptyString(szExcludeFilter) );//or else this function shouldn't be called.
+
+ if( s_hShlwapi_dll != NULL )
+ {
+ //we're using Shlwapi.dll's PathMatchSpec function....
+ //we're running on NT4.0 w/ IE 4.0 installed, or win2k/winXP(or greater)
+ //
+ // Copy the include/exclude filters if specified...
+ //
+ //
+ // we're using the real PathMatchSpec() function which
+ // supports multiple pattern specs...(separated by a semi-colon)
+ // so there's only one filter spec as far as my code is concerned.
+ //
+ if( !IsEmptyString(szIncludeFilter) )
+ {
+ m_szIncludeFilter = _tcsdup(szIncludeFilter);
+ ASSERT( m_szIncludeFilter );
+ m_nNumIncludeFilterSpecs = 1;
+ }
+ if( !IsEmptyString(szExcludeFilter) )
+ {
+ m_szExcludeFilter = _tcsdup(szExcludeFilter);
+ ASSERT( m_szExcludeFilter );
+ m_nNumExcludeFilterSpecs = 1;
+ }
+ }
+ else
+ {
+ //shlwapi.dll isn't on this machine.... can happen on NT4.0 w/ out IE 4.0 installed.
+ ASSERT( s_bShlwapi_dllExists == FALSE );
+
+ //
+ // we're using the function wildcmp() instead of PathMatchSpec..
+ //
+ // this means that multiple pattern specs aren't supported...
+ // in order to support them, we'll tokenize the string into multiple
+ // pattern specs and test the string multiple times(once per pattern spec)
+ // in order to support multiple patterns.
+ //
+ //
+ // m_szIncludeFilter & m_szExclude filter will be used like TCHAR**'s instead of TCHAR*'s
+ //
+
+ m_nNumIncludeFilterSpecs = 0;
+ if( !IsEmptyString(szIncludeFilter) )
+ {
+ TCHAR * szTmpFilter = _tcsdup(szIncludeFilter);
+ TCHAR * pTok = _tcstok( szTmpFilter, _T(";"));
+ while( pTok )
+ {
+ m_nNumIncludeFilterSpecs++;
+ pTok = _tcstok(NULL, _T(";"));
+ }
+ if( m_nNumIncludeFilterSpecs == 1 )
+ m_szIncludeFilter = _tcsdup(szIncludeFilter);
+ else
+ { //allocate room for pointers .. one for each token...
+ m_szIncludeFilter = (TCHAR*)malloc( m_nNumIncludeFilterSpecs * sizeof(TCHAR*));
+
+ free(szTmpFilter);
+ szTmpFilter = _tcsdup(szIncludeFilter);
+ pTok = _tcstok(szTmpFilter, _T(";"));
+ TCHAR ** ppTmp = (TCHAR**)m_szIncludeFilter;
+ while(pTok)
+ {
+ *ppTmp = _tcsdup(pTok);
+ ppTmp++;
+ pTok = _tcstok(NULL, _T(";"));
+ }
+ }
+
+ free(szTmpFilter);
+ }
+
+ //
+ // Do the same for the Exclude filter...
+ //
+ m_nNumExcludeFilterSpecs = 0;
+ if( !IsEmptyString(szExcludeFilter) )
+ {
+ TCHAR * szTmpFilter = _tcsdup(szExcludeFilter);
+ TCHAR * pTok = _tcstok( szTmpFilter, _T(";"));
+ while( pTok )
+ {
+ m_nNumExcludeFilterSpecs++;
+ pTok = _tcstok(NULL, _T(";"));
+ }
+ if( m_nNumExcludeFilterSpecs == 1 )
+ m_szExcludeFilter = _tcsdup(szExcludeFilter);
+ else
+ { //allocate room for pointers .. one for each token...
+ m_szExcludeFilter = (TCHAR*)malloc( m_nNumExcludeFilterSpecs * sizeof(TCHAR*));
+
+ free(szTmpFilter);
+ szTmpFilter = _tcsdup(szExcludeFilter);
+
+ pTok = _tcstok(szTmpFilter, _T(";"));
+ TCHAR ** ppTmp = (TCHAR**)m_szExcludeFilter;
+ while(pTok)
+ {
+ *ppTmp = _tcsdup(pTok);
+ ppTmp++;
+ pTok = _tcstok(NULL, _T(";"));
+ }
+ }
+ free(szTmpFilter);
+ }
+
+ }
+
+ return (m_szExcludeFilter!= NULL || (m_szIncludeFilter != NULL));
+}
+void CDelayedDirectoryChangeHandler::UninitializePathMatchFunc()
+{
+ if( s_bShlwapi_dllExists == TRUE
+ && s_hShlwapi_dll != NULL )
+ {
+ if( InterlockedDecrement(&s_nRefCnt_hShlwapi) <= 0)
+ {
+ s_nRefCnt_hShlwapi = 0;
+ FreeLibrary( s_hShlwapi_dll );
+ s_hShlwapi_dll = NULL;
+ s_fpPatternMatchSpec = wildcmp;
+ }
+ }
+}
+
+bool CDelayedDirectoryChangeHandler::UsesRealPathMatchSpec() const
+//are we using PathMatchSpec() or wildcmp()?
+{
+ if( s_hShlwapi_dll != NULL && s_fpPatternMatchSpec != NULL )
+ return true;
+ if( s_hShlwapi_dll == NULL && s_fpPatternMatchSpec != NULL )
+ return false;
+
+ ASSERT( FALSE );//this function was called before InitializePathMatchFunc()
+ //oops!
+ return false;
+}
+static inline bool HasTrailingBackslash(const CString & str )
+{
+ if( str.GetLength() > 0
+ && str[ str.GetLength() - 1 ] == _T('\\') )
+ return true;
+ return false;
+}
+void CDelayedDirectoryChangeHandler::SetPartialPathOffset(const CString & strWatchedDirName)
+{
+ if( m_dwFilterFlags & CDirectoryChangeWatcher::FILTERS_CHECK_PARTIAL_PATH )
+ {
+ //set the offset to
+ if( HasTrailingBackslash( strWatchedDirName ) )
+ m_dwPartialPathOffset = strWatchedDirName.GetLength();
+ else
+ m_dwPartialPathOffset = strWatchedDirName.GetLength() + 1;
+ }
+ else
+ m_dwPartialPathOffset = 0;
+}
+
+CDirChangeNotification * CDelayedDirectoryChangeHandler::GetNotificationObject()
+//
+// Maybe in future I'll keep a pool of these
+// objects around to increase performance...
+// using objects from a cache will be faster
+// than allocated and destroying a new one each time.
+//
+//
+{
+ ASSERT( m_pRealHandler );
+ return new CDirChangeNotification(this, m_dwPartialPathOffset);//helps support FILTERS_CHECK_PARTIAL_PATH
+}
+
+void CDelayedDirectoryChangeHandler::DisposeOfNotification(CDirChangeNotification * pNotification)
+{
+ delete pNotification;
+}
+
+//These functions are called when the directory to watch has had a change made to it
+void CDelayedDirectoryChangeHandler::On_FileAdded(const CString & strFileName)
+{
+ CDirChangeNotification * p = GetNotificationObject();
+ ASSERT( p );
+ if( p ) p->PostOn_FileAdded( strFileName );
+}
+
+void CDelayedDirectoryChangeHandler::On_FileRemoved(const CString & strFileName)
+{
+ CDirChangeNotification * p = GetNotificationObject();
+ ASSERT( p );
+ if( p ) p->PostOn_FileRemoved( strFileName );
+}
+
+void CDelayedDirectoryChangeHandler::On_FileModified(const CString & strFileName)
+{
+ CDirChangeNotification * p = GetNotificationObject();
+ ASSERT( p );
+ if( p ) p->PostOn_FileModified( strFileName );
+}
+
+void CDelayedDirectoryChangeHandler::On_FileNameChanged(const CString & strOldFileName, const CString & strNewFileName)
+{
+ CDirChangeNotification * p = GetNotificationObject();
+ ASSERT( p );
+ if( p ) p->PostOn_FileNameChanged( strOldFileName, strNewFileName );
+}
+
+void CDelayedDirectoryChangeHandler::On_ReadDirectoryChangesError(DWORD dwError, const CString & strDirectoryName)
+{
+ CDirChangeNotification * p = GetNotificationObject();
+ ASSERT( p );
+ if( p ) p->PostOn_ReadDirectoryChangesError( dwError, strDirectoryName );
+}
+
+void CDelayedDirectoryChangeHandler::On_WatchStarted(DWORD dwError, const CString & strDirectoryName)
+{
+ if( !(m_dwFilterFlags & CDirectoryChangeWatcher::FILTERS_NO_WATCHSTART_NOTIFICATION))
+ {
+ CDirChangeNotification * p = GetNotificationObject();
+
+ if( p ) p->PostOn_WatchStarted(dwError, strDirectoryName);
+ }
+}
+
+void CDelayedDirectoryChangeHandler::On_WatchStopped(const CString & strDirectoryName)
+{
+ if( !(m_dwFilterFlags & CDirectoryChangeWatcher::FILTERS_NO_WATCHSTOP_NOTIFICATION))
+ {
+ CDirChangeNotification * p = GetNotificationObject();
+
+ if( p ){
+ if( m_hWatchStoppedDispatchedEvent )
+ ::ResetEvent(m_hWatchStoppedDispatchedEvent);
+
+ p->PostOn_WatchStopped( strDirectoryName );
+
+ // Wait that this function has been dispatched to the other thread
+ // before continueing. This object may be getting deleted
+ // soon after this function returns, and before the function can be
+ // dispatched to the other thread....
+ WaitForOnWatchStoppedDispatched();
+ }
+ }
+}
+
+
+void CDelayedDirectoryChangeHandler::PostNotification(CDirChangeNotification * pNotification)
+{
+ if( m_pDelayNotifier )
+ m_pDelayNotifier->PostNotification( pNotification );
+}
+
+inline bool IsNonFilterableEvent( CDirChangeNotification::eFunctionToDispatch eEvent)
+// Helper function
+// For filtering events..... these functions can not be filtered out.
+//
+{
+ if( eEvent == CDirChangeNotification::eOn_WatchStarted
+ || eEvent == CDirChangeNotification::eOn_WatchStopped
+ || eEvent == CDirChangeNotification::eOn_ReadDirectoryChangesError )
+ {
+ return true;
+ }
+ else
+ return false;
+}
+DWORD GetPathOffsetBasedOnFilterFlags(CDirChangeNotification * pNot, DWORD dwFilterFlags)
+{
+
+ ASSERT( pNot && dwFilterFlags != 0 );
+ //helps support the filter options FILTERS_CHECK_FULL_PATH, FILTERS_CHECK_PARTIAL_PATH, and FILTERS_CHECK_FILE_NAME_ONLY
+
+ DWORD dwFileNameOffset = 0;//offset needed to support FILTERS_CHECK_FULL_PATH
+ if( dwFilterFlags & CDirectoryChangeWatcher::FILTERS_CHECK_FILE_NAME_ONLY )
+ {
+ //set the offset to support FILTERS_CHECK_FILE_NAME_ONLY
+ TCHAR * pSlash = _tcsrchr(pNot->m_szFileName1, _T('\\'));
+ if( pSlash )
+ dwFileNameOffset = (++pSlash - pNot->m_szFileName1);
+
+ //
+ // Because file name change notifications take place in the same directory,
+ // the same dwFileNameOffset can be used for the szNewFileName(pNot->m_szFileName2)
+ // when checking the filter against the new file name.
+ //
+ }
+ else
+ if( dwFilterFlags & CDirectoryChangeWatcher::FILTERS_CHECK_PARTIAL_PATH)
+ {
+ //
+ // partial path offset is the offset
+ // from the beginning of the file name,
+ // to the end of the watched directory path...
+ // ie: If you're watching "C:\Temp"
+ // and the file C:\Temp\SubFolder\FileName.txt" is changed,
+ // the partial path offset will give you "SubFolder\FileName.txt"
+ // when this is checked against the include/exclude filter.
+ //
+ dwFileNameOffset = pNot->m_dwPartialPathOffset;
+ }
+ //else
+ //if( m_dwFilterFlags & CDirectoryChangeWatcher::FILTERS_CHECK_FULL_PATH )
+ // dwFileNameOffset = 0;
+
+ return dwFileNameOffset;
+}
+
+bool CDelayedDirectoryChangeHandler::NotifyClientOfFileChange(CDirChangeNotification * pNot)
+//
+//
+// Perform the tests to see if the client wants to be notified of this
+// file change notification.
+//
+// Tests performed:
+//
+// Event test: Not all events can be filtered out.
+// On_ReadDirectoryChangesError
+// cannot be filtered out.
+// Filter flags test: User can specify flags so that no tests are performed....all notifications are sent to the user.
+//
+// Filter test: Test the notification file name against include and exclude filters.
+//
+// Only files changes matching the INCLUDE filter will be passed to the client.
+// By not specifying an include filter, all file changes are passed to the client.
+//
+// Any files matching the EXCLUDE filter will not be passed to the client.
+//
+//
+// Note: For the file name change event:
+// If the old file name does not pass the tests for the include and exclude filter
+// but the NEW file name does pass the test for the filters, then the client IS notified.
+//
+// Client test: The CDirectoryChangeHandler derived class is given a chance to filter the event by calling
+// CDirectoryChangeHandler::On_FilterNotification()
+//
+// RETURN VALUE:
+// If this function returns true, the notification function is called.
+// If it returns false, the notification is ignored.
+// The client is notified by calling CDirectoryChangeHandler's virtual functions On_FileAdded(),On_FileRemoved(), etc.
+{
+ ASSERT( pNot );
+ ASSERT( m_pRealHandler );
+
+ //
+ // Some events can't be ignored, or filtered out.
+ //
+
+ if( ((m_dwFilterFlags & CDirectoryChangeWatcher::FILTERS_DONT_USE_ANY_FILTER_TESTS) == CDirectoryChangeWatcher::FILTERS_DONT_USE_ANY_FILTER_TESTS)
+ || IsNonFilterableEvent( pNot->m_eFunctionToDispatch ) )
+ {
+ // Either this is a non-filterable event, or we're not using any filters...
+ // client is notified of all events..
+ return true;
+ }
+
+ //
+ // See if user wants to test CDirectoryChangeHandler::On_FilterNotification()
+ // before tests are performed against the file name, and filter specifications
+ //
+ if( (m_dwFilterFlags & CDirectoryChangeWatcher::FILTERS_TEST_HANDLER_FIRST )//specified that CDirectoryChangeHandler::On_FilterNotification is to be called before any other filter tests
+ && !(m_dwFilterFlags & CDirectoryChangeWatcher::FILTERS_DONT_USE_HANDLER_FILTER)//and did not specify that this CDirectoryChangeHandler::On_FilterNotification is not to be called..
+ && !m_pRealHandler->On_FilterNotification(pNot->m_eFunctionToDispatch, pNot->m_szFileName1, pNot->m_szFileName2) )
+ {
+ //
+ // Client specified to test handler first, and it didn't pass the test... don't notify the client.
+ //
+ return false;
+ }
+ //else
+ //
+ // this file change passed the user test, continue testing
+ // to see if it passes the filter tests.
+
+ DWORD dwFileNameOffset = GetPathOffsetBasedOnFilterFlags(pNot, m_dwFilterFlags);
+
+ //
+ // See if the changed file matches the include or exclude filter
+ // Only allow notifications for included files
+ // that have not been exluded.
+ //
+ if(!(m_dwFilterFlags & CDirectoryChangeWatcher::FILTERS_DONT_USE_FILTERS) )
+ {
+ if( false == IncludeThisNotification(pNot->m_szFileName1 + dwFileNameOffset)
+ || true == ExcludeThisNotification(pNot->m_szFileName1 + dwFileNameOffset) )
+ {
+ if( pNot->m_eFunctionToDispatch != CDirChangeNotification::eOn_FileNameChanged )
+ return false;
+ else{
+ //Special case for file name change:
+ //
+ // the old file name didn't pass the include/exclude filter
+ // but if the new name passes the include/exclude filter,
+ // we will pass it on to the client...
+
+ if( false == IncludeThisNotification(pNot->m_szFileName2 + dwFileNameOffset)
+ || true == ExcludeThisNotification(pNot->m_szFileName2 + dwFileNameOffset) )
+ {
+ // the new file name didn't pass the include/exclude filter test either
+ // so don't pass the notification on...
+ return false;
+ }
+
+ }
+ }
+
+ }
+
+ //
+ // Finally, let the client determine whether or not it wants this file notification
+ // if this test has not already been performed...
+ //
+
+ if( (m_dwFilterFlags & CDirectoryChangeWatcher::FILTERS_TEST_HANDLER_FIRST)
+ || (m_dwFilterFlags & CDirectoryChangeWatcher::FILTERS_DONT_USE_HANDLER_FILTER) )
+ {
+ // if we got this far, and this flag was specified,
+ // it's already passed this test,
+ // or we're not checking it based on the filter flag FILTERS_DONT_USE_HANDLER_FILTER....
+ return true;
+ }
+ else
+ {
+ if( m_pRealHandler->On_FilterNotification(pNot->m_eFunctionToDispatch,
+ pNot->m_szFileName1,
+ pNot->m_szFileName2) )
+ {
+ return true;
+ }
+ else
+ {
+ //else client's derived CDirectoryChangeHandler class chose
+ // not to be notified of this file change
+ return false;
+ }
+ }
+
+
+}
+
+bool CDelayedDirectoryChangeHandler::IncludeThisNotification(LPCTSTR szFileName)
+//
+// The Include filter specifies which file names we should allow to notifications
+// for... otherwise these notifications are not dispatched to the client's code.
+//
+// Tests the file name to see if it matches a filter specification
+//
+// RETURN VALUES:
+//
+// true : notifications for this file are to be included...
+// notifiy the client by calling the appropriate CDirectoryChangeHandler::On_Filexxx() function.
+// false: this file is not included.... do not notifiy the client...
+//
+{
+ ASSERT( szFileName );
+
+ if( m_szIncludeFilter == NULL ) // no filter specified, all files pass....
+ return true;
+ if( m_nNumIncludeFilterSpecs == 1 )
+ {
+ return _PathMatchSpec(szFileName, m_szIncludeFilter)? true : false;
+ }
+ else
+ {
+ TCHAR ** ppTmp = (TCHAR**)m_szIncludeFilter;
+ for(int i(0); i < m_nNumIncludeFilterSpecs; ++i )
+ {
+ if( _PathMatchSpec(szFileName, *ppTmp++) )
+ return true;
+ }
+ return false;
+ }
+
+ return false;
+}
+
+bool CDelayedDirectoryChangeHandler::ExcludeThisNotification(LPCTSTR szFileName)
+//
+// Tests the file name to see if it matches a filter specification
+// if this function returns true, it means that this notification
+// is NOT to be passed to the client.... changes to this kind of file
+// are not
+//
+// RETURN VALUES:
+//
+// true : notifications for this file are to be filtered out(EXCLUDED)...
+// do not notifify the client code.
+// false: notifications for this file are NOT to be filtered out
+//
+//
+{
+
+ ASSERT( szFileName );
+
+ if( m_szExcludeFilter == NULL ) // no exclude filter... nothing is excluded...
+ return false;
+ if( m_nNumExcludeFilterSpecs == 1 )
+ {
+ if( _PathMatchSpec(szFileName, m_szExcludeFilter) )
+ return true;//exclude this notification...
+ return false;
+ }
+ else
+ {
+ TCHAR ** ppTmp = (TCHAR**)m_szExcludeFilter;
+ for(int i(0); i < m_nNumExcludeFilterSpecs; ++i )
+ {
+ if( _PathMatchSpec(szFileName, *ppTmp++) )
+ return true;//exclude this one...
+ }
+ return false;//didn't match any exclude filters...don't exclude it
+ }
+/**
+ if( m_szExcludeFilter == NULL //no exclude filter specified, not excluding anything....
+ || !PathMatchSpec(szFileName, m_szExcludeFilter) )//or didn't match filter pattern.. this is not excluded...
+ {
+ return false;
+ }
+ return true;
+***/
+
+}
+
+void CDelayedDirectoryChangeHandler::DispatchNotificationFunction(CDirChangeNotification * pNotification)
+/*****************************************************
+ This function is called when we want the notification to execute.
+
+
+******************************************************/
+{
+ ASSERT( m_pRealHandler );
+ ASSERT( pNotification );
+ if( pNotification && m_pRealHandler )
+ {
+ //
+ // Allow the client to ignore the notification
+ //
+ //
+ if( NotifyClientOfFileChange(pNotification))
+ {
+ switch( pNotification->m_eFunctionToDispatch )
+ {
+ case CDirChangeNotification::eOn_FileAdded:
+
+ m_pRealHandler->On_FileAdded( pNotification->m_szFileName1 );
+ break;
+
+ case CDirChangeNotification::eOn_FileRemoved:
+
+ m_pRealHandler->On_FileRemoved( pNotification->m_szFileName1 );
+ break;
+
+ case CDirChangeNotification::eOn_FileNameChanged:
+
+ m_pRealHandler->On_FileNameChanged( pNotification->m_szFileName1, pNotification->m_szFileName2 );
+ break;
+
+ case CDirChangeNotification::eOn_FileModified:
+
+ m_pRealHandler->On_FileModified( pNotification->m_szFileName1 );
+ break;
+
+ case CDirChangeNotification::eOn_ReadDirectoryChangesError:
+
+ m_pRealHandler->On_ReadDirectoryChangesError( pNotification->m_dwError, pNotification->m_szFileName1 );
+ break;
+
+ case CDirChangeNotification::eOn_WatchStarted:
+
+ m_pRealHandler->On_WatchStarted(pNotification->m_dwError, pNotification->m_szFileName1);
+ break;
+
+ case CDirChangeNotification::eOn_WatchStopped:
+
+ try{
+ //
+ // The exception handler is just in case of the condition described in DirectoryChanges.h
+ // in the comments for On_WatchStopped()
+ //
+ m_pRealHandler->On_WatchStopped(pNotification->m_szFileName1);
+
+ }catch(...){
+ MessageBeep( 0xffff );
+ MessageBeep( 0xffff );
+ #ifdef DEBUG
+ MessageBox(NULL,_T("An RTFM Exception was raised in On_WatchStopped() -- see Comments for CDirectoryChangeHandler::On_WatchStopped() in DirectoryChanges.h."), _T("Programmer Note(DEBUG INFO):"), MB_ICONEXCLAMATION | MB_OK);
+ #endif
+ }
+ //
+ // Signal that the On_WatchStopped() function has been dispatched.
+ //
+ if( m_hWatchStoppedDispatchedEvent )
+ SetEvent(m_hWatchStoppedDispatchedEvent);
+ break;
+ case CDirChangeNotification::eFunctionNotDefined:
+ default:
+ break;
+ }//end switch()
+ }
+ }
+ if( pNotification ) //
+ DisposeOfNotification(pNotification);// deletes or releases the notification object from memory/use
+ //
+}
+
+BOOL CDelayedDirectoryChangeHandler::WaitForOnWatchStoppedDispatched( )
+//
+// When shutting down, m_pRealHandler->On_WatchStopped() will be called.
+// Because it's possible that this object will be deleted before the notification
+// can be dispatched to the other thread, we have to wait until we know that it's been executed
+// before returning control.
+//
+// This function signals that the function has been dispatched to the other
+// thread and it will be safe to delete this object once this has returned.
+//
+{
+ ASSERT( m_hWatchStoppedDispatchedEvent );
+ DWORD dwWait = WAIT_FAILED;
+ if( m_hWatchStoppedDispatchedEvent )
+ {
+
+ if( m_bAppHasGUI == false )
+ {
+ //
+ // The function will be dispatched to another thread...
+ // just wait for the event to be signalled....
+ do{
+ dwWait = WaitForSingleObject(m_hWatchStoppedDispatchedEvent, 5000);//wait five seconds
+ if( dwWait != WAIT_OBJECT_0 )
+ {
+ TRACE(_T("WARNING: Possible Deadlock detected! ThreadID: %d File: %s Line: %d\n"), GetCurrentThreadId(), _T(__FILE__), __LINE__);
+ }
+ }while( dwWait != WAIT_OBJECT_0 );
+ }
+ else
+ {
+ //
+ // Note to self: This thread doesn't have a message Q, and therefore can't attach to
+ // receive messages and process them... MsgWaitForMultipleObjects won't wake up for messages
+ // unless i attach myself the the other threads input Q....
+ // just use MsgWaitForMultipleObjects() in place of WaitForSingleObject in the places where it's used...
+ //
+ do{
+ dwWait = MsgWaitForMultipleObjects(1, &m_hWatchStoppedDispatchedEvent,
+ FALSE, 5000,
+ QS_ALLEVENTS);//wake up for all events, sent messages, posted messages etc.
+ switch(dwWait)
+ {
+ case WAIT_OBJECT_0:
+ {
+ //
+ // The event has become signalled
+ //
+
+ }break;
+ case WAIT_OBJECT_0 + 1:
+ {
+ //
+ // There is a message in this thread's queue, so
+ // MsgWaitForMultipleObjects returned.
+ // Process those messages, and wait again.
+
+ MSG msg;
+ while( PeekMessage(&msg, NULL, 0,0, PM_REMOVE ) )
+ {
+ if( msg.message != WM_QUIT)
+ {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+ else
+ {
+ /****
+ NOTE: putting WM_QUIT back in the Q caused problems. forget about it.
+ ****/
+ break;
+ }
+ }
+ }break;
+ case WAIT_TIMEOUT:
+ {
+ TRACE(_T("WARNING: Possible Deadlock detected! ThreadID: %d File: %s Line: %d\n"), GetCurrentThreadId(), _T(__FILE__), __LINE__);
+ }break;
+ }
+ }while( dwWait != WAIT_OBJECT_0 );
+ ASSERT( dwWait == WAIT_OBJECT_0 );
+ }
+
+ }
+ else
+ {
+ TRACE(_T("WARNING: Unable to wait for notification that the On_WatchStopped function has been dispatched to another thread.\n"));
+ TRACE(_T("An Exception may occur shortly.\n"));
+ TRACE(_T("File: %s Line: %d"), _T( __FILE__ ), __LINE__);
+
+ }
+
+
+ return (dwWait == WAIT_OBJECT_0 );
+}
+
+void CDelayedDirectoryChangeHandler::SetChangedDirectoryName(const CString & strChangedDirName)
+{
+ ASSERT( m_pRealHandler );
+ CDirectoryChangeHandler::SetChangedDirectoryName(strChangedDirName);
+ if( m_pRealHandler )
+ m_pRealHandler->SetChangedDirectoryName( strChangedDirName );
+}
+const CString & CDelayedDirectoryChangeHandler::GetChangedDirectoryName() const
+{
+ if( m_pRealHandler )
+ return m_pRealHandler->GetChangedDirectoryName();
+ return CDirectoryChangeHandler::GetChangedDirectoryName();
+} \ No newline at end of file
diff --git a/IvyFileMon/DelayedDirectoryChangeHandler.h b/IvyFileMon/DelayedDirectoryChangeHandler.h
new file mode 100644
index 0000000..5409c5b
--- /dev/null
+++ b/IvyFileMon/DelayedDirectoryChangeHandler.h
@@ -0,0 +1,336 @@
+// DelayedDirectoryChangeHandler.h: interface for the CDelayedDirectoryChangeHandler2 class.
+//
+//////////////////////////////////////////////////////////////////////
+//
+// You needn't worry about the classes in this file.
+// they are implementation classes used to help CDirectoryChangeWatcher work.
+//
+//
+
+#if !defined(AFX_DELAYEDDIRECTORYCHANGEHANDLER_H__F20EC22B_1C79_403E_B43C_938F95723D45__INCLUDED_)
+#define AFX_DELAYEDDIRECTORYCHANGEHANDLER_H__F20EC22B_1C79_403E_B43C_938F95723D45__INCLUDED_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+//classes declrared in other files:
+class CDirectoryChangeWatcher;
+class CDirectoryChangeHandler;
+//classes declared in this file:
+class CDirChangeNotification;
+class CDelayedDirectoryChangeHandler;
+
+class CDelayedNotificationWindow;
+class CDelayedNotificationThread;
+
+/*******************************************************************
+ The classes in this file implement methods to ensure that file change
+ notifications are fired in a thread other than the worker thread used
+ by CDirectoryChangeWatcher.
+
+ Dispatching the notifications in to a different thread improves the performance
+ of CDirectoryChangeWatcher so that it can process more notifications faster
+ and notifications aren't 'lost'.
+
+
+ There are two methods of dispatching functions to another thread.
+
+ 1) One is to use the message pump associated w/ the main thread by posting notifications
+ to a hidden window. This is implemented w/ the class CDelayedNotificationWindow.
+
+ 2) The other is to create a worker thread that implements a message pump. This is
+ implemented w/ the class CDelayedNotificationThread.
+
+
+ If your app uses a GUI then it has a already has message pump.
+ You can make sure that CDelayedNotificationWindow is used in this case.
+ The advantage here being that there is one less worker thread used in your program.
+
+ If your app is a command line app or otherwise doesn't have a GUI,
+ then you will want to make sure that you are using the CDelayedNotificationThread
+ to dispatch notifications to another thread.
+
+ This is determined by a flag passed is passed to the constructor of CDirecotryChangeWatcher
+
+********************************************************************/
+
+class CDelayedNotifier
+//
+// Abstract base class for ensuring notifications are fired in a thread
+//
+//
+{
+public:
+ virtual ~CDelayedNotifier(){}
+ virtual void PostNotification(CDirChangeNotification * pNotification) = 0;
+
+};
+
+class CDelayedNotificationWindow : public CDelayedNotifier
+//
+// A class that implements a
+// there will always be only one of the actual windows
+// in existance.
+//
+{
+public:
+ CDelayedNotificationWindow(){ AddRef(); }
+ virtual ~CDelayedNotificationWindow(){ Release(); }
+
+
+ void PostNotification(CDirChangeNotification * pNotification);
+private:
+ long AddRef(); // the window handle is reference counted
+ long Release(); //
+
+ static long s_nRefCnt;
+ static HWND s_hWnd; //there's only one window no matter how many instances of this class there are.... this means that all notifications are handled by the same thread.
+ static BOOL s_bRegisterWindow;
+ BOOL RegisterWindowClass(LPCTSTR szClassName);
+ BOOL CreateNotificationWindow();
+};
+
+class CDelayedNotificationThread : public CDelayedNotifier
+//
+// Class that implements a worker thread w/ a message pump.
+// CDirectoryChangeWatcher posts notifications to this thread, where they are dispatched.
+// This thread executes CDirectoryChangeHandler notifications.
+//
+{
+public:
+ CDelayedNotificationThread()
+ :m_hThreadStartEvent(NULL)
+ {
+ m_hThreadStartEvent = CreateEvent(NULL,FALSE,FALSE,NULL);
+ ASSERT( m_hThreadStartEvent );
+ AddRef();
+ }
+ virtual ~CDelayedNotificationThread()
+ {
+ Release();
+ if( m_hThreadStartEvent )
+ CloseHandle(m_hThreadStartEvent), m_hThreadStartEvent = NULL;
+ }
+
+ void PostNotification(CDirChangeNotification * pNotification);
+
+private:
+ long AddRef(); // The thread handle is reference
+ long Release(); // counted so that only one thread is used
+ // so that there's only one worker thread(performing this functino)
+ static long s_nRefCnt; // no matter how many directories are being watched
+ static HANDLE s_hThread; //
+ static DWORD s_dwThreadID; //
+
+ static UINT __stdcall ThreadFunc(LPVOID lpvThis);
+
+ bool StartThread();
+ bool StopThread();
+
+ BOOL WaitForThreadStartup(){ return WaitForSingleObject(m_hThreadStartEvent, INFINITE) == WAIT_OBJECT_0; };
+ BOOL SignalThreadStartup(){ return SetEvent( m_hThreadStartEvent ) ; }
+
+ HANDLE m_hThreadStartEvent;//signals that the worker thread has started. this fixes a bug condition.
+
+};
+
+
+class CDirChangeNotification
+//
+// A class to help dispatch the change notifications to the main thread.
+//
+// This class holds the data in memory until the notification can be dispatched.(ie: this is the time between when the notification is posted, and the clients notification code is called).
+//
+//
+{
+private:
+ CDirChangeNotification();//not implemented
+public:
+ explicit CDirChangeNotification(CDelayedDirectoryChangeHandler * pDelayedHandler, DWORD dwPartialPathOffset);
+ ~CDirChangeNotification();
+
+ //
+ //
+ void PostOn_FileAdded(LPCTSTR szFileName);
+ void PostOn_FileRemoved(LPCTSTR szFileName);
+ void PostOn_FileNameChanged(LPCTSTR szOldName, LPCTSTR szNewName);
+ void PostOn_FileModified(LPCTSTR szFileName);
+ void PostOn_ReadDirectoryChangesError(DWORD dwError, LPCTSTR szDirectoryName);
+ void PostOn_WatchStarted(DWORD dwError, LPCTSTR szDirectoryName);
+ void PostOn_WatchStopped(LPCTSTR szDirectoryName);
+
+ void DispatchNotificationFunction();
+
+
+ enum eFunctionToDispatch{ eFunctionNotDefined = -1,
+ eOn_FileAdded = FILE_ACTION_ADDED,
+ eOn_FileRemoved = FILE_ACTION_REMOVED,
+ eOn_FileModified = FILE_ACTION_MODIFIED,
+ eOn_FileNameChanged = FILE_ACTION_RENAMED_OLD_NAME,
+ eOn_ReadDirectoryChangesError,
+ eOn_WatchStarted,
+ eOn_WatchStopped
+ };
+protected:
+ void PostNotification();
+
+private:
+ friend class CDelayedDirectoryChangeHandler;
+ CDelayedDirectoryChangeHandler * m_pDelayedHandler;
+
+ //
+ // Members to help implement DispatchNotificationFunction
+ //
+ //
+
+ eFunctionToDispatch m_eFunctionToDispatch;
+ //Notification Data:
+ TCHAR * m_szFileName1;//<-- is the szFileName parameter to On_FileAdded(),On_FileRemoved,On_FileModified(), and is szOldFileName to On_FileNameChanged(). Is also strDirectoryName to On_ReadDirectoryChangesError(), On_WatchStarted(), and On_WatchStopped()
+ TCHAR * m_szFileName2;//<-- is the szNewFileName parameter to On_FileNameChanged()
+ DWORD m_dwError; //<-- is the dwError parameter to On_WatchStarted(), and On_ReadDirectoryChangesError()
+ //
+
+ DWORD m_dwPartialPathOffset;//helps support FILTERS_CHECK_PARTIAL_PATH...not passed to any functions other than may be used during tests in CDelayedDirectoryChangeHandler::NotifyClientOfFileChange()
+
+
+ friend class CDirChangeNotification;
+ friend class CDirectoryChangeWatcher;
+ friend DWORD GetPathOffsetBasedOnFilterFlags(CDirChangeNotification*,DWORD);//a friend function
+};
+
+
+//////////////////////////////////////////////////////////////////////////
+//
+// This class makes it so that a file change notification is executed in the
+// context of the main thread, and not the worker thread.
+//
+//
+// It works by creating a hidden window. When it receieves a notification
+// via one of the On_Filexxx() functions, a message is posted to this window.
+// when the message is handled, the notification is fired again in the context
+// of the main thread, or whichever thread that called CDirectoryChangeWatcher::WatchDirectory()
+//
+//
+/////////////////////////////////////////////////////////////////////////////
+// Note this code wants to use PathMatchSpec()
+// which is only supported on WINNT 4.0 w/ Internet Explorer 4.0 and above.
+// PathMatchSpec is fully supported on Win2000/XP.
+//
+// For the case of WINNT 4.0 w/out IE 4.0, we'll use a simpler function.
+// some functionality is lost, but such is the price.
+//
+
+typedef BOOL (STDAPICALLTYPE * FUNC_PatternMatchSpec)(LPCTSTR pszFile, LPCTSTR pszSpec);
+
+class CDelayedDirectoryChangeHandler : public CDirectoryChangeHandler
+//
+// Decorates an instance of a CDirectoryChangeHandler object.
+// Intercepts notification function calls and posts them to
+// another thread through a method implemented by a class derived from
+// CDelayedNotifier
+//
+//
+// This class implements dispatching the notifications to a thread
+// other than CDirectoryChangeWatcher::MonitorDirectoryChanges()
+//
+// Also supports the include and exclude filters for each directory
+//
+{
+private:
+ CDelayedDirectoryChangeHandler();//not implemented.
+public:
+ CDelayedDirectoryChangeHandler( CDirectoryChangeHandler * pRealHandler, bool bAppHasGUI, LPCTSTR szIncludeFilter, LPCTSTR szExcludeFilter, DWORD dwFilterFlags);
+ virtual ~CDelayedDirectoryChangeHandler();
+
+
+ CDirectoryChangeHandler * GetRealChangeHandler()const { return m_pRealHandler; }
+ CDirectoryChangeHandler * & GetRealChangeHandler(){ return m_pRealHandler; }//FYI: PCLint will give a warning that this exposes a private/protected member& defeats encapsulation.
+
+ void PostNotification(CDirChangeNotification * pNotification);
+ void DispatchNotificationFunction(CDirChangeNotification * pNotification);
+
+
+protected:
+ //These functions are called when the directory to watch has had a change made to it
+ void On_FileAdded(const CString & strFileName);
+ void On_FileRemoved(const CString & strFileName);
+ void On_FileModified(const CString & strFileName);
+ void On_FileNameChanged(const CString & strOldFileName, const CString & strNewFileName);
+ void On_ReadDirectoryChangesError(DWORD dwError, const CString & strDirectoryName);
+
+ void On_WatchStarted(DWORD dwError, const CString & strDirectoryName);
+ void On_WatchStopped(const CString & strDirectoryName);
+
+
+ void SetChangedDirectoryName(const CString & strChangedDirName);
+ const CString & GetChangedDirectoryName()const;
+
+ BOOL WaitForOnWatchStoppedDispatched();//see comments in .cpp
+
+
+ bool NotifyClientOfFileChange(CDirChangeNotification * pNot);
+
+ bool IncludeThisNotification(LPCTSTR szFileName); // based on file name.
+ bool ExcludeThisNotification(LPCTSTR szFileName); // Allows us to filter notifications
+ //
+
+
+
+ CDirChangeNotification * GetNotificationObject();
+ void DisposeOfNotification(CDirChangeNotification * pNotification);
+
+ CDelayedNotifier * m_pDelayNotifier;
+ CDirectoryChangeHandler * m_pRealHandler;
+
+ // m_bAppHasGUI:
+ // This flag, if set to true, indicates that the app has a message
+ bool m_bAppHasGUI; // pump, and that functions are dispatched to the main thread.
+ // Otherwise, functions are dispatched to a separate worker thread.
+ //
+ DWORD m_dwFilterFlags;
+
+ DWORD m_dwPartialPathOffset; //helps support FILTERS_CHECK_PARTIAL_PATH
+ void SetPartialPathOffset(const CString & strWatchedDirName);
+
+ friend class CDirectoryChangeWatcher;
+ friend class CDirectoryChangeWatcher::CDirWatchInfo;
+
+private:
+ HANDLE m_hWatchStoppedDispatchedEvent;//supports WaitForOnWatchStoppedDispatched()
+
+ TCHAR * m_szIncludeFilter; // Supports the include
+ TCHAR * m_szExcludeFilter; // & exclude filters
+
+ //
+ // Load PathMatchSpec dynamically because it's only supported if IE 4.0 or greater is
+ // installed.
+ static HMODULE s_hShlwapi_dll;//for the PathMatchSpec() function
+ static BOOL s_bShlwapi_dllExists;//if on NT4.0 w/out IE 4.0 or greater, this'll be false
+ static long s_nRefCnt_hShlwapi;
+ static FUNC_PatternMatchSpec s_fpPatternMatchSpec;
+
+ BOOL _PathMatchSpec(LPCTSTR szPath, LPCTSTR szPattern);
+ BOOL InitializePathMatchFunc(LPCTSTR szIncludeFilter, LPCTSTR szExcludeFilter);
+ BOOL InitializePatterns(LPCTSTR szIncludeFilter, LPCTSTR szExcludeFilter);
+ void UninitializePathMatchFunc();
+
+ bool UsesRealPathMatchSpec() const;//are we using PathMatchSpec() or wildcmp()?
+
+ //note: if the PathMatchSpec function isn't found, wildcmp() is used instead.
+ //
+ // to support multiple file specs separated by a semi-colon,
+ // the include and exclude filters that are passed into the
+ // the constructor are parsed into separate strings
+ // which are all checked in a loop.
+ //
+ int m_nNumIncludeFilterSpecs;
+ int m_nNumExcludeFilterSpecs;
+
+
+};
+
+
+
+
+#endif // !defined(AFX_DELAYEDDIRECTORYCHANGEHANDLER_H__F20EC22B_1C79_403E_B43C_938F95723D45__INCLUDED_)
diff --git a/IvyFileMon/DirectoryChanges.cpp b/IvyFileMon/DirectoryChanges.cpp
new file mode 100644
index 0000000..10b88b3
--- /dev/null
+++ b/IvyFileMon/DirectoryChanges.cpp
@@ -0,0 +1,2026 @@
+// DirectoryChanges.cpp: implementation of the CDirectoryChangeWatcher and CDirectoryChangeHandler classes.
+//
+///////////////////////////////////////////////////////////////////
+///
+
+/***********************************************************
+
+ Author: Wes Jones wesj@hotmail.com
+
+ File: DirectoryChanges.cpp
+
+ Latest Changes:
+
+ 11/22/2001 -- Fixed bug causing file name's to be truncated if
+ longer than 130 characters. Fixed CFileNotifyInformation::GetFileName()
+ Thanks to Edric(uo_edric@hotmail.com) for pointing this out.
+
+ Added code to enable process privileges when CDirectoryChangeWatcher::WatchDirectory()
+ is first called. See docuementation API for ReadDirectoryChangesW() for more information of required privileges.
+
+ Currently enables these privileges: (11/22/2001)
+ SE_BACKUP_NAME
+ SE_CHANGE_NOTIFY_NAME
+ SE_RESTORE_NAME(02/09/2002)
+ Implemented w/ helper class CPrivilegeEnabler.
+
+ 11/23/2001 Added classes so that all notifications are handled by the
+ same thread that called CDirectoryChangeWatcher::WatchDirectory(),
+ ie: the main thread.
+ CDirectoryChangeHandler::On_Filexxxx() functions are now called in the
+ context of the main thread instead of the worker thread.
+
+ This is good for two reasons:
+ 1: The worker thread spends less time handling each notification.
+ The worker thread simply passes the notification to the main thread,
+ which does the processing.
+ This means that each file change notification is handled as fast as possible
+ and ReadDirectoryChangesW() can be called again to receive more notifications
+ faster.
+
+ 2: This makes it easier to make an ActiveX or ATL object with this class
+ because the events that are fired, fire in the context of the main thread.
+ The fact that I'm using a worker thread w/ this class is totally
+ transparent to a client user.
+ If I decide to turn this app into an ActiveX or ATL object
+ I don't have to worry about wierd COM rules and multithreading issues,
+ and neither does the client, be the client a VB app, C++ app, Delphi app, or whatever.
+
+ Implemented with CDelayedDirectoryChangeHandler in DirectoryChangeHandler.h/.cpp
+
+ 02/06/2002 Fixed a bug that would cause an application to hang.
+ If ReadDirectoryChangesW was to fail during normal operation,
+ the short story is that the application would hang
+ when it called CDirectoryChangeWatcher::UnwatchDirectory(const CString & )
+
+ One way to reproduce this behavior on the old code
+ is to watch a directory using a UNC path, and then change the IP
+ address of that machine while the watch was running. Exitting
+ the app after this would cause it to hang.
+
+ Steps to reproduce it:
+
+ 1) Assume that the computer running the code is
+ named 'ThisComputer' and there is a shared folder named 'FolderName'
+
+
+ 2) Start a watch on a folder using a UNC path: ie: \\ThisComputer\FolderName
+
+ eg: CDirectoryChangeWatcher watcher;
+
+ watcher.WatchDirectory(_T("\\\\ThisComputer\\FolderName",/ * other parameters * /)
+
+ 3) Change the IP address of 'ThisComputer'
+
+ ** ReadDirectoryChangesW() will fail some point after this.
+
+
+ 4) Exit the application... it may hang.
+
+
+ Anyways, that's what the bug fix is for.
+
+
+ 02/06/2002 New side effects for CDirectoryChangeHandler::On_ReadDirectoryChangeError()
+
+ If CDirectoryChangeHandler::On_ReadDirectoryChangeError() is ever executed
+ the directory that you are watching will have been unwatched automatically due
+ to the error condition.
+
+ A call to CDirectoryChangeWatcher::IsWatchingDirectory() will fail because the directory
+ is no longer being watched. You'll need to re-watch that directory.
+
+ 02/09/2002 Added a parameter to CDirectoryChangeHandler::On_ReadDirectoryChangeError()
+
+ Added the parameter: const CString & strDirectoryName
+ The new signature is now:
+ virtual void CDirectoryChangeHandler::On_ReadDirectoryChangeError(DWORD dwError, const CString & strDirectoryName);
+
+ This new parameter gives you the name of the directory that the error occurred on.
+
+ 04/25/2002 Provided a way to get around the requirement of a message pump.
+ A console app can now use this w/out problems by passing false
+ to the constructor of CDirectoryChangeWatcher.
+ An app w/ a message pump can also pass false if it so desires w/out problems.
+
+ 04/25/2002 Added two new virtual functions to CDirectoryChangeHandler
+
+ Added:
+ On_WatchStarted(DWORD dwError, const CString & strDirectoryName)
+ On_WatchStopped(const CString & strDirectoryName);
+ See header file for details.
+
+ 04/27/2002 Added new function to CDirectoryChangeHandler:
+
+ Added virtual bool On_FilterNotification(DWORD dwNotifyAction, LPCTSTR szFileName, LPCTSTR szNewFileName)
+
+ This function is called before any notification function, and allows the
+ CDirectoryChangeHandler derived class to ignore file notifications
+ by performing a programmer defined test.
+ By ignore, i mean that
+ On_FileAdded(), On_FileRemoved(), On_FileModified(), or On_FileNameChanged()
+ will NOT be called if this function returns false.
+
+ The default implementation always returns true, signifying that ALL notifications
+ are to be called.
+
+
+ 04/27/2002 Added new Parameters to CDirectoryChangeWatcher::WatchDirectory()
+
+ The new parameters are:
+ LPCTSTR szIncludeFilter
+ LPCTSTR szExcludeFilter
+ Both parameters are defaulted to NULL.
+ Signature is now:
+ CDirectoryChangeWatcher::WatchDirectory(const CString & strDirToWatch,
+ DWORD dwChangesToWatchFor,
+ CDirectoryChangeHandler * pChangeHandler,
+ BOOL bWatchSubDirs = FALSE,
+ LPCTSTR szIncludeFilter = NULL,
+ LPCTSTR szExcludeFilter = NULL)
+
+ 04/27/2002 Added support for include and exclude filters.
+ These filters allow you to receive notifications for just the files you
+ want... ie: you can specify that you only want to receive notifications
+ for changes to "*.txt" files or some other such file filter.
+
+ NOTE: This feature is implemented w/ the PathMatchSpec() api function
+ which is only available on NT4.0 if Internet Explorer 4.0 or above is installed.
+ See MSDN for PathMatchSpec(). Win2000, and XP do not have to worry about it.
+
+ Filter specifications:
+ Accepts wild card characters * and ?, just as you're used to for the DOS dir command.
+ It is possible to specify multiple extenstions in the filter by separating each filter spec
+ with a semi-colon.
+ eg: "*.txt;*.tmp;*.log" <-- this filter specifies all .txt, .tmp, & .log files
+
+
+
+ Filters are passed as parameters to CDirectoryChangeWatcher::WatchDirectory()
+
+ NOTE: The filters you specify take precedence over CDirectoryChangeHandler::On_FilterNotification().
+ This means that if the file name does not pass the filters that you specify
+ when the watch is started, On_FilterNotification() will not be called.
+ Filter specifications are case insensitive, ie: ".tmp" and ".TMP" are the same
+
+
+ FILTER TYPES:
+ Include Filter:
+ If you specify an include filter, you are specifying that you
+ only want to receive notifications for specific file types.
+ eg: "*.log" means that you only want notifications for changes
+ to files w/ an exention of ".log".
+ Changes to ALL OTHER other file types are ignored.
+ An empty, or not specified include filter means that you want
+ notifications for changes of ALL file types.
+ Exclude filter:
+
+ If you specify an exclude filter, you are specifying that
+ you do not wish to receive notifications for a specific type of file or files.
+ eg: "*.tmp" would mean that you do not want any notifications
+ regarding files that end w/ ".tmp"
+ An empty, or not specified exclude filter means that
+ you do not wish to exclude any file notifications.
+
+
+
+
+
+
+
+************************************************************/
+
+#include "stdafx.h"
+#include "DirectoryChanges.h"
+#include "DelayedDirectoryChangeHandler.h"
+
+#ifdef _DEBUG
+#undef THIS_FILE
+static char THIS_FILE[]=__FILE__;
+#define new DEBUG_NEW
+#endif
+//
+//
+// Fwd Declares & #define's
+//
+//
+//
+// Helper classes
+class CPrivilegeEnabler; //for use w/ enabling process priveledges when this code is first used. It's a singleton.
+
+class CFileNotifyInformation;//helps CDirectoryChangeWatcher class notify CDirectoryChangeHandler class of changes to files.
+
+class CDelayedDirectoryChangeHandler; // Helps all notifications become handled by the main
+ // thread, or a worker thread depending upon a value passed to the
+ // constructor of CDirectoryChangeWatcher.
+ //
+ // For notifications to fire in the main thread, your app must have a message pump.
+ //
+ // The 'main' thread is the one that called CDirectoryChangeWatcher::WatchDirectory()
+
+// Helper functions
+static BOOL EnablePrivilege(LPCTSTR pszPrivName, BOOL fEnable = TRUE);
+static bool IsDirectory(const CString & strPath);
+/////////////////////////////////////////////////////////////////////
+// Helper functions.
+BOOL EnablePrivilege(LPCTSTR pszPrivName, BOOL fEnable /*= TRUE*/)
+//
+// I think this code is from a Jeffrey Richter book...
+//
+// Enables user priviledges to be set for this process.
+//
+// Process needs to have access to certain priviledges in order
+// to use the ReadDirectoryChangesW() API. See documentation.
+{
+ BOOL fOk = FALSE;
+ // Assume function fails
+ HANDLE hToken;
+ // Try to open this process's access token
+ if (OpenProcessToken(GetCurrentProcess(),
+ TOKEN_ADJUST_PRIVILEGES, &hToken))
+ {
+ // privilege
+ TOKEN_PRIVILEGES tp = { 1 };
+
+ if( LookupPrivilegeValue(NULL, pszPrivName, &tp.Privileges[0].Luid) )
+ {
+ tp.Privileges[0].Attributes = fEnable ? SE_PRIVILEGE_ENABLED : 0;
+
+ AdjustTokenPrivileges(hToken, FALSE, &tp,
+ sizeof(tp), NULL, NULL);
+
+ fOk = (GetLastError() == ERROR_SUCCESS);
+ }
+ CloseHandle(hToken);
+ }
+ return(fOk);
+}
+
+static bool IsDirectory(const CString & strPath)
+//
+// Returns: bool
+// true if strPath is a path to a directory
+// false otherwise.
+//
+{
+ DWORD dwAttrib = GetFileAttributes( strPath );
+ return static_cast<bool>( ( dwAttrib != 0xffffffff
+ && (dwAttrib & FILE_ATTRIBUTE_DIRECTORY)) );
+
+
+}
+///////////////////////////////////////////////
+//Helper class:
+
+class CFileNotifyInformation
+/*******************************
+
+A Class to more easily traverse the FILE_NOTIFY_INFORMATION records returned
+by ReadDirectoryChangesW().
+
+FILE_NOTIFY_INFORMATION is defined in Winnt.h as:
+
+ typedef struct _FILE_NOTIFY_INFORMATION {
+ DWORD NextEntryOffset;
+ DWORD Action;
+ DWORD FileNameLength;
+ WCHAR FileName[1];
+} FILE_NOTIFY_INFORMATION, *PFILE_NOTIFY_INFORMATION;
+
+ ReadDirectoryChangesW basically puts x amount of these records in a
+ buffer that you specify.
+ The FILE_NOTIFY_INFORMATION structure is a 'dynamically sized' structure (size depends on length
+ of the file name (+ sizeof the DWORDs in the struct))
+
+ Because each structure contains an offset to the 'next' file notification
+ it is basically a singly linked list. This class treats the structure in that way.
+
+
+ Sample Usage:
+ BYTE Read_Buffer[ 4096 ];
+
+ ...
+ ReadDirectoryChangesW(...Read_Buffer, 4096,...);
+ ...
+
+ CFileNotifyInformation notify_info( Read_Buffer, 4096);
+ do{
+ switch( notify_info.GetAction() )
+ {
+ case xx:
+ notify_info.GetFileName();
+ }
+
+ while( notify_info.GetNextNotifyInformation() );
+
+********************************/
+{
+public:
+ CFileNotifyInformation( BYTE * lpFileNotifyInfoBuffer, DWORD dwBuffSize)
+ : m_pBuffer( lpFileNotifyInfoBuffer ),
+ m_dwBufferSize( dwBuffSize )
+ {
+ ASSERT( lpFileNotifyInfoBuffer && dwBuffSize );
+
+ m_pCurrentRecord = (PFILE_NOTIFY_INFORMATION) m_pBuffer;
+ }
+
+
+ BOOL GetNextNotifyInformation();
+
+ BOOL CopyCurrentRecordToBeginningOfBuffer(OUT DWORD & ref_dwSizeOfCurrentRecord);
+
+ DWORD GetAction() const;//gets the type of file change notifiation
+ CString GetFileName()const;//gets the file name from the FILE_NOTIFY_INFORMATION record
+ CString GetFileNameWithPath(const CString & strRootPath) const;//same as GetFileName() only it prefixes the strRootPath into the file name
+
+
+protected:
+ BYTE * m_pBuffer;//<--all of the FILE_NOTIFY_INFORMATION records 'live' in the buffer this points to...
+ DWORD m_dwBufferSize;
+ PFILE_NOTIFY_INFORMATION m_pCurrentRecord;//this points to the current FILE_NOTIFY_INFORMATION record in m_pBuffer
+
+};
+
+BOOL CFileNotifyInformation::GetNextNotifyInformation()
+/***************
+ Sets the m_pCurrentRecord to the next FILE_NOTIFY_INFORMATION record.
+
+ Even if this return FALSE, (unless m_pCurrentRecord is NULL)
+ m_pCurrentRecord will still point to the last record in the buffer.
+****************/
+{
+ if( m_pCurrentRecord
+ && m_pCurrentRecord->NextEntryOffset != 0UL)//is there another record after this one?
+ {
+ //set the current record to point to the 'next' record
+ PFILE_NOTIFY_INFORMATION pOld = m_pCurrentRecord;
+ m_pCurrentRecord = (PFILE_NOTIFY_INFORMATION) ((LPBYTE)m_pCurrentRecord + m_pCurrentRecord->NextEntryOffset);
+
+ ASSERT( (DWORD)((BYTE*)m_pCurrentRecord - m_pBuffer) < m_dwBufferSize);//make sure we haven't gone too far
+
+ if( (DWORD)((BYTE*)m_pCurrentRecord - m_pBuffer) > m_dwBufferSize )
+ {
+ //we've gone too far.... this data is hosed.
+ //
+ // This sometimes happens if the watched directory becomes deleted... remove the FILE_SHARE_DELETE flag when using CreateFile() to get the handle to the directory...
+ m_pCurrentRecord = pOld;
+ }
+
+ return (BOOL)(m_pCurrentRecord != pOld);
+ }
+ return FALSE;
+}
+
+BOOL CFileNotifyInformation::CopyCurrentRecordToBeginningOfBuffer(OUT DWORD & ref_dwSizeOfCurrentRecord)
+/*****************************************
+ Copies the FILE_NOTIFY_INFORMATION record to the beginning of the buffer
+ specified in the constructor.
+
+ The size of the current record is returned in DWORD & dwSizeOfCurrentRecord.
+
+*****************************************/
+{
+ ASSERT( m_pBuffer && m_pCurrentRecord );
+ if( !m_pCurrentRecord ) return FALSE;
+
+ BOOL bRetVal = TRUE;
+
+ //determine the size of the current record.
+ ref_dwSizeOfCurrentRecord = sizeof( FILE_NOTIFY_INFORMATION );
+ //subtract out sizeof FILE_NOTIFY_INFORMATION::FileName[1]
+ WCHAR FileName[1];//same as is defined for FILE_NOTIFY_INFORMATION::FileName
+ UNREFERENCED_PARAMETER(FileName);
+ ref_dwSizeOfCurrentRecord -= sizeof(FileName);
+ //and replace it w/ value of FILE_NOTIFY_INFORMATION::FileNameLength
+ ref_dwSizeOfCurrentRecord += m_pCurrentRecord->FileNameLength;
+
+ ASSERT( (DWORD)((LPBYTE)m_pCurrentRecord + ref_dwSizeOfCurrentRecord) <= m_dwBufferSize );
+
+ ASSERT( (void*)m_pBuffer != (void*)m_pCurrentRecord );//if this is the case, your buffer is way too small
+ if( (void*)m_pBuffer != (void*) m_pCurrentRecord )
+ {//copy the m_pCurrentRecord to the beginning of m_pBuffer
+
+ ASSERT( (DWORD)m_pCurrentRecord > (DWORD)m_pBuffer + ref_dwSizeOfCurrentRecord);//will it overlap?
+ __try{
+ memcpy(m_pBuffer, m_pCurrentRecord, ref_dwSizeOfCurrentRecord);
+ bRetVal = TRUE;
+ }
+ __except(EXCEPTION_EXECUTE_HANDLER)
+ {
+ TRACE(_T("EXCEPTION! CFileNotifyInformation::CopyCurrentRecordToBeginningOfBuffer() -- probably because bytes overlapped in a call to memcpy()"));
+ bRetVal = FALSE;
+ }
+ }
+ //else
+ //there was only one record in this buffer, and m_pCurrentRecord is already at the beginning of the buffer
+ return bRetVal;
+}
+
+DWORD CFileNotifyInformation::GetAction() const
+{
+ ASSERT( m_pCurrentRecord );
+ if( m_pCurrentRecord )
+ return m_pCurrentRecord->Action;
+ return 0UL;
+}
+
+CString CFileNotifyInformation::GetFileName() const
+{
+ //
+ //BUG FIX:
+ // File Name's longer than 130 characters are truncated
+ //
+ // Thanks Edric @ uo_edric@hotmail.com for pointing this out.
+ if( m_pCurrentRecord )
+ {
+ WCHAR wcFileName[ MAX_PATH + 1] = {0};//L"";
+ memcpy( wcFileName,
+ m_pCurrentRecord->FileName,
+ //min( MAX_PATH, m_pCurrentRecord->FileNameLength) <-- buggy line
+ min( (MAX_PATH * sizeof(WCHAR)), m_pCurrentRecord->FileNameLength));
+
+
+ return CString( wcFileName );
+ }
+ return CString();
+}
+
+static inline bool HasTrailingBackslash(const CString & str )
+{
+ if( str.GetLength() > 0
+ && str[ str.GetLength() - 1 ] == _T('\\') )
+ return true;
+ return false;
+}
+CString CFileNotifyInformation::GetFileNameWithPath(const CString & strRootPath) const
+{
+ CString strFileName( strRootPath );
+ //if( strFileName.Right(1) != _T("\\") )
+ if( !HasTrailingBackslash( strRootPath ) )
+ strFileName += _T("\\");
+
+ strFileName += GetFileName();
+ return strFileName;
+}
+/////////////////////////////////////////////////////////////////////////////////
+class CPrivilegeEnabler
+//
+// Enables privileges for this process
+// first time CDirectoryChangeWatcher::WatchDirectory() is called.
+//
+// It's a singleton.
+//
+{
+private:
+ CPrivilegeEnabler();//ctor
+public:
+ ~CPrivilegeEnabler(){};
+
+ static CPrivilegeEnabler & Instance();
+ //friend static CPrivilegeEnabler & Instance();
+};
+
+CPrivilegeEnabler::CPrivilegeEnabler()
+{
+ LPCTSTR arPrivelegeNames[] = {
+ SE_BACKUP_NAME, // these two are required for the FILE_FLAG_BACKUP_SEMANTICS flag used in the call to
+ SE_RESTORE_NAME,// CreateFile() to open the directory handle for ReadDirectoryChangesW
+
+ SE_CHANGE_NOTIFY_NAME //just to make sure...it's on by default for all users.
+ //<others here as needed>
+ };
+ for(int i = 0; i < sizeof(arPrivelegeNames) / sizeof(arPrivelegeNames[0]); ++i)
+ {
+ if( !EnablePrivilege(arPrivelegeNames[i], TRUE) )
+ {
+ TRACE(_T("Unable to enable privilege: %s -- GetLastError(): %d\n"), arPrivelegeNames[i], GetLastError());
+ TRACE(_T("CDirectoryChangeWatcher notifications may not work as intended due to insufficient access rights/process privileges.\n"));
+ TRACE(_T("File: %s Line: %d\n"), _T(__FILE__), __LINE__);
+ }
+ }
+}
+
+CPrivilegeEnabler & CPrivilegeEnabler::Instance()
+{
+ static CPrivilegeEnabler theInstance;//constructs this first time it's called.
+ return theInstance;
+}
+//
+//
+//
+///////////////////////////////////////////////////////////
+
+
+//
+//
+//////////////////////////////////////////////////////////////////////
+// Construction/Destruction
+//////////////////////////////////////////////////////////////////////
+CDirectoryChangeHandler::CDirectoryChangeHandler()
+: m_nRefCnt( 1 ),
+ m_pDirChangeWatcher( NULL ),
+ m_nWatcherRefCnt( 0L )
+{
+}
+
+CDirectoryChangeHandler::~CDirectoryChangeHandler()
+{
+ UnwatchDirectory();
+}
+
+long CDirectoryChangeHandler::AddRef()
+{
+ return InterlockedIncrement(&m_nRefCnt);
+}
+
+long CDirectoryChangeHandler::Release()
+{
+ long nRefCnt = -1;
+ if( (nRefCnt = InterlockedDecrement(&m_nRefCnt)) == 0 )
+ delete this;
+ return nRefCnt;
+}
+long CDirectoryChangeHandler::CurRefCnt()const
+{
+ return m_nRefCnt;
+}
+
+BOOL CDirectoryChangeHandler::UnwatchDirectory()
+{
+ CSingleLock lock(&m_csWatcher, TRUE);
+ ASSERT( lock.IsLocked() );
+
+ if( m_pDirChangeWatcher )
+ return m_pDirChangeWatcher->UnwatchDirectory( this );
+ return TRUE;
+}
+
+long CDirectoryChangeHandler::ReferencesWatcher(CDirectoryChangeWatcher * pDirChangeWatcher)
+{
+ ASSERT( pDirChangeWatcher );
+ CSingleLock lock(&m_csWatcher, TRUE);
+ if( m_pDirChangeWatcher
+ && m_pDirChangeWatcher != pDirChangeWatcher )
+ {
+ TRACE(_T("CDirectoryChangeHandler...is becoming used by a different CDirectoryChangeWatcher!\n"));
+ TRACE(_T("Directories being handled by this object will now be unwatched.\nThis object is now being used to ")
+ _T("handle changes to a directory watched by different CDirectoryChangeWatcher object, probably on a different directory"));
+
+ if( UnwatchDirectory() )
+ {
+ m_pDirChangeWatcher = pDirChangeWatcher;
+ m_nWatcherRefCnt = 1; //when this reaches 0, set m_pDirChangeWatcher to NULL
+ return m_nWatcherRefCnt;
+ }
+ else
+ {
+ ASSERT( FALSE );//shouldn't get here!
+ }
+ }
+ else
+ {
+ ASSERT( !m_pDirChangeWatcher || m_pDirChangeWatcher == pDirChangeWatcher );
+
+ m_pDirChangeWatcher = pDirChangeWatcher;
+
+ if( m_pDirChangeWatcher )
+ return InterlockedIncrement(&m_nWatcherRefCnt);
+
+ }
+ return m_nWatcherRefCnt;
+}
+
+long CDirectoryChangeHandler::ReleaseReferenceToWatcher(CDirectoryChangeWatcher * pDirChangeWatcher)
+{
+ ASSERT( m_pDirChangeWatcher == pDirChangeWatcher );
+ CSingleLock lock(&m_csWatcher, TRUE);
+ long nRef;
+ if( (nRef = InterlockedDecrement(&m_nWatcherRefCnt)) <= 0L )
+ {
+ m_pDirChangeWatcher = NULL; //Setting this to NULL so that this->UnwatchDirectory() which is called in the dtor
+ //won't call m_pDirChangeWatcher->UnwatchDirecotry(this).
+ //m_pDirChangeWatcher may point to a destructed object depending on how
+ //these classes are being used.
+ m_nWatcherRefCnt = 0L;
+ }
+ return nRef;
+}
+
+//
+//
+// Default implmentations for CDirectoryChangeHandler's virtual functions.
+//
+//
+void CDirectoryChangeHandler::On_FileAdded(const CString & strFileName)
+{
+ TRACE(_T("The following file was added: %s\n"), strFileName);
+}
+
+void CDirectoryChangeHandler::On_FileRemoved(const CString & strFileName)
+{
+ TRACE(_T("The following file was removed: %s\n"), strFileName);
+}
+
+void CDirectoryChangeHandler::On_FileModified(const CString & strFileName)
+{
+ TRACE(_T("The following file was modified: %s\n"), strFileName);
+}
+
+void CDirectoryChangeHandler::On_FileNameChanged(const CString & strOldFileName, const CString & strNewFileName)
+{
+ TRACE(_T("The file %s was RENAMED to %s\n"), strOldFileName, strNewFileName);
+}
+void CDirectoryChangeHandler::On_ReadDirectoryChangesError(DWORD dwError, const CString & strDirectoryName)
+{
+ TRACE(_T("WARNING!!!!!\n") );
+ TRACE(_T("An error has occurred on a watched directory!\n"));
+ TRACE(_T("This directory has become unwatched! -- %s \n"), strDirectoryName);
+ TRACE(_T("ReadDirectoryChangesW has failed! %d"), dwError);
+ ASSERT( FALSE );//you should override this function no matter what. an error will occur someday.
+}
+
+void CDirectoryChangeHandler::On_WatchStarted(DWORD dwError, const CString & strDirectoryName)
+{
+ if( dwError == 0 )
+ TRACE(_T("A watch has begun on the following directory: %s\n"), strDirectoryName);
+ else
+ TRACE(_T("A watch failed to start on the following directory: (Error: %d) %s\n"),dwError, strDirectoryName);
+}
+
+void CDirectoryChangeHandler::On_WatchStopped(const CString & strDirectoryName)
+{
+ TRACE(_T("The watch on the following directory has stopped: %s\n"), strDirectoryName);
+}
+
+bool CDirectoryChangeHandler::On_FilterNotification(DWORD /*dwNotifyAction*/, LPCTSTR /*szFileName*/, LPCTSTR /*szNewFileName*/)
+//
+// bool On_FilterNotification(DWORD dwNotifyAction, LPCTSTR szFileName, LPCTSTR szNewFileName);
+//
+// This function gives your class a chance to filter unwanted notifications.
+//
+// PARAMETERS:
+// DWORD dwNotifyAction -- specifies the event to filter
+// LPCTSTR szFileName -- specifies the name of the file for the event.
+// LPCTSTR szNewFileName -- specifies the new file name of a file that has been renamed.
+//
+// RETURN VALUE:
+// return true from this function, and you will receive the notification.
+// return false from this function, and your class will NOT receive the notification.
+//
+// Valid values of dwNotifyAction:
+// FILE_ACTION_ADDED -- On_FileAdded() is about to be called.
+// FILE_ACTION_REMOVED -- On_FileRemoved() is about to be called.
+// FILE_ACTION_MODIFIED -- On_FileModified() is about to be called.
+// FILE_ACTION_RENAMED_OLD_NAME-- On_FileNameChanged() is about to be call.
+//
+//
+// NOTE: When the value of dwNotifyAction is FILE_ACTION_RENAMED_OLD_NAME,
+// szFileName will be the old name of the file, and szNewFileName will
+// be the new name of the renamed file.
+//
+// The default implementation always returns true, indicating that all notifications will
+// be sent.
+//
+{
+ return true;
+}
+
+void CDirectoryChangeHandler::SetChangedDirectoryName(const CString & strChangedDirName)
+{
+ m_strChangedDirectoryName = strChangedDirName;
+}
+////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////
+
+CDirectoryChangeWatcher::CDirectoryChangeWatcher(bool bAppHasGUI /*= true*/, DWORD dwFilterFlags/*=FILTERS_CHECK_FILE_NAME_ONLY*/)
+: m_hCompPort( NULL )
+ ,m_hThread( NULL )
+ ,m_dwThreadID( 0UL )
+ ,m_bAppHasGUI( bAppHasGUI )
+ ,m_dwFilterFlags( dwFilterFlags == 0? FILTERS_DEFAULT_BEHAVIOR : dwFilterFlags)
+{
+ //NOTE:
+ // The bAppHasGUI variable indicates that you have a message pump associated
+ // with the main thread(or the thread that first calls CDirectoryChangeWatcher::WatchDirectory() ).
+ // Directory change notifications are dispatched to your main thread.
+ //
+ // If your app doesn't have a gui, then pass false. Doing so causes a worker thread
+ // to be created that implements a message pump where it dispatches/executes the notifications.
+ // It's ok to pass false even if your app does have a GUI.
+ // Passing false is required for Console applications, or applications without a message pump.
+ // Note that notifications are fired in a worker thread.
+ //
+
+ //NOTE:
+ //
+ //
+}
+
+CDirectoryChangeWatcher::~CDirectoryChangeWatcher()
+{
+
+ UnwatchAllDirectories();
+
+ if( m_hCompPort )
+ {
+ CloseHandle( m_hCompPort );
+ m_hCompPort = NULL;
+ }
+}
+
+DWORD CDirectoryChangeWatcher::SetFilterFlags(DWORD dwFilterFlags)
+//
+// SetFilterFlags()
+//
+// sets filter behavior for directories watched AFTER this function has been called.
+//
+//
+//
+{
+ DWORD dwOld = m_dwFilterFlags;
+ m_dwFilterFlags = dwFilterFlags;
+ if( m_dwFilterFlags == 0 )
+ m_dwFilterFlags = FILTERS_DEFAULT_BEHAVIOR;//the default.
+ return dwOld;
+}
+
+BOOL CDirectoryChangeWatcher::IsWatchingDirectory(const CString & strDirName)const
+/*********************************************
+ Determines whether or not a directory is being watched
+
+ be carefull that you have the same name path name, including the backslash
+ as was used in the call to WatchDirectory().
+
+ ie:
+ "C:\\Temp"
+ is different than
+ "C:\\Temp\\"
+**********************************************/
+{
+ CSingleLock lock( const_cast<CCriticalSection*>(&m_csDirWatchInfo), TRUE);
+ ASSERT( lock.IsLocked() );
+ int i;
+ if( GetDirWatchInfo(strDirName, i) )
+ return TRUE;
+ return FALSE;
+}
+
+int CDirectoryChangeWatcher::NumWatchedDirectories()const
+{
+ CSingleLock lock(const_cast<CCriticalSection*>(&m_csDirWatchInfo), TRUE);
+ ASSERT( lock.IsLocked() );
+ int nCnt(0),max = m_DirectoriesToWatch.GetSize();
+ for(int i(0); i < max; ++i)
+ {
+ if( m_DirectoriesToWatch[i] != NULL )//array may contain NULL elements.
+ nCnt++;
+ }
+
+ return nCnt;
+}
+
+DWORD CDirectoryChangeWatcher::WatchDirectory(const CString & strDirToWatch,
+ DWORD dwChangesToWatchFor,
+ CDirectoryChangeHandler * pChangeHandler,
+ BOOL bWatchSubDirs /*=FALSE*/,
+ LPCTSTR szIncludeFilter /*=NULL*/,
+ LPCTSTR szExcludeFilter /*=NULL*/
+ )
+/*************************************************************
+FUNCTION: WatchDirectory(const CString & strDirToWatch, --the name of the directory to watch
+ DWORD dwChangesToWatchFor, --the changes to watch for see dsk docs..for ReadDirectoryChangesW
+ CDirectoryChangeHandler * pChangeHandler -- handles changes in specified directory
+ BOOL bWatchSubDirs --specified to watch sub directories of the directory that you want to watch
+ )
+
+PARAMETERS:
+ const CString & strDirToWatch -- specifies the path of the directory to watch.
+ DWORD dwChangesToWatchFor -- specifies flags to be passed to ReadDirectoryChangesW()
+ CDirectoryChangeHandler * -- ptr to an object which will handle notifications of file changes.
+ BOOL bWatchSubDirs -- specifies to watch subdirectories.
+ LPCTSTR szIncludeFilter -- A file pattern string for files that you wish to receive notifications
+ for. See Remarks.
+ LPCTSTR szExcludeFilter -- A file pattern string for files that you do not wish to receive notifications for. See Remarks
+
+ Starts watching the specified directory(and optionally subdirectories) for the specified changes
+
+ When specified changes take place the appropriate CDirectoryChangeHandler::On_Filexxx() function is called.
+
+ dwChangesToWatchFor can be a combination of the following flags, and changes map out to the
+ following functions:
+ FILE_NOTIFY_CHANGE_FILE_NAME -- CDirectoryChangeHandler::On_FileAdded()
+ CDirectoryChangeHandler::On_FileNameChanged,
+ CDirectoryChangeHandler::On_FileRemoved
+ FILE_NOTIFY_CHANGE_DIR_NAME -- CDirectoryChangeHandler::On_FileNameAdded(),
+ CDirectoryChangeHandler::On_FileRemoved
+ FILE_NOTIFY_CHANGE_ATTRIBUTES -- CDirectoryChangeHandler::On_FileModified
+ FILE_NOTIFY_CHANGE_SIZE -- CDirectoryChangeHandler::On_FileModified
+ FILE_NOTIFY_CHANGE_LAST_WRITE -- CDirectoryChangeHandler::On_FileModified
+ FILE_NOTIFY_CHANGE_LAST_ACCESS -- CDirectoryChangeHandler::On_FileModified
+ FILE_NOTIFY_CHANGE_CREATION -- CDirectoryChangeHandler::On_FileModified
+ FILE_NOTIFY_CHANGE_SECURITY -- CDirectoryChangeHandler::On_FileModified?
+
+
+ Returns ERROR_SUCCESS if the directory will be watched,
+ or a windows error code if the directory couldn't be watched.
+ The error code will most likely be related to a call to CreateFile(), or
+ from the initial call to ReadDirectoryChangesW(). It's also possible to get an
+ error code related to being unable to create an io completion port or being unable
+ to start the worker thread.
+
+ This function will fail if the directory to be watched resides on a
+ computer that is not a Windows NT/2000/XP machine.
+
+
+ You can only have one watch specified at a time for any particular directory.
+ Calling this function with the same directory name will cause the directory to be
+ unwatched, and then watched again(w/ the new parameters that have been passed in).
+
+**************************************************************/
+{
+ ASSERT( dwChangesToWatchFor != 0);
+
+ if( strDirToWatch.IsEmpty()
+ || dwChangesToWatchFor == 0
+ || pChangeHandler == NULL )
+ {
+ TRACE(_T("ERROR: You've passed invalid parameters to CDirectoryChangeWatcher::WatchDirectory()\n"));
+ ::SetLastError(ERROR_INVALID_PARAMETER);
+ return ERROR_INVALID_PARAMETER;
+ }
+
+
+ //double check that it's really a directory
+ if( !IsDirectory( strDirToWatch ) )
+ {
+ TRACE(_T("ERROR: CDirectoryChangeWatcher::WatchDirectory() -- %s is not a directory!\n"), strDirToWatch);
+ ::SetLastError(ERROR_BAD_PATHNAME);
+ return ERROR_BAD_PATHNAME;
+ }
+
+ //double check that this directory is not already being watched....
+ //if it is, then unwatch it before restarting it...
+ if( IsWatchingDirectory( strDirToWatch) )
+ {
+ VERIFY(
+ UnwatchDirectory( strDirToWatch )
+ );
+ }
+ //
+ //
+ // Reference this singleton so that privileges for this process are enabled
+ // so that it has required permissions to use the ReadDirectoryChangesW API, etc.
+ //
+ CPrivilegeEnabler::Instance();
+ //
+ //open the directory to watch....
+ HANDLE hDir = CreateFile(strDirToWatch,
+ FILE_LIST_DIRECTORY,
+ FILE_SHARE_READ | FILE_SHARE_WRITE ,//| FILE_SHARE_DELETE, <-- removing FILE_SHARE_DELETE prevents the user or someone else from renaming or deleting the watched directory. This is a good thing to prevent.
+ NULL, //security attributes
+ OPEN_EXISTING,
+ FILE_FLAG_BACKUP_SEMANTICS | //<- the required priviliges for this flag are: SE_BACKUP_NAME and SE_RESTORE_NAME. CPrivilegeEnabler takes care of that.
+ FILE_FLAG_OVERLAPPED, //OVERLAPPED!
+ NULL);
+ if( hDir == INVALID_HANDLE_VALUE )
+ {
+ DWORD dwError = GetLastError();
+ TRACE(_T("CDirectoryChangeWatcher::WatchDirectory() -- Couldn't open directory for monitoring. %d\n"), dwError);
+ ::SetLastError(dwError);//who knows if TRACE will cause GetLastError() to return success...probably won't, but set it manually just for fun.
+ return dwError;
+ }
+ //opened the dir!
+
+ CDirWatchInfo * pDirInfo = new CDirWatchInfo( hDir, strDirToWatch, pChangeHandler, dwChangesToWatchFor, bWatchSubDirs, m_bAppHasGUI, szIncludeFilter, szExcludeFilter, m_dwFilterFlags);
+ if( !pDirInfo )
+ {
+ TRACE(_T("WARNING: Couldn't allocate a new CDirWatchInfo() object --- File: %s Line: %d\n"), _T( __FILE__ ), __LINE__);
+ CloseHandle( hDir );
+ ::SetLastError(ERROR_OUTOFMEMORY);
+ return ERROR_OUTOFMEMORY;
+ }
+
+ //create a IO completion port/or associate this key with
+ //the existing IO completion port
+ m_hCompPort = CreateIoCompletionPort(hDir,
+ m_hCompPort, //if m_hCompPort is NULL, hDir is associated with a NEW completion port,
+ //if m_hCompPort is NON-NULL, hDir is associated with the existing completion port that the handle m_hCompPort references
+ (DWORD)pDirInfo, //the completion 'key'... this ptr is returned from GetQueuedCompletionStatus() when one of the events in the dwChangesToWatchFor filter takes place
+ 0);
+ if( m_hCompPort == NULL )
+ {
+ TRACE(_T("ERROR -- Unable to create I/O Completion port! GetLastError(): %d File: %s Line: %d"), GetLastError(), _T( __FILE__ ), __LINE__ );
+ DWORD dwError = GetLastError();
+ pDirInfo->DeleteSelf( NULL );
+ ::SetLastError(dwError);//who knows what the last error will be after i call pDirInfo->DeleteSelf(), so set it just to make sure
+ return dwError;
+ }
+ else
+ {//completion port created/directory associated w/ it successfully
+
+ //if the thread isn't running start it....
+ //when the thread starts, it will call ReadDirectoryChangesW and wait
+ //for changes to take place
+ if( m_hThread == NULL )
+ {
+ //start the thread
+ CWinThread * pThread = AfxBeginThread(MonitorDirectoryChanges, this);
+ if( !pThread )
+ {//couldn't create the thread!
+ TRACE(_T("CDirectoryChangeWatcher::WatchDirectory()-- AfxBeginThread failed!\n"));
+ pDirInfo->DeleteSelf( NULL );
+ return (GetLastError() == ERROR_SUCCESS)? ERROR_MAX_THRDS_REACHED : GetLastError();
+ }
+ else
+ {
+ m_hThread = pThread->m_hThread;
+ m_dwThreadID = pThread->m_nThreadID;
+ pThread->m_bAutoDelete = TRUE;//pThread is deleted when thread ends....it's TRUE by default(for CWinThread ptrs returned by AfxBeginThread(threadproc, void*)), but just makin sure.
+
+ }
+ }
+ if( m_hThread != NULL )
+ {//thread is running,
+ //signal the thread to issue the initial call to
+ //ReadDirectoryChangesW()
+ DWORD dwStarted = pDirInfo->StartMonitor( m_hCompPort );
+
+ if( dwStarted != ERROR_SUCCESS )
+ {//there was a problem!
+ TRACE(_T("Unable to watch directory: %s -- GetLastError(): %d\n"), dwStarted);
+ pDirInfo->DeleteSelf( NULL );
+ ::SetLastError(dwStarted);//I think this'll set the Err object in a VB app.....
+ return dwStarted;
+ }
+ else
+ {//ReadDirectoryChangesW was successfull!
+ //add the directory info to the first empty slot in the array
+
+ //associate the pChangeHandler with this object
+ pChangeHandler->ReferencesWatcher( this );//reference is removed when directory is unwatched.
+ //CDirWatchInfo::DeleteSelf() must now be called w/ this CDirectoryChangeWatcher pointer becuse
+ //of a reference count
+
+ //save the CDirWatchInfo* so I'll be able to use it later.
+ VERIFY( AddToWatchInfo( pDirInfo ) );
+ SetLastError(dwStarted);
+ return dwStarted;
+ }
+
+ }
+ else
+ {
+ ASSERT(FALSE);//control path shouldn't get here
+ ::SetLastError(ERROR_MAX_THRDS_REACHED);
+ return ERROR_MAX_THRDS_REACHED;
+ }
+
+ }
+ ASSERT( FALSE );//shouldn't get here.
+}
+
+BOOL CDirectoryChangeWatcher::UnwatchAllDirectories()
+{
+
+ //unwatch all of the watched directories
+ //delete all of the CDirWatchInfo objects,
+ //kill off the worker thread
+ if( m_hThread != NULL )
+ {
+ ASSERT( m_hCompPort != NULL );
+
+ CSingleLock lock( &m_csDirWatchInfo, TRUE);
+ ASSERT( lock.IsLocked() );
+
+ CDirWatchInfo * pDirInfo;
+ //Unwatch each of the watched directories
+ //and delete the CDirWatchInfo associated w/ that directory...
+ int max = m_DirectoriesToWatch.GetSize();
+ for(int i = 0; i < max; ++i)
+ {
+ if( (pDirInfo = m_DirectoriesToWatch[i]) != NULL )
+ {
+ VERIFY( pDirInfo->UnwatchDirectory( m_hCompPort ) );
+
+ m_DirectoriesToWatch.SetAt(i, NULL) ;
+ pDirInfo->DeleteSelf(this);
+ }
+
+ }
+ m_DirectoriesToWatch.RemoveAll();
+ //kill off the thread
+ PostQueuedCompletionStatus(m_hCompPort, 0, 0, NULL);//The thread will catch this and exit the thread
+ //wait for it to exit
+ WaitForSingleObject(m_hThread, INFINITE);
+ //CloseHandle( m_hThread );//Since thread was started w/ AfxBeginThread() this handle is closed automatically, closing it again will raise an exception
+ m_hThread = NULL;
+ m_dwThreadID = 0UL;
+
+ //close the completion port...
+ CloseHandle( m_hCompPort );
+ m_hCompPort = NULL;
+
+
+ return TRUE;
+ }
+ else
+ {
+#ifdef _DEBUG
+ //make sure that there aren't any
+ //CDirWatchInfo objects laying around... they should have all been destroyed
+ //and removed from the array m_DirectoriesToWatch
+ if( m_DirectoriesToWatch.GetSize() > 0 )
+ {
+ for(int i = 0; i < m_DirectoriesToWatch.GetSize(); ++i)
+ {
+ ASSERT( m_DirectoriesToWatch[i] == NULL );
+ }
+ }
+#endif
+ }
+ return FALSE;
+}
+
+BOOL CDirectoryChangeWatcher::UnwatchDirectory(const CString & strDirToStopWatching)
+/***************************************************************
+FUNCTION: UnwatchDirectory(const CString & strDirToStopWatching -- if this directory is being watched, the watch is stopped
+
+****************************************************************/
+{
+ BOOL bRetVal = FALSE;
+
+
+
+ if( m_hCompPort != NULL )//The io completion port must be open
+ {
+ ASSERT( !strDirToStopWatching.IsEmpty() );
+
+ CSingleLock lock(&m_csDirWatchInfo, TRUE);
+ ASSERT( lock.IsLocked() );
+ int nIdx = -1;
+ CDirWatchInfo * pDirInfo = GetDirWatchInfo(strDirToStopWatching, nIdx);
+ if( pDirInfo != NULL
+ && nIdx != -1 )
+ {
+
+ //stop watching this directory
+ VERIFY( pDirInfo->UnwatchDirectory( m_hCompPort ) );
+
+ //cleanup the object used to watch the directory
+ m_DirectoriesToWatch.SetAt(nIdx, NULL);
+ pDirInfo->DeleteSelf(this);
+ bRetVal = TRUE;
+ }
+ }
+
+ return bRetVal;
+}
+
+BOOL CDirectoryChangeWatcher::UnwatchDirectory(CDirectoryChangeHandler * pChangeHandler)
+/************************************
+
+ This function is called from the dtor of CDirectoryChangeHandler automatically,
+ but may also be called by a programmer because it's public...
+
+ A single CDirectoryChangeHandler may be used for any number of watched directories.
+
+ Unwatch any directories that may be using this
+ CDirectoryChangeHandler * pChangeHandler to handle changes to a watched directory...
+
+ The CDirWatchInfo::m_pChangeHandler member of objects in the m_DirectoriesToWatch
+ array will == pChangeHandler if that handler is being used to handle changes to a directory....
+************************************/
+{
+ ASSERT( pChangeHandler );
+
+ CSingleLock lock(&m_csDirWatchInfo, TRUE);
+
+ ASSERT( lock.IsLocked() );
+
+ int nUnwatched = 0;
+ int nIdx = -1;
+ CDirWatchInfo * pDirInfo;
+ //
+ // go through and unwatch any directory that is
+ // that is using this pChangeHandler as it's file change notification handler.
+ //
+ while( (pDirInfo = GetDirWatchInfo( pChangeHandler, nIdx )) != NULL )
+ {
+ VERIFY( pDirInfo->UnwatchDirectory( m_hCompPort ) );
+
+ nUnwatched++;
+ m_DirectoriesToWatch.SetAt(nIdx, NULL);
+ pDirInfo->DeleteSelf(this);
+ }
+ return (BOOL)(nUnwatched != 0);
+}
+
+BOOL CDirectoryChangeWatcher::UnwatchDirectoryBecauseOfError(CDirWatchInfo * pWatchInfo)
+//
+// Called in the worker thread in the case that ReadDirectoryChangesW() fails
+// during normal operation. One way to force this to happen is to watch a folder
+// using a UNC path and changing that computer's IP address.
+//
+{
+ ASSERT( pWatchInfo );
+ ASSERT( m_dwThreadID == GetCurrentThreadId() );//this should be called from the worker thread only.
+ BOOL bRetVal = FALSE;
+ if( pWatchInfo )
+ {
+ CSingleLock lock(&m_csDirWatchInfo, TRUE);
+
+ ASSERT( lock.IsLocked() );
+ int nIdx = -1;
+ if( GetDirWatchInfo(pWatchInfo, nIdx) == pWatchInfo )
+ {
+ // we are actually watching this....
+
+ //
+ // Remove this CDirWatchInfo object from the list of watched directories.
+ //
+ m_DirectoriesToWatch.SetAt(nIdx, NULL);//mark the space as free for the next watch...
+
+ //
+ // and delete it...
+ //
+
+ pWatchInfo->DeleteSelf(this);
+
+ }
+
+ }
+ return bRetVal;
+}
+
+int CDirectoryChangeWatcher::AddToWatchInfo(CDirectoryChangeWatcher::CDirWatchInfo * pWatchInfo)
+//
+//
+// To add the CDirWatchInfo * to an array.
+// The array is used to keep track of which directories
+// are being watched.
+//
+// Add the ptr to the first non-null slot in the array.
+{
+ CSingleLock lock( &m_csDirWatchInfo, TRUE);
+ ASSERT( lock.IsLocked() );
+
+ //first try to add it to the first empty slot in m_DirectoriesToWatch
+ int max = m_DirectoriesToWatch.GetSize();
+ for(int i = 0; i < max; ++i)
+ {
+ if( m_DirectoriesToWatch[i] == NULL )
+ {
+ m_DirectoriesToWatch[i] = pWatchInfo;
+ break;
+ }
+ }
+ if( i == max )
+ {
+ // there where no empty slots, add it to the end of the array
+ try{
+ i = m_DirectoriesToWatch.Add( pWatchInfo );
+ }
+ catch(CMemoryException * e){
+ e->ReportError();
+ e->Delete();//??? delete this? I thought CMemoryException objects where pre allocated in mfc? -- sample code in msdn does, so will i
+ i = -1;
+ }
+ }
+
+ return (BOOL)(i != -1);
+}
+
+//
+// functions for retrieving the directory watch info based on different parameters
+//
+CDirectoryChangeWatcher::CDirWatchInfo * CDirectoryChangeWatcher::GetDirWatchInfo(const CString & strDirName, int & ref_nIdx)const
+{
+ if( strDirName.IsEmpty() )// can't be watching a directory if it's you don't pass in the name of it...
+ return FALSE; //
+
+ CSingleLock lock(const_cast<CCriticalSection*>(&m_csDirWatchInfo), TRUE);
+
+ int max = m_DirectoriesToWatch.GetSize();
+ CDirWatchInfo * p = NULL;
+ for(int i = 0; i < max; ++i )
+ {
+ if( (p = m_DirectoriesToWatch[i]) != NULL
+ && p->m_strDirName.CompareNoCase( strDirName ) == 0 )
+ {
+ ref_nIdx = i;
+ return p;
+ }
+ }
+
+ return NULL;//NOT FOUND
+}
+
+CDirectoryChangeWatcher::CDirWatchInfo * CDirectoryChangeWatcher::GetDirWatchInfo(CDirectoryChangeWatcher::CDirWatchInfo * pWatchInfo, int & ref_nIdx)const
+{
+ ASSERT( pWatchInfo != NULL );
+
+ CSingleLock lock( const_cast<CCriticalSection*>(&m_csDirWatchInfo), TRUE);
+ int i(0), max = m_DirectoriesToWatch.GetSize();
+ CDirWatchInfo * p;
+ for(; i < max; ++i)
+ {
+ if( (p = m_DirectoriesToWatch[i]) != NULL
+ && p == pWatchInfo )
+ {
+ ref_nIdx = i;
+ return p;
+ }
+ }
+ return NULL;//NOT FOUND
+}
+
+CDirectoryChangeWatcher::CDirWatchInfo * CDirectoryChangeWatcher::GetDirWatchInfo(CDirectoryChangeHandler * pChangeHandler, int & ref_nIdx)const
+{
+ ASSERT( pChangeHandler != NULL );
+ CSingleLock lock( const_cast<CCriticalSection*>(&m_csDirWatchInfo), TRUE);
+ int i(0),max = m_DirectoriesToWatch.GetSize();
+ CDirWatchInfo * p;
+ for( ; i < max; ++i)
+ {
+ if( (p = m_DirectoriesToWatch[i]) != NULL
+ && p->GetRealChangeHandler() == pChangeHandler )
+ {
+ ref_nIdx = i;
+ return p;
+ }
+ }
+ return NULL;//NOT FOUND
+}
+
+long CDirectoryChangeWatcher::ReleaseReferenceToWatcher(CDirectoryChangeHandler * pChangeHandler)
+{
+ ASSERT( pChangeHandler );
+ return pChangeHandler->ReleaseReferenceToWatcher(this);
+}
+
+CDirectoryChangeWatcher::CDirWatchInfo::CDirWatchInfo(HANDLE hDir,
+ const CString & strDirectoryName,
+ CDirectoryChangeHandler * pChangeHandler,
+ DWORD dwChangeFilter,
+ BOOL bWatchSubDir,
+ bool bAppHasGUI,
+ LPCTSTR szIncludeFilter,
+ LPCTSTR szExcludeFilter,
+ DWORD dwFilterFlags)
+ : m_pChangeHandler( NULL ),
+ m_hDir(hDir),
+ m_dwChangeFilter( dwChangeFilter ),
+ m_bWatchSubDir( bWatchSubDir ),
+ m_strDirName( strDirectoryName ),
+ m_dwBufLength(0),
+ m_dwReadDirError(ERROR_SUCCESS),
+ m_StartStopEvent(FALSE, TRUE), //NOT SIGNALLED, MANUAL RESET
+ m_RunningState( RUNNING_STATE_NOT_SET )
+{
+
+ ASSERT( hDir != INVALID_HANDLE_VALUE
+ && !strDirectoryName.IsEmpty() );
+
+ //
+ // This object 'decorates' the pChangeHandler passed in
+ // so that notifications fire in the context a thread other than
+ // CDirectoryChangeWatcher::MonitorDirectoryChanges()
+ //
+ // Supports the include and exclude filters
+ //
+ //
+ m_pChangeHandler = new CDelayedDirectoryChangeHandler( pChangeHandler, bAppHasGUI, szIncludeFilter, szExcludeFilter, dwFilterFlags );
+ if( m_pChangeHandler )
+ m_pChangeHandler->SetPartialPathOffset( m_strDirName );//to support FILTERS_CHECK_PARTIAL_PATH..this won't change for the duration of the watch, so set it once... HERE!
+ ASSERT( m_pChangeHandler );
+
+ ASSERT( GetChangeHandler() );
+ ASSERT( GetRealChangeHandler() );
+ if( GetRealChangeHandler() )
+ GetRealChangeHandler()->AddRef();
+
+ memset(&m_Overlapped, 0, sizeof(m_Overlapped));
+ //memset(m_Buffer, 0, sizeof(m_Buffer));
+}
+
+CDirectoryChangeWatcher::CDirWatchInfo::~CDirWatchInfo()
+{
+ if( GetChangeHandler() )
+ {//If this call to CDirectoryChangeHandler::Release() causes m_pChangeHandler to delete itself,
+ //the dtor for CDirectoryChangeHandler will call CDirectoryChangeWatcher::UnwatchDirectory( CDirectoryChangeHandler * ),
+ //which will make try to delete this object again.
+ //if m_pChangeHandler is NULL, it won't try to delete this object again...
+ CDirectoryChangeHandler * pTmp = SetRealDirectoryChangeHandler( NULL );
+ if( pTmp )
+ pTmp->Release();
+ else{
+ ASSERT( FALSE );
+ }
+ }
+
+ CloseDirectoryHandle();
+
+ delete m_pChangeHandler;
+ m_pChangeHandler = NULL;
+
+}
+void CDirectoryChangeWatcher::CDirWatchInfo::DeleteSelf(CDirectoryChangeWatcher * pWatcher)
+//
+// There's a reason for this function!
+//
+// the dtor is private to enforce that it is used.
+//
+//
+// pWatcher can be NULL only if CDirecotryChangeHandler::ReferencesWatcher() has NOT been called.
+// ie: in certain sections of WatchDirectory() it's ok to pass this w/ NULL, but no where else.
+//
+{
+ //ASSERT( pWatcher != NULL );
+
+
+ ASSERT( GetRealChangeHandler() );
+ if( pWatcher )
+ {
+ //
+ //
+ // Before this object is deleted, the CDirectoryChangeHandler object
+ // needs to release it's reference count to the CDirectoryChangeWatcher object.
+ // I might forget to do this since I getting rid of CDirWatchInfo objects
+ // in more than one place...hence the reason for this function.
+ //
+ pWatcher->ReleaseReferenceToWatcher( GetRealChangeHandler() );
+ }
+
+ delete this;
+}
+
+CDelayedDirectoryChangeHandler* CDirectoryChangeWatcher::CDirWatchInfo::GetChangeHandler() const
+{
+ return m_pChangeHandler;
+}
+
+CDirectoryChangeHandler * CDirectoryChangeWatcher::CDirWatchInfo::GetRealChangeHandler() const
+//
+// The 'real' change handler is the CDirectoryChangeHandler object
+// passed to CDirectoryChangeWatcher::WatchDirectory() -- this is the object
+// that really handles the changes.
+//
+{
+ ASSERT( m_pChangeHandler );
+ return m_pChangeHandler->GetRealChangeHandler();
+}
+
+CDirectoryChangeHandler * CDirectoryChangeWatcher::CDirWatchInfo::SetRealDirectoryChangeHandler(CDirectoryChangeHandler * pChangeHandler)
+//
+// Allows you to switch out, at run time, which object really handles the change notifications.
+//
+{
+ CDirectoryChangeHandler * pOld = GetRealChangeHandler();
+ m_pChangeHandler->GetRealChangeHandler() = pChangeHandler;
+ return pOld;
+}
+
+BOOL CDirectoryChangeWatcher::CDirWatchInfo::CloseDirectoryHandle()
+//
+// Closes the directory handle that was opened in CDirectoryChangeWatcher::WatchDirecotry()
+//
+//
+{
+ BOOL b = TRUE;
+ if( m_hDir != INVALID_HANDLE_VALUE )
+ {
+ b = CloseHandle(m_hDir);
+ m_hDir = INVALID_HANDLE_VALUE;
+ }
+ return b;
+}
+
+DWORD CDirectoryChangeWatcher::CDirWatchInfo::StartMonitor(HANDLE hCompPort)
+/*********************************************
+ Sets the running state of the object to perform the initial call to ReadDirectoryChangesW()
+ , wakes up the thread waiting on GetQueuedCompletionStatus()
+ and waits for an event to be set before returning....
+
+ The return value is either ERROR_SUCCESS if ReadDirectoryChangesW is successfull,
+ or is the value of GetLastError() for when ReadDirectoryChangesW() failed.
+**********************************************/
+{
+ ASSERT( hCompPort );
+
+ //Guard the properties of this object
+ VERIFY( LockProperties() );
+
+
+
+ m_RunningState = RUNNING_STATE_START_MONITORING;//set the state member to indicate that the object is to START monitoring the specified directory
+ PostQueuedCompletionStatus(hCompPort, sizeof(this), (DWORD)this, &m_Overlapped);//make the thread waiting on GetQueuedCompletionStatus() wake up
+
+ VERIFY( UnlockProperties() );//unlock this object so that the thread can get at them...
+
+ //wait for signal that the initial call
+ //to ReadDirectoryChanges has been made
+ DWORD dwWait = 0;
+ do{
+ dwWait = WaitForSingleObject(m_StartStopEvent, 10 * 1000);
+ if( dwWait != WAIT_OBJECT_0 )
+ {
+ //
+ // shouldn't ever see this one....but just in case you do, notify me of the problem wesj@hotmail.com.
+ //
+ TRACE(_T("WARNING! Possible lockup detected. FILE: %s Line: %d\n"), _T( __FILE__ ), __LINE__);
+ }
+ } while( dwWait != WAIT_OBJECT_0 );
+
+ ASSERT( dwWait == WAIT_OBJECT_0 );
+ m_StartStopEvent.ResetEvent();
+
+ return m_dwReadDirError;//This value is set in the worker thread when it first calls ReadDirectoryChangesW().
+}
+
+BOOL CDirectoryChangeWatcher::CDirWatchInfo::UnwatchDirectory(HANDLE hCompPort)
+/*******************************************
+
+ Sets the running state of the object to stop monitoring a directory,
+ Causes the worker thread to wake up and to stop monitoring the dierctory
+
+********************************************/
+{
+ ASSERT( hCompPort );
+ //
+ // Signal that the worker thread is to stop watching the directory
+ //
+ if( SignalShutdown(hCompPort) )
+ {
+ //and wait for the thread to signal that it has shutdown
+ return WaitForShutdown();
+
+ }
+ return FALSE;
+}
+
+BOOL CDirectoryChangeWatcher::CDirWatchInfo::SignalShutdown( HANDLE hCompPort )
+//added to fix a bug -- this will be called normally by UnwatchDirectory(HANDLE)
+// and abnormally by the worker thread in the case that ReadDirectoryChangesW fails -- see code.
+//
+// Signals the worker thread(via the I/O completion port) that it is to stop watching the
+// directory for this object, and then returns.
+//
+{
+ BOOL bRetVal = FALSE;
+ ASSERT( hCompPort );
+ ASSERT( m_hDir != INVALID_HANDLE_VALUE );
+ //Lock the properties so that they aren't modified by another thread
+ VERIFY( LockProperties() ); //unlikey to fail...
+
+ //set the state member to indicate that the object is to stop monitoring the
+ //directory that this CDirWatchInfo is responsible for...
+ m_RunningState = CDirectoryChangeWatcher::CDirWatchInfo::RUNNING_STATE_STOP;
+ //put this object in the I/O completion port... GetQueuedCompletionStatus() will return it inside the worker thread.
+ bRetVal = PostQueuedCompletionStatus(hCompPort, sizeof(CDirWatchInfo*), (DWORD)this, &m_Overlapped);
+
+ if( !bRetVal )
+ {
+ TRACE(_T("PostQueuedCompletionStatus() failed! GetLastError(): %d\n"), GetLastError());
+ }
+ VERIFY( UnlockProperties() );
+
+ return bRetVal;
+}
+
+BOOL CDirectoryChangeWatcher::CDirWatchInfo::WaitForShutdown()
+//
+// This is to be called some time after SignalShutdown().
+//
+//
+{
+ ASSERT_VALID(&m_StartStopEvent);
+
+ //Wait for the Worker thread to indicate that the watch has been stopped
+ DWORD dwWait;
+ bool bWMQuitReceived = false;
+ do{
+ dwWait = MsgWaitForMultipleObjects(1, &m_StartStopEvent.m_hObject, FALSE, 5000, QS_ALLINPUT);//wait five seconds
+ switch( dwWait )
+ {
+ case WAIT_OBJECT_0:
+ //handle became signalled!
+ break;
+ case WAIT_OBJECT_0 + 1:
+ {
+ //This thread awoke due to sent/posted message
+ //process the message Q
+ //
+ // There is a message in this thread's queue, so
+ // MsgWaitForMultipleObjects returned.
+ // Process those messages, and wait again.
+
+ MSG msg;
+ while( PeekMessage(&msg, NULL, 0,0, PM_REMOVE ) )
+ {
+ if( msg.message != WM_QUIT)
+ {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+ else
+ {
+ /****
+ This appears to be causing quite a lot of pain, to quote Mustafa.
+
+ //it's the WM_QUIT message, put it back in the Q and
+ // exit this function
+ PostMessage(msg.hwnd, msg.message, msg.wParam, msg.lParam );
+ bWMQuitReceived = true;
+
+ ****/
+ break;
+ }
+ }
+ }break;
+ case WAIT_TIMEOUT:
+ {
+ TRACE(_T("WARNING: Possible Deadlock detected! ThreadID: %d File: %s Line: %d\n"), GetCurrentThreadId(), _T(__FILE__), __LINE__);
+ }break;
+ }//end switch(dwWait)
+ }while( dwWait != WAIT_OBJECT_0 && !bWMQuitReceived );
+
+
+
+ ASSERT( dwWait == WAIT_OBJECT_0 || bWMQuitReceived);
+
+ m_StartStopEvent.ResetEvent();
+
+ return (BOOL) (dwWait == WAIT_OBJECT_0 || bWMQuitReceived);
+}
+
+
+UINT CDirectoryChangeWatcher::MonitorDirectoryChanges(LPVOID lpvThis)
+/********************************************
+ The worker thread function which monitors directory changes....
+********************************************/
+{
+ DWORD numBytes;
+
+ CDirWatchInfo * pdi;
+ LPOVERLAPPED lpOverlapped;
+
+ CDirectoryChangeWatcher * pThis = reinterpret_cast<CDirectoryChangeWatcher*>(lpvThis);
+ ASSERT( pThis );
+
+ pThis->On_ThreadInitialize();
+
+
+ do
+ {
+ // Retrieve the directory info for this directory
+ // through the io port's completion key
+ if( !GetQueuedCompletionStatus( pThis->m_hCompPort,
+ &numBytes,
+ (LPDWORD) &pdi,//<-- completion Key
+ &lpOverlapped,
+ INFINITE) )
+ {//The io completion request failed...
+ //probably because the handle to the directory that
+ //was used in a call to ReadDirectoryChangesW has been closed.
+
+ //
+ // calling pdi->CloseDirectoryHandle() will cause GetQueuedCompletionStatus() to return false.
+ //
+ //
+ if( !pdi
+ || ( pdi && AfxIsValidAddress(pdi, sizeof(CDirectoryChangeWatcher::CDirWatchInfo)))
+ && pdi->m_hDir != INVALID_HANDLE_VALUE //the directory handle is still open! (we expect this when after we close the directory handle )
+ )
+ {
+#ifdef _DEBUG
+ TRACE(_T("GetQueuedCompletionStatus() returned FALSE\nGetLastError(): %d Completion Key: %p lpOverlapped: %p\n"), GetLastError(), pdi, lpOverlapped);
+ MessageBeep( static_cast<UINT>(-1) );
+#endif
+ }
+ }
+
+ if ( pdi )//pdi will be null if I call PostQueuedCompletionStatus(m_hCompPort, 0,0,NULL);
+ {
+ //
+ // The following check to AfxIsValidAddress() should go off in the case
+ // that I have deleted this CDirWatchInfo object, but it was still in
+ // "in the Queue" of the i/o completion port from a previous overlapped operation.
+ //
+ ASSERT( AfxIsValidAddress(pdi,
+ sizeof(CDirectoryChangeWatcher::CDirWatchInfo)) );
+ /***********************************
+ The CDirWatchInfo::m_RunningState is pretty much the only member
+ of CDirWatchInfo that can be modified from the other thread.
+ The functions StartMonitor() and UnwatchDirecotry() are the functions that
+ can modify that variable.
+
+ So that I'm sure that I'm getting the right value,
+ I'm using a critical section to guard against another thread modyfying it when I want
+ to read it...
+
+ ************************************/
+ bool bObjectShouldBeOk = true;
+ try{
+ VERIFY( pdi->LockProperties() );//don't give the main thread a chance to change this object
+ }
+ catch(...){
+ //any sort of exception here indicates I've
+ //got a hosed object.
+ TRACE(_T("CDirectoryChangeWatcher::MonitorDirectoryChanges() -- pdi->LockProperties() raised an exception!\n"));
+ bObjectShouldBeOk = false;
+ }
+ if( bObjectShouldBeOk )
+ {
+ //while we're working with this object...
+
+ CDirWatchInfo::eRunningState Run_State = pdi->m_RunningState ;
+
+ VERIFY( pdi->UnlockProperties() );//let another thread back at the properties...
+ /***********************************
+ Unlock it so that there isn't a DEADLOCK if
+ somebody tries to call a function which will
+ cause CDirWatchInfo::UnwatchDirectory() to be called
+ from within the context of this thread (eg: a function called because of
+ the handler for one of the CDirectoryChangeHandler::On_Filexxx() functions)
+
+ ************************************/
+
+ ASSERT( pdi->GetChangeHandler() );
+ switch( Run_State )
+ {
+ case CDirWatchInfo::RUNNING_STATE_START_MONITORING:
+ {
+ //Issue the initial call to ReadDirectoryChangesW()
+
+ if( !ReadDirectoryChangesW( pdi->m_hDir,
+ pdi->m_Buffer,//<--FILE_NOTIFY_INFORMATION records are put into this buffer
+ READ_DIR_CHANGE_BUFFER_SIZE,
+ pdi->m_bWatchSubDir,
+ pdi->m_dwChangeFilter,
+ &pdi->m_dwBufLength,//this var not set when using asynchronous mechanisms...
+ &pdi->m_Overlapped,
+ NULL) )//no completion routine!
+ {
+ pdi->m_dwReadDirError = GetLastError();
+ if( pdi->GetChangeHandler() )
+ pdi->GetChangeHandler()->On_WatchStarted(pdi->m_dwReadDirError, pdi->m_strDirName);
+ }
+ else
+ {//read directory changes was successful!
+ //allow it to run normally
+ pdi->m_RunningState = CDirWatchInfo::RUNNING_STATE_NORMAL;
+ pdi->m_dwReadDirError = ERROR_SUCCESS;
+ if( pdi->GetChangeHandler() )
+ pdi->GetChangeHandler()->On_WatchStarted(ERROR_SUCCESS, pdi->m_strDirName );
+ }
+ pdi->m_StartStopEvent.SetEvent();//signall that the ReadDirectoryChangesW has been called
+ //check CDirWatchInfo::m_dwReadDirError to see whether or not ReadDirectoryChangesW succeeded...
+
+ //
+ // note that pdi->m_dwReadDirError is the value returned by WatchDirectory()
+ //
+
+
+ }break;
+ case CDirWatchInfo::RUNNING_STATE_STOP:
+ {
+ //We want to shut down the monitoring of the directory
+ //that pdi is managing...
+
+ if( pdi->m_hDir != INVALID_HANDLE_VALUE )
+ {
+ //Since I've previously called ReadDirectoryChangesW() asynchronously, I am waiting
+ //for it to return via GetQueuedCompletionStatus(). When I close the
+ //handle that ReadDirectoryChangesW() is waiting on, it will
+ //cause GetQueuedCompletionStatus() to return again with this pdi object....
+ // Close the handle, and then wait for the call to GetQueuedCompletionStatus()
+ //to return again by breaking out of the switch, and letting GetQueuedCompletionStatus()
+ //get called again
+ pdi->CloseDirectoryHandle();
+ pdi->m_RunningState = CDirWatchInfo::RUNNING_STATE_STOP_STEP2;//back up step...GetQueuedCompletionStatus() will still need to return from the last time that ReadDirectoryChangesW() was called.....
+
+ //
+ // The watch has been stopped, tell the client about it
+ // if( pdi->GetChangeHandler() )
+ pdi->GetChangeHandler()->On_WatchStopped( pdi->m_strDirName );
+ }
+ else
+ {
+ //either we weren't watching this direcotry in the first place,
+ //or we've already stopped monitoring it....
+ pdi->m_StartStopEvent.SetEvent();//set the event that ReadDirectoryChangesW has returned and no further calls to it will be made...
+ }
+
+
+ }break;
+ case CDirWatchInfo::RUNNING_STATE_STOP_STEP2:
+ {
+
+ //GetQueuedCompletionStatus() has returned from the last
+ //time that ReadDirectoryChangesW was called...
+ //Using CloseHandle() on the directory handle used by
+ //ReadDirectoryChangesW will cause it to return via GetQueuedCompletionStatus()....
+ if( pdi->m_hDir == INVALID_HANDLE_VALUE )
+ pdi->m_StartStopEvent.SetEvent();//signal that no further calls to ReadDirectoryChangesW will be made
+ //and this pdi can be deleted
+ else
+ {//for some reason, the handle is still open..
+
+ pdi->CloseDirectoryHandle();
+
+ //wait for GetQueuedCompletionStatus() to return this pdi object again
+
+
+ }
+
+ }break;
+
+ case CDirWatchInfo::RUNNING_STATE_NORMAL:
+ {
+
+ if( pdi->GetChangeHandler() )
+ pdi->GetChangeHandler()->SetChangedDirectoryName( pdi->m_strDirName );
+
+ DWORD dwReadBuffer_Offset = 0UL;
+
+ //process the FILE_NOTIFY_INFORMATION records:
+ CFileNotifyInformation notify_info( (LPBYTE)pdi->m_Buffer, READ_DIR_CHANGE_BUFFER_SIZE);
+
+ pThis->ProcessChangeNotifications(notify_info, pdi, dwReadBuffer_Offset);
+
+
+ // Changes have been processed,
+ // Reissue the watch command
+ //
+ if( !ReadDirectoryChangesW( pdi->m_hDir,
+ pdi->m_Buffer + dwReadBuffer_Offset,//<--FILE_NOTIFY_INFORMATION records are put into this buffer
+ READ_DIR_CHANGE_BUFFER_SIZE - dwReadBuffer_Offset,
+ pdi->m_bWatchSubDir,
+ pdi->m_dwChangeFilter,
+ &pdi->m_dwBufLength,//this var not set when using asynchronous mechanisms...
+ &pdi->m_Overlapped,
+ NULL) )//no completion routine!
+ {
+ //
+ // NOTE:
+ // In this case the thread will not wake up for
+ // this pdi object because it is no longer associated w/
+ // the I/O completion port...there will be no more outstanding calls to ReadDirectoryChangesW
+ // so I have to skip the normal shutdown routines(normal being what happens when CDirectoryChangeWatcher::UnwatchDirectory() is called.
+ // and close this up, & cause it to be freed.
+ //
+ TRACE(_T("WARNING: ReadDirectoryChangesW has failed during normal operations...failed on directory: %s\n"), pdi->m_strDirName);
+
+ ASSERT( pThis );
+ //
+ // To help insure that this has been unwatched by the time
+ // the main thread processes the On_ReadDirectoryChangesError() notification
+ // bump the thread priority up temporarily. The reason this works is because the notification
+ // is really posted to another thread's message queue,...by setting this thread's priority
+ // to highest, this thread will get to shutdown the watch by the time the other thread has a chance
+ // to handle it. *note* not technically guaranteed 100% to be the case, but in practice it'll work.
+ int nOldThreadPriority = GetThreadPriority( GetCurrentThread() );
+ SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST);
+
+ //
+ // Notify the client object....(a CDirectoryChangeHandler derived class)
+ //
+ try{
+ pdi->m_dwReadDirError = GetLastError();
+ pdi->GetChangeHandler()->On_ReadDirectoryChangesError( pdi->m_dwReadDirError, pdi->m_strDirName );
+
+
+ //Do the shutdown
+ pThis->UnwatchDirectoryBecauseOfError( pdi );
+ //pdi = NULL; <-- DO NOT set this to NULL, it will cause this worker thread to exit.
+ //pdi is INVALID at this point!!
+ }
+ catch(...)
+ {
+ //just in case of exception, this thread will be set back to
+ //normal priority.
+ }
+ //
+ // Set the thread priority back to normal.
+ //
+ SetThreadPriority(GetCurrentThread(), nOldThreadPriority);
+
+ }
+ else
+ {//success, continue as normal
+ pdi->m_dwReadDirError = ERROR_SUCCESS;
+ }
+ }break;
+ default:
+ TRACE(_T("MonitorDirectoryChanges() -- how did I get here?\n"));
+ break;//how did I get here?
+ }//end switch( pdi->m_RunningState )
+
+
+
+ }//end if( bObjectShouldBeOk )
+ }//end if( pdi )
+ } while( pdi );
+
+ pThis->On_ThreadExit();
+ return 0; //thread is ending
+}
+
+void CDirectoryChangeWatcher::ProcessChangeNotifications(IN CFileNotifyInformation & notify_info,
+ IN CDirectoryChangeWatcher::CDirWatchInfo * pdi,
+ OUT DWORD & ref_dwReadBuffer_Offset//used in case ...see case for FILE_ACTION_RENAMED_OLD_NAME
+ )
+/////////////////////////////////////////////////////////////
+//
+// Processes the change notifications and dispatches the handling of the
+// notifications to the CDirectoryChangeHandler object passed to WatchDirectory()
+//
+/////////////////////////////////////////////////////////////
+{
+ //
+ // Sanity check...
+ // this function should only be called by the worker thread.
+ //
+ ASSERT( m_dwThreadID == GetCurrentThreadId() );
+
+ // Validate parameters...
+ //
+ ASSERT( pdi );
+ ASSERT( AfxIsValidAddress(pdi, sizeof(CDirectoryChangeWatcher::CDirWatchInfo) ) );
+
+ if( !pdi || !AfxIsValidAddress(pdi, sizeof(CDirectoryChangeWatcher::CDirWatchInfo)) )
+ {
+ TRACE(_T("Invalid arguments to CDirectoryChangeWatcher::ProcessChangeNotifications() -- pdi is invalid!\n"));
+ TRACE(_T("File: %s Line: %d"), _T( __FILE__ ), __LINE__ );
+ return;
+ }
+
+
+
+ DWORD dwLastAction = 0;
+ ref_dwReadBuffer_Offset = 0UL;
+
+
+ CDirectoryChangeHandler * pChangeHandler = pdi->GetChangeHandler();
+ //CDelayedDirectoryChangeHandler * pChangeHandler = pdi->GetChangeHandler();
+ ASSERT( pChangeHandler );
+ ASSERT( AfxIsValidAddress(pChangeHandler, sizeof(CDirectoryChangeHandler)) );
+ //ASSERT( AfxIsValidAddress(pChangeHandler, sizeof(CDelayedDirectoryChangeHandler)) );
+ if( !pChangeHandler )
+ {
+ TRACE(_T("CDirectoryChangeWatcher::ProcessChangeNotifications() Unable to continue, pdi->GetChangeHandler() returned NULL!\n"));
+ TRACE(_T("File: %s Line: %d\n"), _T( __FILE__ ), __LINE__ );
+ return;
+ }
+
+
+ //
+ // go through and process the notifications contained in the
+ // CFileChangeNotification object( CFileChangeNotification is a wrapper for the FILE_NOTIFY_INFORMATION structure
+ // returned by ReadDirectoryChangesW)
+ //
+ do
+ {
+ //The FileName member of the FILE_NOTIFY_INFORMATION
+ //structure contains the NAME of the file RELATIVE to the
+ //directory that is being watched...
+ //ie, if watching C:\Temp and the file C:\Temp\MyFile.txt is changed,
+ //the file name will be "MyFile.txt"
+ //If watching C:\Temp, AND you're also watching subdirectories
+ //and the file C:\Temp\OtherFolder\MyOtherFile.txt is modified,
+ //the file name will be "OtherFolder\MyOtherFile.txt
+
+ //The CDirectoryChangeHandler::On_Filexxx() functions will receive the name of the file
+ //which includes the full path to the directory being watched
+
+
+ //
+ // See what the change was
+ //
+
+ switch( notify_info.GetAction() )
+ {
+ case FILE_ACTION_ADDED: // a file was added!
+
+ pChangeHandler->On_FileAdded( notify_info.GetFileNameWithPath( pdi->m_strDirName ) ); break;
+
+ case FILE_ACTION_REMOVED: //a file was removed
+
+ pChangeHandler->On_FileRemoved( notify_info.GetFileNameWithPath( pdi->m_strDirName ) ); break;
+
+ case FILE_ACTION_MODIFIED:
+ //a file was changed
+ //pdi->m_pChangeHandler->On_FileModified( strLastFileName ); break;
+ pChangeHandler->On_FileModified( notify_info.GetFileNameWithPath( pdi->m_strDirName ) ); break;
+
+ case FILE_ACTION_RENAMED_OLD_NAME:
+ {//a file name has changed, and this is the OLD name
+ //This record is followed by another one w/
+ //the action set to FILE_ACTION_RENAMED_NEW_NAME (contains the new name of the file
+
+ CString strOldFileName = notify_info.GetFileNameWithPath( pdi->m_strDirName );
+
+
+ if( notify_info.GetNextNotifyInformation() )
+ {//there is another PFILE_NOTIFY_INFORMATION record following the one we're working on now...
+ //it will be the record for the FILE_ACTION_RENAMED_NEW_NAME record
+
+
+ ASSERT( notify_info.GetAction() == FILE_ACTION_RENAMED_NEW_NAME );//making sure that the next record after the OLD_NAME record is the NEW_NAME record
+
+ //get the new file name
+ CString strNewFileName = notify_info.GetFileNameWithPath( pdi->m_strDirName );
+
+ pChangeHandler->On_FileNameChanged( strOldFileName, strNewFileName);
+ }
+ else
+ {
+ //this OLD_NAME was the last record returned by ReadDirectoryChangesW
+ //I will have to call ReadDirectoryChangesW again so that I will get
+ //the record for FILE_ACTION_RENAMED_NEW_NAME
+
+ //Adjust an offset so that when I call ReadDirectoryChangesW again,
+ //the FILE_NOTIFY_INFORMATION will be placed after
+ //the record that we are currently working on.
+
+ /***************
+ Let's say that 200 files all had their names changed at about the same time
+ There will be 400 FILE_NOTIFY_INFORMATION records (one for OLD_NAME and one for NEW_NAME for EACH file which had it's name changed)
+ that ReadDirectoryChangesW will have to report to
+ me. There might not be enough room in the buffer
+ and the last record that we DID get was an OLD_NAME record,
+ I will need to call ReadDirectoryChangesW again so that I will get the NEW_NAME
+ record. This way I'll always have to strOldFileName and strNewFileName to pass
+ to CDirectoryChangeHandler::On_FileRenamed().
+
+ After ReadDirecotryChangesW has filled out our buffer with
+ FILE_NOTIFY_INFORMATION records,
+ our read buffer would look something like this:
+ End Of Buffer
+ |
+ \-/
+ |_________________________________________________________________________
+ | |
+ |file1 OLD name record|file1 NEW name record|...|fileX+1 OLD_name record| |(the record we want would be here, but we've ran out of room, so we adjust an offset and call ReadDirecotryChangesW again to get it)
+ |_________________________________________________________________________|
+
+ Since the record I need is still waiting to be returned to me,
+ and I need the current 'OLD_NAME' record,
+ I'm copying the current FILE_NOTIFY_INFORMATION record
+ to the beginning of the buffer used by ReadDirectoryChangesW()
+ and I adjust the offset into the read buffer so the the NEW_NAME record
+ will be placed into the buffer after the OLD_NAME record now at the beginning of the buffer.
+
+ Before we call ReadDirecotryChangesW again,
+ modify the buffer to contain the current OLD_NAME record...
+
+ |_______________________________________________________
+ | |
+ |fileX old name record(saved)|<this is now garbage>.....|
+ |_______________________________________________________|
+ /-\
+ |
+ Offset for Read
+ Re-issue the watch command to get the rest of the records...
+
+ ReadDirectoryChangesW(..., pBuffer + (an Offset),
+
+ After GetQueuedCompletionStatus() returns,
+ our buffer will look like this:
+
+ |__________________________________________________________________________________________________________
+ | |
+ |fileX old name record(saved)|fileX new name record(the record we've been waiting for)| <other records>... |
+ |__________________________________________________________________________________________________________|
+
+ Then I'll be able to know that a file name was changed
+ and I will have the OLD and the NEW name of the file to pass to CDirectoryChangeHandler::On_FileNameChanged
+
+ ****************/
+ //NOTE that this case has never happened to me in my testing
+ //so I can only hope that the code works correctly.
+ //It would be a good idea to set a breakpoint on this line of code:
+ VERIFY( notify_info.CopyCurrentRecordToBeginningOfBuffer( ref_dwReadBuffer_Offset ) );
+
+
+ }
+ break;
+ }
+ case FILE_ACTION_RENAMED_NEW_NAME:
+ {
+ //This should have been handled in FILE_ACTION_RENAMED_OLD_NAME
+ ASSERT( dwLastAction == FILE_ACTION_RENAMED_OLD_NAME );
+ ASSERT( FALSE );//this shouldn't get here
+ }
+
+ default:
+ TRACE(_T("CDirectoryChangeWatcher::ProcessChangeNotifications() -- unknown FILE_ACTION_ value! : %d\n"), notify_info.GetAction() );
+ break;//unknown action
+ }
+
+ dwLastAction = notify_info.GetAction();
+
+
+ } while( notify_info.GetNextNotifyInformation() );
+} \ No newline at end of file
diff --git a/IvyFileMon/DirectoryChanges.h b/IvyFileMon/DirectoryChanges.h
new file mode 100644
index 0000000..b2e41c3
--- /dev/null
+++ b/IvyFileMon/DirectoryChanges.h
@@ -0,0 +1,493 @@
+// DirectoryChanges.h: interface for the
+// CDirectoryChangeWatcher and CDirectoryChangeHandler classes.
+//
+// Uses an io completion port and ReadDirectoryChangesW -- this code will only work on
+// Windows NT/2000/XP.
+//
+// The directory being watched must be a directory on a Windows NT/2000/XP machine
+//
+//
+// These classes are based on the FWatch sample program in the SDK.
+//
+//
+// If you get a compile time error that ReadDirectoryChangesW is an undeclared identifier,
+// you'll need to #define _WIN32_WINNT 0x400 in stdafx.h.
+//
+//
+/*******************************************************************
+// *** COPYRIGHT NOTICE ****
+//
+// Copyright 2001. Wes Jones (wesj@hotmail.com)
+//
+// This code is free for use under the following conditions:
+//
+// You may not claim authorship of this code.
+// You may not sell or distrubute this code without author's permission.
+// You are not permitted to sell this code in it's compiled, non-compiled, executable, or any other form.
+// Executable code excepted in the case that it is not sold separately from an application which
+// uses this it. This means you can use this code for your applications as you see fit, but this code may not be sold in any form
+// to others for use in their applications.
+// This copyright notice may not be removed.
+//
+//
+// If this code was NOT obtained by downloading it from www.codeproject.com,
+// or given to you by a friend or coworker, please tell me, & let me know how you got it. There a plenty of lazy bastards that
+// collect source code from the internet, and then sell it as part of
+// a 'Programmer's Library'. Using this code for such a purpose is stricly prohibited.
+//
+// If you'd like to pay me to turn this into an ActiveX/COM object so you
+// can use it in a Visual Basic application, feel free to contact me with an offer,
+// and I will create it for you. Otherwise, here is the source code, and you may make your own
+// ActiveX/COM object, providing that it is not sold separately.
+//
+// No guarantees or warranties are expressed or implied.
+// This code may contain bugs.
+// Warning: May contain matter. If this should come into contact with anti-matter, a violent explosion may occur.
+*******************************************************************/
+
+// Please let me know of any bugs, bug fixes, or enhancements made to this code.
+// If you have ideas for this, and/or tips or admonitions, I would be glad to hear them.
+//
+// See notes at top of DirectoryChanges.cpp modification history and more info.
+//
+//
+//////////////////////////////////////////////////////////////////////
+
+#if !defined(AFX_DIRECTORYCHANGES_H__02E53FDE_CB22_4176_B6D7_DA3675D9F1A6__INCLUDED_)
+#define AFX_DIRECTORYCHANGES_H__02E53FDE_CB22_4176_B6D7_DA3675D9F1A6__INCLUDED_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+#include <afxmt.h>
+#include <afxtempl.h>
+
+#define READ_DIR_CHANGE_BUFFER_SIZE 4096
+
+class CFileNotifyInformation;//helper class
+class CDirectoryChangeWatcher;
+class CDelayedDirectoryChangeHandler;//helper class...used in implementation
+
+class CDirectoryChangeHandler
+/***********************************
+ A class to handle changes to files in a directory.
+ The virtual On_Filexxx() functions are called whenever changes are made to a watched directory that is being handled by this object...
+ The On_Filexxx() functions execute in the context of the main thread if true is passed to the constructor of CDirectoryChangeWatcher,
+ OR they fire in the context of a worker thread if false is passed to the constructor of CDirectoryChangeWatcher
+
+ NOTES:
+ A CDirectoryChangeHandler can only be used by ONE CDirectoryChangeWatcher object,
+ but a CDirectoryChangeWatcher object may use multiple CDirectoryChangeHandler objects.
+
+ When this object is destroyed, whatever directories that it was being used to handle directory changes for
+ will automatically be 'unwatched'.
+
+ The class is reference counted. The reference count is increased every time it is used
+ in a (successfull) call to CDirectoryChangeWatcher::WatchDirectory() and is decremented whenever
+ the directory becomes 'unwatched'.
+
+ The only notifications are File Added, Removed, Modified, and Renamed.
+ Even though the CDirectoryChangeWatcher::WatchDirectory (which'll call ReadDirectoryChangesW()) function allows you to specify flags
+ to watch for changes to last access time, last write time, attributes changed, etc,
+ these changes all map out to On_FileModified() which doesn't specify the type of modification.
+
+
+ NOTE: The CDirectoryChangeHandler::On_Filexxx() functions
+ are called in the context of the main thread, the thread that called CDirectoryChangeWatcher::WatchDirectory(),
+ if you pass true to the constructor of CDirectoryChangeWatcher. This is accomplished via a hidden window,
+ and REQUIRES that your app has a message pump.
+ For console applications, or applications w/out a message pump, you can pass false to the constructor
+ of CDirectoryChangeWatcher, and these notifications will fire in the context of a worker thread. By passing false
+ to the constructor of CDirectoryChangeWatcher, you do NOT NEED a message pump in your application.
+
+
+
+************************************/
+{
+public:
+
+ CDirectoryChangeHandler();
+ virtual ~CDirectoryChangeHandler();
+
+ //this class is reference counted
+ long AddRef();
+ long Release();
+ long CurRefCnt()const;
+
+
+ BOOL UnwatchDirectory();//causes CDirectoryChangeWatcher::UnwatchDirectory() to be called.
+
+ const CString & GetChangedDirectoryName() const { return m_strChangedDirectoryName;}//WARNING: don't use this, this function will be removed in a future release.
+ //returns the directory name where the change occured. This contains
+ //the last directory to have changed if the same CDirectoryChangeHandler is
+ //being used to watch multiple directories. It will return an empty string
+ //if no changes have been made to a directory yet. It will always be the
+ //name of the currently changed directory(as specified in CDirectoryChangeWatcher::WatchDirectory())
+ //if called in the context of one of the
+ //On_Filexxx() functions.
+protected:
+ //
+ // Override these functions:
+ // These functions are called when the directory to watch has had a change made to it
+ virtual void On_FileAdded(const CString & strFileName); //=0;
+ //
+ // On_FileAdded()
+ //
+ // This function is called when a file in one of the watched folders(or subfolders)
+ // has been created.
+ //
+ // For this function to execute you'll need to specify FILE_NOTIFY_CHANGE_FILE_NAME or FILE_NOTIFY_CHANGE_DIR_NAME(for directories)
+ // when you call CDirectoryChangeWatcher::WatchDirectory()
+ //
+ virtual void On_FileRemoved(const CString & strFileName);// = 0;
+ //
+ // On_FileRemoved()
+ //
+ // This function is called when a file in one of the watched folders(or subfolders)
+ // has been deleted(or moved to another directory)
+ //
+ // For this function to execute you'll need to specify FILE_NOTIFY_CHANGE_FILE_NAME or FILE_NOTIFY_CHANGE_DIR_NAME(for directories)
+ // when you call CDirectoryChangeWatcher::WatchDirecotry()
+ //
+
+ virtual void On_FileNameChanged(const CString & strOldFileName, const CString & strNewFileName);// = 0;
+ //
+ // On_FileNameChanged()
+ //
+ // This function is called when a file in one of the watched folders(or subfolders)
+ // has been renamed.
+ //
+ //
+ // You'll need to specify FILE_NOTIFY_CHANGE_FILE_NAME (or FILE_NOTIFY_CHANGE_DIR_NAME(for directories))
+ // when you call CDirectoryChangeWatcher::WatchDirectory()
+ //
+ //
+
+ virtual void On_FileModified(const CString & strFileName);// = 0;
+ //
+ // On_FileModified()
+ //
+ // This function is called whenever an attribute specified by the watch
+ // filter has changed on a file in the watched directory or
+ //
+ // Specify any of the following flags when you call CDirectoryChangeWatcher::WatchDirectory()
+ //
+ //
+ // FILE_NOTIFY_CHANGE_ATTRIBUTES
+ // FILE_NOTIFY_CHANGE_SIZE
+ // FILE_NOTIFY_CHANGE_LAST_WRITE
+ // FILE_NOTIFY_CHANGE_LAST_ACCESS
+ // FILE_NOTIFY_CHANGE_CREATION (* See Note # 1* )
+ // FILE_NOTIFY_CHANGE_SECURITY
+ //
+ //
+ // General Note) Windows tries to optimize some of these notifications. You may not get
+ // a notification every single time a file is accessed for example.
+ // There's a MS KB article or something on this(sorry forgot which one).
+ //
+ // Note #1 ) This notification isn't what you might think(FILE_NOTIFY_CHANGE_CREATION).
+ // See the help files for ReadDirectoryChangesW...
+ // This notifies you of a change to the file's
+ // creation time, not when the file is created.
+ // Use FILE_NOTIFY_CHANGE_FILE_NAME to know about newly created files.
+ //
+
+ virtual void On_ReadDirectoryChangesError(DWORD dwError, const CString & strDirectoryName);
+ //
+ // On_ReadDirectoryChangesError()
+ //
+ // This function is called when ReadDirectoryChangesW() fails during normal
+ // operation (ie: some time after you've called CDirectoryChangeWatcher::WatchDirectory())
+ //
+ //
+ // NOTE: *** READ THIS *** READ THIS *** READ THIS *** READ THIS ***
+ //
+ // NOTE: If this function has been called, the watched directory has been automatically unwatched.
+ // You will not receive any further notifications for that directory until
+ // you call CDirectoryChangeWatcher::WatchDirectory() again.
+ //
+ // On_WatchStopped() will not be called.
+
+
+ virtual void On_WatchStarted(DWORD dwError, const CString & strDirectoryName);
+ //
+ // void On_WatchStarted()
+ //
+ // This function is called when a directory watch has begun.
+ // It will be called whether CDirectoryChangeWatcher::WatchDirectory() is successful or not. Check the dwError parameter.
+ //
+ // PARAMETERS:
+ // DWORD dwError -- 0 if successful, else it's the return value of GetLastError()
+ // indicating why the watch failed.
+ // const CString & strDirectoryName -- The full path and name of the directory being watched.
+
+ virtual void On_WatchStopped(const CString & strDirectoryName);
+ //
+ // void On_WatchStopped()
+ //
+ // This function is called when a directory is unwatched (except on the case of the direcotry being unwatched due to an error)
+ //
+ // WARNING: *** READ THIS *** READ THIS *** READ THIS *** READ THIS ***
+ //
+ // This function MAY be called before the destructor of CDirectoryChangeWatcher
+ // finishes.
+ //
+ // Be careful if your implementation of this fuction
+ // interacts with some sort of a window handle or other object(a class, a file, etc.).
+ // It's possible that that object/window handle will NOT be valid anymore the very last time
+ // that On_WatchStopped() is called.
+ // This scenario is likely if the CDirectoryChangeWatcher instance is currently watching a
+ // directory, and it's destructor is called some time AFTER these objects/windows
+ // your change handler interacts with have been destroyed.
+ //
+ // If your CDirectoryChangeHandler derived class interacts w/ a window or other
+ // object, it's a good idea to unwatch any directories before the object/window is destroyed.
+ // Otherwise, place tests for valid objects/windows in the implementation of this function.
+ //
+ // Failure to follow either tip can result in a mysterious RTFM error, or a 'Run time errors'
+ //
+
+ virtual bool On_FilterNotification(DWORD dwNotifyAction, LPCTSTR szFileName, LPCTSTR szNewFileName);
+ //
+ // bool On_FilterNotification(DWORD dwNotifyAction, LPCTSTR szFileName, LPCTSTR szNewFileName);
+ //
+ // This function gives your class a chance to filter unwanted notifications.
+ //
+ // PARAMETERS:
+ // DWORD dwNotifyAction -- specifies the event to filter
+ // LPCTSTR szFileName -- specifies the name of the file for the event.
+ // LPCTSTR szNewFileName -- specifies the new file name of a file that has been renamed.
+ //
+ // ** szFileName and szNewFileName will always be the full path and file name with extention.
+ //
+ // RETURN VALUE:
+ // return true , and you will receive the notification.
+ // return false, and your class will NOT receive the notification.
+ //
+ // Valid values of dwNotifyAction:
+ // FILE_ACTION_ADDED -- On_FileAdded() is about to be called.
+ // FILE_ACTION_REMOVED -- On_FileRemoved() is about to be called.
+ // FILE_ACTION_MODIFIED -- On_FileModified() is about to be called.
+ // FILE_ACTION_RENAMED_OLD_NAME-- On_FileNameChanged() is about to be call.
+ //
+ //
+ // NOTE: When the value of dwNotifyAction is FILE_ACTION_RENAMED_OLD_NAME,
+ // szFileName will be the old name of the file, and szNewFileName will
+ // be the new name of the renamed file.
+ //
+ // The default implementation always returns true, indicating that all notifications will
+ // be sent.
+ //
+ // NOTE: This function may or may not be called depending upon the flags you specify to control
+ // filter behavior.
+ // If you are specifying filters when watching the directory, you will not get this notification
+ // if the file name does not pass the filter test, even if this function returns true.
+ //
+
+ //
+ //
+ // End Override these functions (ie: don't worry about the rest of this class)
+ //
+
+ void SetChangedDirectoryName(const CString & strChangedDirName);//please don't use this function, it will be removed in future releases.
+
+private:
+ long m_nRefCnt;
+
+ CString m_strChangedDirectoryName;//will be removed in a future release.
+
+ friend class CDirectoryChangeWatcher;
+ friend class CDelayedDirectoryChangeHandler;
+ //
+ // This class keeps a reference to the CDirectoryChangeHandler
+ // that was used when an object of this type is passed
+ // to CDirectoryChangeWatcher::WatchDirectory().
+ //
+ // This way, when the CDirectoryChangeWatcher object is destroyed(or if CDirectoryChangeHandler::UnwatchDirectory() is called)
+ // AFTER CDirectoryChangeWatcher::UnwatchDirecotry() or CDirectoryChangeWatcher::UnwatchAllDirectories() is called
+ // the directory(or direcotries) that this
+ // CDirectoryChangeWatcher object is handling will be automatically unwatched
+ // If the CDirectoryChangeWatcher object is destroyed before the CDirectoryChangeHandler objects
+ // that are being used with that watcher are destroyed, the reference counting prevents
+ // this class from referencing a destructed object.
+ // Basically, neither class needs to worry about the lifetime of the other(CDirectoryChangeWatcher && CDirectoryChangeHandler)
+ //
+
+ long ReferencesWatcher(CDirectoryChangeWatcher * pDirChangeWatcher);
+ long ReleaseReferenceToWatcher(CDirectoryChangeWatcher * pDirChangeWatcher);
+ CDirectoryChangeWatcher * m_pDirChangeWatcher;
+ long m_nWatcherRefCnt; //<-- when this reaches 0, m_pDirChangeWatcher is set to NULL
+ CCriticalSection m_csWatcher;
+};
+
+///////////////////////////////////////////////////////////
+
+class CDirectoryChangeWatcher
+/***************************************
+ A class to monitor a directory for changes made to files in it, or it's subfolders.
+ The class CDirectoryChangeHandler handles the changes. You derive a class from CDirectoryChangeHandler to handle them.
+
+
+ This class uses the Win32 API ReadDirectoryChangesW() to watch a directory for file changes.
+
+ Multiple directories can be watched simultaneously using a single instance of CDirectoryChangeWatcher.
+ Single or multiple instances of CDirectoryChangeHandler object(s) can be used to handle changes to watched directories.
+ Directories can be added and removed from the watch dynamically at run time without destroying
+ the CDirectoryChangeWatcher object (or CDirectoryChangeHandler object(s).
+
+ This class uses a worker thread, an io completion port, and ReadDirectoryChangesW() to monitor changes to a direcory (or subdirectories).
+ There will always only be a single thread no matter how many directories are being watched(per instance of CDirectoryChangeHandler)
+
+ THREAD ISSUES:
+ This class uses worker threads.
+ Notifications (calling CDirectoryChangeHandler's virtual functions) are executed
+ in the context of either the main thread, OR in a worker thread.
+
+ The 'main' thread is the thread that first calls CDirectoryChangeWatcher::WatchDirectory().
+ For notifications to execute in the main thread, it's required that the calling thread(usually the main thread)
+ has a message pump in order to process the notifications.
+
+ For applications w/out a message pump, notifications are executed in the context of a worker thread.
+
+ See the constructor for CDirectoryChangeWatcher.
+
+
+****************************************/
+{
+public:
+
+ enum { //options for determining the behavior of the filter tests.
+ //
+ FILTERS_DONT_USE_FILTERS = 1, //never test the include/exclude filters. CDirectoryChangeHandler::On_FilterNotification() is still called.
+ FILTERS_CHECK_FULL_PATH = 2, //For the file path: "C:\FolderName\SubFolder\FileName.xyz", the entire path is checked for the filter pattern.
+ FILTERS_CHECK_PARTIAL_PATH = 4, //For the file path: "C:\FolderName\SubFolder\FileName.xyz", only "SubFolder\FileName.xyz" is checked against the filter pattern, provided that you are watching the folder "C:\FolderName", and you are also watching subfolders.
+ FILTERS_CHECK_FILE_NAME_ONLY = 8, //For the file path: "C:\FolderName\SubFolder\FileName.xyz", only "FileName.xyz" is checked against the filter pattern.
+ FILTERS_TEST_HANDLER_FIRST = 16, //test CDirectoryChangeHandler::On_FilterNotification() before checking the include/exclude filters. the default is to check the include/exclude filters first.
+ FILTERS_DONT_USE_HANDLER_FILTER = 32, //CDirectoryChangeHander::On_FilterNotification() won't be called.
+ FILTERS_NO_WATCHSTART_NOTIFICATION = 64,//CDirectoryChangeHander::On_WatchStarted() won't be called.
+ FILTERS_NO_WATCHSTOP_NOTIFICATION = 128,//CDirectoryChangeHander::On_WatchStopped() won't be called.
+ FILTERS_DEFAULT_BEHAVIOR = (FILTERS_CHECK_FILE_NAME_ONLY ),
+ FILTERS_DONT_USE_ANY_FILTER_TESTS = (FILTERS_DONT_USE_FILTERS | FILTERS_DONT_USE_HANDLER_FILTER),
+ FILTERS_NO_WATCH_STARTSTOP_NOTIFICATION = (FILTERS_NO_WATCHSTART_NOTIFICATION | FILTERS_NO_WATCHSTOP_NOTIFICATION)
+ };
+
+ //ctor/dtor
+ CDirectoryChangeWatcher(bool bAppHasGUI = true, DWORD dwFilterFlags = FILTERS_DEFAULT_BEHAVIOR);//see comments in ctor .cpp file.
+ virtual ~CDirectoryChangeWatcher();
+
+ //
+ // Starts a watch on a directory:
+ //
+ DWORD WatchDirectory(const CString & strDirToWatch,
+ DWORD dwChangesToWatchFor,
+ CDirectoryChangeHandler * pChangeHandler,
+ BOOL bWatchSubDirs = FALSE,
+ LPCTSTR szIncludeFilter = NULL,
+ LPCTSTR szExcludeFilter = NULL);
+
+ BOOL IsWatchingDirectory (const CString & strDirName)const;
+ int NumWatchedDirectories()const; //counts # of directories being watched.
+
+
+ BOOL UnwatchDirectory(const CString & strDirToStopWatching);//stops watching specified directory.
+ BOOL UnwatchAllDirectories();//stops watching ALL watched directories.
+
+ DWORD SetFilterFlags(DWORD dwFilterFlags);//sets filter behavior for directories watched AFTER this function has been called.
+ DWORD GetFilterFlags()const{return m_dwFilterFlags;}
+
+protected:
+
+ virtual void On_ThreadInitialize(){}//All file change notifications has taken place in the context of a worker thread...do any thread initialization here..
+ virtual void On_ThreadExit(){}//do thread cleanup here
+
+private:
+ friend class CDirectoryChangeHandler;
+ BOOL UnwatchDirectory(CDirectoryChangeHandler * pChangeHandler);//called in CDirectoryChangeHandler::~CDirectoryChangeHandler()
+
+
+ UINT static MonitorDirectoryChanges(LPVOID lpvThis );//the worker thread that monitors directories.
+
+ class CDirWatchInfo
+ //this class is used internally by CDirectoryChangeWatcher
+ //to help manage the watched directories
+ {
+ private:
+ CDirWatchInfo(); //private & not implemented
+ CDirWatchInfo & operator=(const CDirWatchInfo & rhs);//so that they're aren't accidentally used. -- you'll get a linker error
+ public:
+ CDirWatchInfo(HANDLE hDir, const CString & strDirectoryName,
+ CDirectoryChangeHandler * pChangeHandler,
+ DWORD dwChangeFilter, BOOL bWatchSubDir,
+ bool bAppHasGUI,
+ LPCTSTR szIncludeFilter,
+ LPCTSTR szExcludeFilter,
+ DWORD dwFilterFlags);
+ private:
+ ~CDirWatchInfo( );//only I can delete myself....use DeleteSelf()
+ public:
+ void DeleteSelf(CDirectoryChangeWatcher * pWatcher);
+
+ DWORD StartMonitor(HANDLE hCompPort);
+ BOOL UnwatchDirectory( HANDLE hCompPort );
+ protected:
+ BOOL SignalShutdown( HANDLE hCompPort );
+ BOOL WaitForShutdown();
+ public:
+ BOOL LockProperties() { return m_cs.Lock(); }
+ BOOL UnlockProperties(){ return m_cs.Unlock(); }
+
+ CDelayedDirectoryChangeHandler* GetChangeHandler() const;
+ CDirectoryChangeHandler * GetRealChangeHandler() const;//the 'real' change handler is your CDirectoryChangeHandler derived class.
+ CDirectoryChangeHandler * SetRealDirectoryChangeHandler(CDirectoryChangeHandler * pChangeHandler);
+
+ BOOL CloseDirectoryHandle();
+
+ //CDirectoryChangeHandler * m_pChangeHandler;
+ CDelayedDirectoryChangeHandler * m_pChangeHandler;
+ HANDLE m_hDir;//handle to directory that we're watching
+ DWORD m_dwChangeFilter;
+ BOOL m_bWatchSubDir;
+ CString m_strDirName;//name of the directory that we're watching
+ CHAR m_Buffer[ READ_DIR_CHANGE_BUFFER_SIZE ];//buffer for ReadDirectoryChangesW
+ DWORD m_dwBufLength;//length or returned data from ReadDirectoryChangesW -- ignored?...
+ OVERLAPPED m_Overlapped;
+ DWORD m_dwReadDirError;//indicates the success of the call to ReadDirectoryChanges()
+ CCriticalSection m_cs;
+ CEvent m_StartStopEvent;
+ enum eRunningState{
+ RUNNING_STATE_NOT_SET, RUNNING_STATE_START_MONITORING, RUNNING_STATE_STOP, RUNNING_STATE_STOP_STEP2,
+ RUNNING_STATE_STOPPED, RUNNING_STATE_NORMAL
+ };
+ eRunningState m_RunningState;
+ };//end nested class CDirWatchInfo
+
+ void ProcessChangeNotifications(IN CFileNotifyInformation & notify_info,
+ IN CDirWatchInfo * pdi,
+ OUT DWORD & ref_dwReadBuffer_Offset);
+ friend class CDirWatchInfo;//so that CDirWatchInfo can call the following function.
+ long ReleaseReferenceToWatcher(CDirectoryChangeHandler * pChangeHandler);
+
+ BOOL UnwatchDirectoryBecauseOfError(CDirWatchInfo * pWatchInfo);//called in case of error.
+ int AddToWatchInfo(CDirWatchInfo * pWatchInfo);
+ //
+ // functions for retrieving the directory watch info based on different parameters
+ //
+ CDirWatchInfo * GetDirWatchInfo(IN const CString & strDirName, OUT int & ref_nIdx)const;
+ CDirWatchInfo * GetDirWatchInfo(IN CDirWatchInfo * pWatchInfo, OUT int & ref_nIdx)const;
+ CDirWatchInfo * GetDirWatchInfo(IN CDirectoryChangeHandler * pChangeHandler, OUT int & ref_nIdx)const;
+
+
+ HANDLE m_hCompPort; //i/o completion port
+ HANDLE m_hThread; //MonitorDirectoryChanges() thread handle
+ DWORD m_dwThreadID;
+ CTypedPtrArray<CPtrArray, CDirWatchInfo*> m_DirectoriesToWatch; //holds info about the directories that we're watching.
+ CCriticalSection m_csDirWatchInfo;
+
+ bool m_bAppHasGUI; //dispatch to main thread, or a worker thread?
+ DWORD m_dwFilterFlags;//options for determining the behavior of the filter tests.
+
+};
+
+
+#endif // !defined(AFX_DIRECTORYCHANGES_H__02E53FDE_CB22_4176_B6D7_DA3675D9F1A6__INCLUDED_)
diff --git a/IvyFileMon/HistoryEdit.cpp b/IvyFileMon/HistoryEdit.cpp
new file mode 100644
index 0000000..cdb1f0f
--- /dev/null
+++ b/IvyFileMon/HistoryEdit.cpp
@@ -0,0 +1,82 @@
+/*
+ * HistoryEdit.cpp
+ *
+ * Description:
+ * CHistoryEdit implementation
+ *
+ * A CEdit subclass that allows you to display a text history
+ * of events.
+ *
+ * Author:
+ * Ravi Bhavnani (ravib@datablast.net)
+ *
+ * Revision History:
+ * 15 Mar 1998 rab Original version
+ */
+
+#include "stdafx.h"
+#include "HistoryEdit.h"
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+
+/////////////////////////////////////////////////////////////////////////////
+// CHistoryEdit
+
+CHistoryEdit::CHistoryEdit()
+{
+ m_bSelectable = FALSE;
+}
+
+CHistoryEdit::~CHistoryEdit()
+{
+}
+
+BEGIN_MESSAGE_MAP(CHistoryEdit, CEdit)
+ //{{AFX_MSG_MAP(CHistoryEdit)
+ ON_WM_SETFOCUS()
+ //}}AFX_MSG_MAP
+END_MESSAGE_MAP()
+
+/////////////////////////////////////////////////////////////////////////////
+// CHistoryEdit operations
+
+void CHistoryEdit::AppendString
+ (CString str)
+//
+// Purpose:
+// Appends a text string to the history buffer.
+//
+// Returns:
+// None.
+//
+{
+CString strBuffer; // current contents of edit control
+
+ // Append string
+ GetWindowText (strBuffer);
+ if (!strBuffer.IsEmpty())
+ strBuffer += "\r\n";
+ strBuffer += str;
+ SetWindowText (strBuffer);
+
+ // Scroll the edit control
+ LineScroll (GetLineCount(), 0);
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// CHistoryEdit message handlers
+
+void CHistoryEdit::OnSetFocus(CWnd* pOldWnd)
+{
+ // Don't allow user to select text
+ if (m_bSelectable)
+ CEdit::OnSetFocus (pOldWnd);
+ else
+ pOldWnd->SetFocus();
+}
+
+// End EditHistroy.cpp \ No newline at end of file
diff --git a/IvyFileMon/HistoryEdit.h b/IvyFileMon/HistoryEdit.h
new file mode 100644
index 0000000..7e2a112
--- /dev/null
+++ b/IvyFileMon/HistoryEdit.h
@@ -0,0 +1,65 @@
+/*
+ * HistoryEdit.h
+ *
+ * Description:
+ * CHistoryEdit interface
+ *
+ * A CEdit subclass that allows you to display a scrolling history
+ * of text entries.
+ *
+ * Author:
+ * Ravi Bhavnani (ravib@datablast.net)
+ *
+ * Revision History:
+ * 15 Mar 1998 rab Original version
+ */
+
+#ifndef _HistoryEdit_h_
+#define _HistoryEdit_h_
+
+/////////////////////////////////////////////////////////////////////////////
+// CHistoryEdit window
+
+class CHistoryEdit : public CEdit
+{
+// Construction
+public:
+ CHistoryEdit();
+
+// Attributes
+public:
+
+// Operations
+public:
+ void AppendString (CString str);
+ BOOL IsSelectable() { return m_bSelectable; }
+ void AllowSelection (BOOL bAllowSelect) { m_bSelectable = bAllowSelect; }
+
+// Overrides
+ // ClassWizard generated virtual function overrides
+ //{{AFX_VIRTUAL(CHistoryEdit)
+ //}}AFX_VIRTUAL
+
+// Implementation
+public:
+ virtual ~CHistoryEdit();
+
+ // Generated message map functions
+protected:
+ //{{AFX_MSG(CHistoryEdit)
+ afx_msg void OnSetFocus(CWnd* pOldWnd);
+ //}}AFX_MSG
+ DECLARE_MESSAGE_MAP()
+
+protected:
+ BOOL m_bSelectable; // flag: user can select text in control
+};
+
+/////////////////////////////////////////////////////////////////////////////
+
+//{{AFX_INSERT_LOCATION}}
+// Microsoft Developer Studio will insert additional declarations immediately before the previous line.
+
+#endif
+
+// End HistoryEdit.h \ No newline at end of file
diff --git a/IvyFileMon/InstIvyFileMon/InstIvyFileMon.vdproj b/IvyFileMon/InstIvyFileMon/InstIvyFileMon.vdproj
new file mode 100644
index 0000000..5d95e1f
--- /dev/null
+++ b/IvyFileMon/InstIvyFileMon/InstIvyFileMon.vdproj
@@ -0,0 +1,795 @@
+"DeployProject"
+{
+"VSVersion" = "3:701"
+"ProjectType" = "8:{2C2AF0D9-9B47-4FE5-BEF2-169778172667}"
+"IsWebType" = "8:FALSE"
+"ProjectName" = "8:Install"
+"LanguageId" = "3:1036"
+"CodePage" = "3:1252"
+"UILanguageId" = "3:1036"
+"SccProjectName" = "8:\"$/Bus/IvyFileMon\", VTEAAAAA"
+"SccLocalPath" = "8:..\\.."
+"SccAuxPath" = "8:"
+"SccProvider" = "8:MSSCCI:Microsoft Visual SourceSafe"
+ "Hierarchy"
+ {
+ "Entry"
+ {
+ "MsmKey" = "8:_0DD44A8F6530438CB2E0A50F1F80A210"
+ "OwnerKey" = "8:_DB155C88AA6C4534BEF14067EEC6FB65"
+ "MsmSig" = "8:_UNDEFINED"
+ }
+ "Entry"
+ {
+ "MsmKey" = "8:_0E19E1F4B469891E119B302278BD7676"
+ "OwnerKey" = "8:_DB155C88AA6C4534BEF14067EEC6FB65"
+ "MsmSig" = "8:_UNDEFINED"
+ }
+ "Entry"
+ {
+ "MsmKey" = "8:_1787477A582B41BDA52AEC8762B60240"
+ "OwnerKey" = "8:_378194BEC0C642BBBEB8EBC7B0B33A4F"
+ "MsmSig" = "8:_UNDEFINED"
+ }
+ "Entry"
+ {
+ "MsmKey" = "8:_378194BEC0C642BBBEB8EBC7B0B33A4F"
+ "OwnerKey" = "8:_UNDEFINED"
+ "MsmSig" = "8:_UNDEFINED"
+ }
+ "Entry"
+ {
+ "MsmKey" = "8:_7E184C689278442B851207163DF58EC0"
+ "OwnerKey" = "8:_378194BEC0C642BBBEB8EBC7B0B33A4F"
+ "MsmSig" = "8:_UNDEFINED"
+ }
+ "Entry"
+ {
+ "MsmKey" = "8:_7E184C689278442B851207163DF58EC0"
+ "OwnerKey" = "8:_DB155C88AA6C4534BEF14067EEC6FB65"
+ "MsmSig" = "8:_UNDEFINED"
+ }
+ "Entry"
+ {
+ "MsmKey" = "8:_DB155C88AA6C4534BEF14067EEC6FB65"
+ "OwnerKey" = "8:_UNDEFINED"
+ "MsmSig" = "8:_UNDEFINED"
+ }
+ }
+ "Configurations"
+ {
+ "Debug"
+ {
+ "DisplayName" = "8:Debug"
+ "IsDebugOnly" = "11:TRUE"
+ "IsReleaseOnly" = "11:FALSE"
+ "OutputFilename" = "8:Debug\\InstIvyFileMon.msi"
+ "PackageFilesAs" = "3:2"
+ "PackageFileSize" = "3:-2147483648"
+ "CabType" = "3:1"
+ "Compression" = "3:2"
+ "SignOutput" = "11:FALSE"
+ "CertificateFile" = "8:"
+ "PrivateKeyFile" = "8:"
+ "TimeStampServer" = "8:"
+ "InstallerBootstrapper" = "3:1"
+ }
+ "Release"
+ {
+ "DisplayName" = "8:Release"
+ "IsDebugOnly" = "11:FALSE"
+ "IsReleaseOnly" = "11:TRUE"
+ "OutputFilename" = "8:Release\\InstIvyFileMon.msi"
+ "PackageFilesAs" = "3:2"
+ "PackageFileSize" = "3:-2147483648"
+ "CabType" = "3:1"
+ "Compression" = "3:2"
+ "SignOutput" = "11:FALSE"
+ "CertificateFile" = "8:"
+ "PrivateKeyFile" = "8:"
+ "TimeStampServer" = "8:"
+ "InstallerBootstrapper" = "3:1"
+ }
+ }
+ "Deployable"
+ {
+ "CustomAction"
+ {
+ }
+ "DefaultFeature"
+ {
+ "Name" = "8:DefaultFeature"
+ "Title" = "8:"
+ "Description" = "8:"
+ }
+ "ExternalPersistence"
+ {
+ "LaunchCondition"
+ {
+ }
+ }
+ "Feature"
+ {
+ }
+ "File"
+ {
+ "{A582A373-4685-4296-BEFE-614B80A702C3}:_0E19E1F4B469891E119B302278BD7676"
+ {
+ "SourcePath" = "8:WSOCK32.dll"
+ "TargetName" = "8:WSOCK32.dll"
+ "Tag" = "8:"
+ "Folder" = "8:_DDAA30AF8058479E808821672EB9DC2B"
+ "Condition" = "8:"
+ "Transitive" = "11:FALSE"
+ "Vital" = "11:TRUE"
+ "ReadOnly" = "11:FALSE"
+ "Hidden" = "11:FALSE"
+ "System" = "11:FALSE"
+ "Permanent" = "11:FALSE"
+ "SharedLegacy" = "11:FALSE"
+ "PackageAs" = "3:1"
+ "Register" = "3:1"
+ "Exclude" = "11:TRUE"
+ "IsDependency" = "11:TRUE"
+ "IsolateTo" = "8:"
+ }
+ }
+ "FileType"
+ {
+ }
+ "Folder"
+ {
+ "{78BAF5CE-F2E5-45BE-83BC-DB6AF387E941}:_31436C2B3945454BA152271137CCAA39"
+ {
+ "Name" = "8:#1916"
+ "AlwaysCreate" = "11:FALSE"
+ "Condition" = "8:"
+ "Transitive" = "11:FALSE"
+ "Property" = "8:DesktopFolder"
+ "Folders"
+ {
+ }
+ }
+ "{78BAF5CE-F2E5-45BE-83BC-DB6AF387E941}:_DC93F1DBB219414A9D02F4F54143F682"
+ {
+ "Name" = "8:#1919"
+ "AlwaysCreate" = "11:FALSE"
+ "Condition" = "8:"
+ "Transitive" = "11:FALSE"
+ "Property" = "8:ProgramMenuFolder"
+ "Folders"
+ {
+ }
+ }
+ "{58C0ADA3-3CEA-43BD-A3B3-2EA121BC8217}:_DDAA30AF8058479E808821672EB9DC2B"
+ {
+ "DefaultLocation" = "8:[ProgramFilesFolder][Manufacturer]\\[ProductName]"
+ "Name" = "8:#1925"
+ "AlwaysCreate" = "11:FALSE"
+ "Condition" = "8:"
+ "Transitive" = "11:FALSE"
+ "Property" = "8:TARGETDIR"
+ "Folders"
+ {
+ }
+ }
+ }
+ "LaunchCondition"
+ {
+ }
+ "Locator"
+ {
+ }
+ "MsiBootstrapper"
+ {
+ "LangId" = "3:1036"
+ }
+ "Product"
+ {
+ "Name" = "8:Microsoft Visual Studio"
+ "ProductName" = "8:IvyFileMon"
+ "ProductCode" = "8:{AF7B8270-4FA5-4FF6-A907-D256980F7F0B}"
+ "PackageCode" = "8:{90EA5730-52C0-4E00-A799-573745C6C394}"
+ "UpgradeCode" = "8:{AA7342E8-DA6F-4BC3-BCB3-0ED1B53336E5}"
+ "RestartWWWService" = "11:FALSE"
+ "RemovePreviousVersions" = "11:TRUE"
+ "DetectNewerInstalledVersion" = "11:TRUE"
+ "ProductVersion" = "8:1.0.0"
+ "Manufacturer" = "8:CENA PII"
+ "ARPHELPTELEPHONE" = "8:"
+ "ARPHELPLINK" = "8:"
+ "Title" = "8:IvyFileMon"
+ "Subject" = "8:"
+ "ARPCONTACT" = "8:CENA"
+ "Keywords" = "8:"
+ "ARPCOMMENTS" = "8:"
+ "ARPURLINFOABOUT" = "8:"
+ "ARPPRODUCTICON" = "8:"
+ "ARPIconIndex" = "3:0"
+ "SearchPath" = "8:"
+ "UseSystemSearchPath" = "11:TRUE"
+ }
+ "Registry"
+ {
+ "HKLM"
+ {
+ "Keys"
+ {
+ "{6A471EEF-D31B-40F8-BCF6-C9E8EC783F36}:_90B278BF81154475985C1A4181CF94D4"
+ {
+ "Name" = "8:Software"
+ "Condition" = "8:"
+ "AlwaysCreate" = "11:FALSE"
+ "DeleteAtUninstall" = "11:FALSE"
+ "Transitive" = "11:FALSE"
+ "Keys"
+ {
+ "{6A471EEF-D31B-40F8-BCF6-C9E8EC783F36}:_F9F082172E184264885DD15D618580FC"
+ {
+ "Name" = "8:[Manufacturer]"
+ "Condition" = "8:"
+ "AlwaysCreate" = "11:FALSE"
+ "DeleteAtUninstall" = "11:FALSE"
+ "Transitive" = "11:FALSE"
+ "Keys"
+ {
+ }
+ "Values"
+ {
+ }
+ }
+ }
+ "Values"
+ {
+ }
+ }
+ }
+ }
+ "HKCU"
+ {
+ "Keys"
+ {
+ "{6A471EEF-D31B-40F8-BCF6-C9E8EC783F36}:_55DCD8AB55384E17A422F91638D7BAD6"
+ {
+ "Name" = "8:Software"
+ "Condition" = "8:"
+ "AlwaysCreate" = "11:FALSE"
+ "DeleteAtUninstall" = "11:FALSE"
+ "Transitive" = "11:FALSE"
+ "Keys"
+ {
+ "{6A471EEF-D31B-40F8-BCF6-C9E8EC783F36}:_298A7C0021DA49E58AEA25CA12D1EB8E"
+ {
+ "Name" = "8:[Manufacturer]"
+ "Condition" = "8:"
+ "AlwaysCreate" = "11:FALSE"
+ "DeleteAtUninstall" = "11:FALSE"
+ "Transitive" = "11:FALSE"
+ "Keys"
+ {
+ }
+ "Values"
+ {
+ }
+ }
+ }
+ "Values"
+ {
+ }
+ }
+ }
+ }
+ "HKCR"
+ {
+ "Keys"
+ {
+ }
+ }
+ "HKU"
+ {
+ "Keys"
+ {
+ }
+ }
+ "HKPU"
+ {
+ "Keys"
+ {
+ }
+ }
+ }
+ "Sequences"
+ {
+ }
+ "Shortcut"
+ {
+ }
+ "UserInterface"
+ {
+ "{B654A020-6903-4E6A-A86C-75DC463DB54B}:_746273026A43484A92DF764E5FE0F8D5"
+ {
+ "UseDynamicProperties" = "11:FALSE"
+ "IsDependency" = "11:FALSE"
+ "SourcePath" = "8:<VsdDialogDir>\\VsdUserInterface.wim"
+ }
+ "{8D9DEE8B-DD8B-4F48-9072-C4364E4F4011}:_7E930155F5254413952D4A13E6984096"
+ {
+ "Name" = "8:#1901"
+ "Sequence" = "3:2"
+ "Attributes" = "3:2"
+ "Dialogs"
+ {
+ "{18ADD6EC-89FE-4ED7-AD3E-211C40278470}:_9201B3D3FED9428884C552E8907BD940"
+ {
+ "Sequence" = "3:100"
+ "DisplayName" = "8:Progression"
+ "UseDynamicProperties" = "11:TRUE"
+ "IsDependency" = "11:FALSE"
+ "SourcePath" = "8:<VsdDialogDir>\\VsdAdminProgressDlg.wid"
+ "Properties"
+ {
+ "BannerBitmap"
+ {
+ "Name" = "8:BannerBitmap"
+ "DisplayName" = "8:#1001"
+ "Description" = "8:#1101"
+ "Type" = "3:8"
+ "ContextData" = "8:Bitmap"
+ "Attributes" = "3:4"
+ "Setting" = "3:1"
+ "UsePlugInResources" = "11:TRUE"
+ }
+ "ShowProgress"
+ {
+ "Name" = "8:ShowProgress"
+ "DisplayName" = "8:#1009"
+ "Description" = "8:#1109"
+ "Type" = "3:5"
+ "ContextData" = "8:1;True=1;False=0"
+ "Attributes" = "3:0"
+ "Setting" = "3:0"
+ "Value" = "3:1"
+ "DefaultValue" = "3:1"
+ "UsePlugInResources" = "11:TRUE"
+ }
+ }
+ }
+ }
+ }
+ "{8D9DEE8B-DD8B-4F48-9072-C4364E4F4011}:_89117C1BBE544863B1B3C820791387B3"
+ {
+ "Name" = "8:#1902"
+ "Sequence" = "3:1"
+ "Attributes" = "3:3"
+ "Dialogs"
+ {
+ "{18ADD6EC-89FE-4ED7-AD3E-211C40278470}:_89B7CC371BC44ECAA1ADC833B75A98BE"
+ {
+ "Sequence" = "3:100"
+ "DisplayName" = "8:Terminé"
+ "UseDynamicProperties" = "11:TRUE"
+ "IsDependency" = "11:FALSE"
+ "SourcePath" = "8:<VsdDialogDir>\\VsdFinishedDlg.wid"
+ "Properties"
+ {
+ "BannerBitmap"
+ {
+ "Name" = "8:BannerBitmap"
+ "DisplayName" = "8:#1001"
+ "Description" = "8:#1101"
+ "Type" = "3:8"
+ "ContextData" = "8:Bitmap"
+ "Attributes" = "3:4"
+ "Setting" = "3:1"
+ "UsePlugInResources" = "11:TRUE"
+ }
+ "UpdateText"
+ {
+ "Name" = "8:UpdateText"
+ "DisplayName" = "8:#1058"
+ "Description" = "8:#1158"
+ "Type" = "3:15"
+ "ContextData" = "8:"
+ "Attributes" = "3:0"
+ "Setting" = "3:1"
+ "Value" = "8:#1258"
+ "DefaultValue" = "8:#1258"
+ "UsePlugInResources" = "11:TRUE"
+ }
+ }
+ }
+ }
+ }
+ "{8D9DEE8B-DD8B-4F48-9072-C4364E4F4011}:_A340822367004EA3B2E848CB3A4A7AC0"
+ {
+ "Name" = "8:#1900"
+ "Sequence" = "3:1"
+ "Attributes" = "3:1"
+ "Dialogs"
+ {
+ "{18ADD6EC-89FE-4ED7-AD3E-211C40278470}:_5467627873D44B388595ABEDF07BE051"
+ {
+ "Sequence" = "3:300"
+ "DisplayName" = "8:Confirmer l'installation"
+ "UseDynamicProperties" = "11:TRUE"
+ "IsDependency" = "11:FALSE"
+ "SourcePath" = "8:<VsdDialogDir>\\VsdConfirmDlg.wid"
+ "Properties"
+ {
+ "BannerBitmap"
+ {
+ "Name" = "8:BannerBitmap"
+ "DisplayName" = "8:#1001"
+ "Description" = "8:#1101"
+ "Type" = "3:8"
+ "ContextData" = "8:Bitmap"
+ "Attributes" = "3:4"
+ "Setting" = "3:1"
+ "UsePlugInResources" = "11:TRUE"
+ }
+ }
+ }
+ "{18ADD6EC-89FE-4ED7-AD3E-211C40278470}:_A19F72F966EC4837BD7FA41F66501195"
+ {
+ "Sequence" = "3:100"
+ "DisplayName" = "8:Bienvenue"
+ "UseDynamicProperties" = "11:TRUE"
+ "IsDependency" = "11:FALSE"
+ "SourcePath" = "8:<VsdDialogDir>\\VsdWelcomeDlg.wid"
+ "Properties"
+ {
+ "BannerBitmap"
+ {
+ "Name" = "8:BannerBitmap"
+ "DisplayName" = "8:#1001"
+ "Description" = "8:#1101"
+ "Type" = "3:8"
+ "ContextData" = "8:Bitmap"
+ "Attributes" = "3:4"
+ "Setting" = "3:1"
+ "UsePlugInResources" = "11:TRUE"
+ }
+ "CopyrightWarning"
+ {
+ "Name" = "8:CopyrightWarning"
+ "DisplayName" = "8:#1002"
+ "Description" = "8:#1102"
+ "Type" = "3:3"
+ "ContextData" = "8:"
+ "Attributes" = "3:0"
+ "Setting" = "3:1"
+ "Value" = "8:#1202"
+ "DefaultValue" = "8:#1202"
+ "UsePlugInResources" = "11:TRUE"
+ }
+ "Welcome"
+ {
+ "Name" = "8:Welcome"
+ "DisplayName" = "8:#1003"
+ "Description" = "8:#1103"
+ "Type" = "3:3"
+ "ContextData" = "8:"
+ "Attributes" = "3:0"
+ "Setting" = "3:1"
+ "Value" = "8:#1203"
+ "DefaultValue" = "8:#1203"
+ "UsePlugInResources" = "11:TRUE"
+ }
+ }
+ }
+ "{18ADD6EC-89FE-4ED7-AD3E-211C40278470}:_A5FFBF0129BC461FBFEE8A03128FF939"
+ {
+ "Sequence" = "3:200"
+ "DisplayName" = "8:Dossier d'installation"
+ "UseDynamicProperties" = "11:TRUE"
+ "IsDependency" = "11:FALSE"
+ "SourcePath" = "8:<VsdDialogDir>\\VsdFolderDlg.wid"
+ "Properties"
+ {
+ "BannerBitmap"
+ {
+ "Name" = "8:BannerBitmap"
+ "DisplayName" = "8:#1001"
+ "Description" = "8:#1101"
+ "Type" = "3:8"
+ "ContextData" = "8:Bitmap"
+ "Attributes" = "3:4"
+ "Setting" = "3:1"
+ "UsePlugInResources" = "11:TRUE"
+ }
+ }
+ }
+ }
+ }
+ "{8D9DEE8B-DD8B-4F48-9072-C4364E4F4011}:_AE2014636E5E4F25B59645C85CE9CD4B"
+ {
+ "Name" = "8:#1902"
+ "Sequence" = "3:2"
+ "Attributes" = "3:3"
+ "Dialogs"
+ {
+ "{18ADD6EC-89FE-4ED7-AD3E-211C40278470}:_EF4845026C4E48A18C9D2D3C97217128"
+ {
+ "Sequence" = "3:100"
+ "DisplayName" = "8:Terminé"
+ "UseDynamicProperties" = "11:TRUE"
+ "IsDependency" = "11:FALSE"
+ "SourcePath" = "8:<VsdDialogDir>\\VsdAdminFinishedDlg.wid"
+ "Properties"
+ {
+ "BannerBitmap"
+ {
+ "Name" = "8:BannerBitmap"
+ "DisplayName" = "8:#1001"
+ "Description" = "8:#1101"
+ "Type" = "3:8"
+ "ContextData" = "8:Bitmap"
+ "Attributes" = "3:4"
+ "Setting" = "3:1"
+ "UsePlugInResources" = "11:TRUE"
+ }
+ }
+ }
+ }
+ }
+ "{8D9DEE8B-DD8B-4F48-9072-C4364E4F4011}:_CF64177EA7D844FD8AA819106C1D5F47"
+ {
+ "Name" = "8:#1901"
+ "Sequence" = "3:1"
+ "Attributes" = "3:2"
+ "Dialogs"
+ {
+ "{18ADD6EC-89FE-4ED7-AD3E-211C40278470}:_A404463CA0524BA1B155B28BB93CE3D1"
+ {
+ "Sequence" = "3:100"
+ "DisplayName" = "8:Progression"
+ "UseDynamicProperties" = "11:TRUE"
+ "IsDependency" = "11:FALSE"
+ "SourcePath" = "8:<VsdDialogDir>\\VsdProgressDlg.wid"
+ "Properties"
+ {
+ "BannerBitmap"
+ {
+ "Name" = "8:BannerBitmap"
+ "DisplayName" = "8:#1001"
+ "Description" = "8:#1101"
+ "Type" = "3:8"
+ "ContextData" = "8:Bitmap"
+ "Attributes" = "3:4"
+ "Setting" = "3:1"
+ "UsePlugInResources" = "11:TRUE"
+ }
+ "ShowProgress"
+ {
+ "Name" = "8:ShowProgress"
+ "DisplayName" = "8:#1009"
+ "Description" = "8:#1109"
+ "Type" = "3:5"
+ "ContextData" = "8:1;True=1;False=0"
+ "Attributes" = "3:0"
+ "Setting" = "3:0"
+ "Value" = "3:1"
+ "DefaultValue" = "3:1"
+ "UsePlugInResources" = "11:TRUE"
+ }
+ }
+ }
+ }
+ }
+ "{B654A020-6903-4E6A-A86C-75DC463DB54B}:_DD40DA431C6A4691AD9C0CD2B694F4D3"
+ {
+ "UseDynamicProperties" = "11:FALSE"
+ "IsDependency" = "11:FALSE"
+ "SourcePath" = "8:<VsdDialogDir>\\VsdBasicDialogs.wim"
+ }
+ "{8D9DEE8B-DD8B-4F48-9072-C4364E4F4011}:_F0487C7ECB9243B89FFDC800DD19A924"
+ {
+ "Name" = "8:#1900"
+ "Sequence" = "3:2"
+ "Attributes" = "3:1"
+ "Dialogs"
+ {
+ "{18ADD6EC-89FE-4ED7-AD3E-211C40278470}:_746AC3F4F55049E082938A764D4F913E"
+ {
+ "Sequence" = "3:200"
+ "DisplayName" = "8:Dossier d'installation"
+ "UseDynamicProperties" = "11:TRUE"
+ "IsDependency" = "11:FALSE"
+ "SourcePath" = "8:<VsdDialogDir>\\VsdAdminFolderDlg.wid"
+ "Properties"
+ {
+ "BannerBitmap"
+ {
+ "Name" = "8:BannerBitmap"
+ "DisplayName" = "8:#1001"
+ "Description" = "8:#1101"
+ "Type" = "3:8"
+ "ContextData" = "8:Bitmap"
+ "Attributes" = "3:4"
+ "Setting" = "3:1"
+ "UsePlugInResources" = "11:TRUE"
+ }
+ }
+ }
+ "{18ADD6EC-89FE-4ED7-AD3E-211C40278470}:_D2355D14BD594C9EA6DCAB182C8C94CF"
+ {
+ "Sequence" = "3:100"
+ "DisplayName" = "8:Bienvenue"
+ "UseDynamicProperties" = "11:TRUE"
+ "IsDependency" = "11:FALSE"
+ "SourcePath" = "8:<VsdDialogDir>\\VsdAdminWelcomeDlg.wid"
+ "Properties"
+ {
+ "BannerBitmap"
+ {
+ "Name" = "8:BannerBitmap"
+ "DisplayName" = "8:#1001"
+ "Description" = "8:#1101"
+ "Type" = "3:8"
+ "ContextData" = "8:Bitmap"
+ "Attributes" = "3:4"
+ "Setting" = "3:1"
+ "UsePlugInResources" = "11:TRUE"
+ }
+ "CopyrightWarning"
+ {
+ "Name" = "8:CopyrightWarning"
+ "DisplayName" = "8:#1002"
+ "Description" = "8:#1102"
+ "Type" = "3:3"
+ "ContextData" = "8:"
+ "Attributes" = "3:0"
+ "Setting" = "3:1"
+ "Value" = "8:#1202"
+ "DefaultValue" = "8:#1202"
+ "UsePlugInResources" = "11:TRUE"
+ }
+ "Welcome"
+ {
+ "Name" = "8:Welcome"
+ "DisplayName" = "8:#1003"
+ "Description" = "8:#1103"
+ "Type" = "3:3"
+ "ContextData" = "8:"
+ "Attributes" = "3:0"
+ "Setting" = "3:1"
+ "Value" = "8:#1203"
+ "DefaultValue" = "8:#1203"
+ "UsePlugInResources" = "11:TRUE"
+ }
+ }
+ }
+ "{18ADD6EC-89FE-4ED7-AD3E-211C40278470}:_F29124EDF2D24CAF9003CA4D78974AA8"
+ {
+ "Sequence" = "3:300"
+ "DisplayName" = "8:Confirmer l'installation"
+ "UseDynamicProperties" = "11:TRUE"
+ "IsDependency" = "11:FALSE"
+ "SourcePath" = "8:<VsdDialogDir>\\VsdAdminConfirmDlg.wid"
+ "Properties"
+ {
+ "BannerBitmap"
+ {
+ "Name" = "8:BannerBitmap"
+ "DisplayName" = "8:#1001"
+ "Description" = "8:#1101"
+ "Type" = "3:8"
+ "ContextData" = "8:Bitmap"
+ "Attributes" = "3:4"
+ "Setting" = "3:1"
+ "UsePlugInResources" = "11:TRUE"
+ }
+ }
+ }
+ }
+ }
+ }
+ "MergeModule"
+ {
+ "{35A69C6E-5BA4-440D-803D-762B59A45393}:_0DD44A8F6530438CB2E0A50F1F80A210"
+ {
+ "UseDynamicProperties" = "11:TRUE"
+ "IsDependency" = "11:TRUE"
+ "SourcePath" = "8:vc_user_stl71_rtl_x86_---.msm"
+ "Properties"
+ {
+ }
+ "LanguageId" = "3:0"
+ "Exclude" = "11:FALSE"
+ "Folder" = "8:"
+ "Feature" = "8:"
+ "IsolateTo" = "8:"
+ }
+ "{35A69C6E-5BA4-440D-803D-762B59A45393}:_1787477A582B41BDA52AEC8762B60240"
+ {
+ "UseDynamicProperties" = "11:TRUE"
+ "IsDependency" = "11:TRUE"
+ "SourcePath" = "8:vc_user_mfc71_rtl_x86_---.msm"
+ "Properties"
+ {
+ }
+ "LanguageId" = "3:0"
+ "Exclude" = "11:FALSE"
+ "Folder" = "8:"
+ "Feature" = "8:"
+ "IsolateTo" = "8:"
+ }
+ "{35A69C6E-5BA4-440D-803D-762B59A45393}:_7E184C689278442B851207163DF58EC0"
+ {
+ "UseDynamicProperties" = "11:TRUE"
+ "IsDependency" = "11:TRUE"
+ "SourcePath" = "8:vc_user_crt71_rtl_x86_---.msm"
+ "Properties"
+ {
+ }
+ "LanguageId" = "3:0"
+ "Exclude" = "11:FALSE"
+ "Folder" = "8:"
+ "Feature" = "8:"
+ "IsolateTo" = "8:"
+ }
+ }
+ "ProjectOutput"
+ {
+ "{8062640A-2EEE-46E9-AB67-688E9A886E9F}:_378194BEC0C642BBBEB8EBC7B0B33A4F"
+ {
+ "SourcePath" = "8:..\\Release\\IvyFileMon.exe"
+ "TargetName" = "8:"
+ "Tag" = "8:"
+ "Folder" = "8:_DDAA30AF8058479E808821672EB9DC2B"
+ "Condition" = "8:"
+ "Transitive" = "11:FALSE"
+ "Vital" = "11:TRUE"
+ "ReadOnly" = "11:FALSE"
+ "Hidden" = "11:FALSE"
+ "System" = "11:FALSE"
+ "Permanent" = "11:FALSE"
+ "SharedLegacy" = "11:FALSE"
+ "PackageAs" = "3:1"
+ "Register" = "3:1"
+ "Exclude" = "11:FALSE"
+ "IsDependency" = "11:FALSE"
+ "IsolateTo" = "8:"
+ "ProjectOutputGroupRegister" = "3:1"
+ "OutputConfiguration" = "8:"
+ "OutputGroupCanonicalName" = "8:Built"
+ "OutputProjectGuid" = "8:{B50B2407-50BF-4DF3-832E-41EDA9914A4D}"
+ "ShowKeyOutput" = "11:TRUE"
+ "ExcludeFilters"
+ {
+ }
+ }
+ "{8062640A-2EEE-46E9-AB67-688E9A886E9F}:_DB155C88AA6C4534BEF14067EEC6FB65"
+ {
+ "SourcePath" = "8:..\\..\\Ivy\\Release\\Ivy.dll"
+ "TargetName" = "8:"
+ "Tag" = "8:"
+ "Folder" = "8:_DDAA30AF8058479E808821672EB9DC2B"
+ "Condition" = "8:"
+ "Transitive" = "11:FALSE"
+ "Vital" = "11:TRUE"
+ "ReadOnly" = "11:FALSE"
+ "Hidden" = "11:FALSE"
+ "System" = "11:FALSE"
+ "Permanent" = "11:FALSE"
+ "SharedLegacy" = "11:FALSE"
+ "PackageAs" = "3:1"
+ "Register" = "3:1"
+ "Exclude" = "11:FALSE"
+ "IsDependency" = "11:FALSE"
+ "IsolateTo" = "8:"
+ "ProjectOutputGroupRegister" = "3:1"
+ "OutputConfiguration" = "8:"
+ "OutputGroupCanonicalName" = "8:Built"
+ "OutputProjectGuid" = "8:{84E0039A-6721-4B18-9792-E9AE4274AC0E}"
+ "ShowKeyOutput" = "11:TRUE"
+ "ExcludeFilters"
+ {
+ }
+ }
+ }
+ "VJSharpPlugin"
+ {
+ }
+ }
+}
diff --git a/IvyFileMon/InstIvyFileMon/InstIvyFileMon.vdproj.vspscc b/IvyFileMon/InstIvyFileMon/InstIvyFileMon.vdproj.vspscc
new file mode 100644
index 0000000..49d6f78
--- /dev/null
+++ b/IvyFileMon/InstIvyFileMon/InstIvyFileMon.vdproj.vspscc
@@ -0,0 +1,10 @@
+""
+{
+"FILE_VERSION" = "9237"
+"ENLISTMENT_CHOICE" = "NEVER"
+"PROJECT_FILE_RELATIVE_PATH" = "relative:Install\\InstIvyFileMon"
+"NUMBER_OF_EXCLUDED_FILES" = "0"
+"ORIGINAL_PROJECT_FILE_PATH" = ""
+"NUMBER_OF_NESTED_PROJECTS" = "0"
+"SOURCE_CONTROL_SETTINGS_PROVIDER" = "PROJECT"
+}
diff --git a/IvyFileMon/IvyFileMon.cpp b/IvyFileMon/IvyFileMon.cpp
new file mode 100644
index 0000000..ca09d47
--- /dev/null
+++ b/IvyFileMon/IvyFileMon.cpp
@@ -0,0 +1,65 @@
+// IvyFileMon.cpp : Définit les comportements de classe pour l'application.
+//
+
+#include "stdafx.h"
+#include "IvyFileMon.h"
+#include "IvyFileMonDlg.h"
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#endif
+
+
+// CIvyFileMonApp
+
+BEGIN_MESSAGE_MAP(CIvyFileMonApp, CWinApp)
+ ON_COMMAND(ID_HELP, CWinApp::OnHelp)
+END_MESSAGE_MAP()
+
+
+// construction de CIvyFileMonApp
+
+CIvyFileMonApp::CIvyFileMonApp()
+{
+ // TODO : ajoutez ici le code de la construction.
+ // Placez toutes les initialisations dans InitInstance
+}
+
+
+// Le seul et unique objet CIvyFileMonApp
+
+CIvyFileMonApp theApp;
+
+
+// initialisation de CIvyFileMonApp
+
+BOOL CIvyFileMonApp::InitInstance()
+{
+ // InitCommonControls() est requis sur Windows XP si le manifeste de l'application
+ // spécifie l'utilisation de ComCtl32.dll version 6 ou ultérieure pour activer les
+ // styles visuels. Dans le cas contraire, la création de fenêtres échouera.
+ InitCommonControls();
+
+ CWinApp::InitInstance();
+
+ AfxEnableControlContainer();
+
+
+ CIvyFileMonDlg dlg;
+ m_pMainWnd = &dlg;
+ INT_PTR nResponse = dlg.DoModal();
+ if (nResponse == IDOK)
+ {
+ // TODO : Placez ici le code définissant le comportement lorsque la boîte de dialogue est
+ // fermée avec OK
+ }
+ else if (nResponse == IDCANCEL)
+ {
+ // TODO : Placez ici le code définissant le comportement lorsque la boîte de dialogue est
+ // fermée avec Annuler
+ }
+
+ // Lorsque la boîte de dialogue est fermée, retourner FALSE afin de quitter
+ // l'application, plutôt que de démarrer la pompe de messages de l'application.
+ return FALSE;
+}
diff --git a/IvyFileMon/IvyFileMon.h b/IvyFileMon/IvyFileMon.h
new file mode 100644
index 0000000..3c06f30
--- /dev/null
+++ b/IvyFileMon/IvyFileMon.h
@@ -0,0 +1,31 @@
+// IvyFileMon.h : fichier d'en-tête principal pour l'application PROJECT_NAME
+//
+
+#pragma once
+
+#ifndef __AFXWIN_H__
+ #error include 'stdafx.h' before including this file for PCH
+#endif
+
+#include "resource.h" // symboles principaux
+
+
+// CIvyFileMonApp :
+// Consultez IvyFileMon.cpp pour l'implémentation de cette classe
+//
+
+class CIvyFileMonApp : public CWinApp
+{
+public:
+ CIvyFileMonApp();
+
+// Substitutions
+ public:
+ virtual BOOL InitInstance();
+
+// Implémentation
+
+ DECLARE_MESSAGE_MAP()
+};
+
+extern CIvyFileMonApp theApp;
diff --git a/IvyFileMon/IvyFileMon.rc b/IvyFileMon/IvyFileMon.rc
new file mode 100644
index 0000000..117f3ca
--- /dev/null
+++ b/IvyFileMon/IvyFileMon.rc
@@ -0,0 +1,215 @@
+// Microsoft Visual C++ generated resource script.
+//
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "afxres.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// Français (France) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_FRA)
+#ifdef _WIN32
+LANGUAGE LANG_FRENCH, SUBLANG_FRENCH
+#pragma code_page(1252)
+#endif //_WIN32
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE
+BEGIN
+ "resource.h\0"
+END
+
+2 TEXTINCLUDE
+BEGIN
+ "#include ""afxres.h""\r\n"
+ "\0"
+END
+
+3 TEXTINCLUDE
+BEGIN
+ "#define _AFX_NO_SPLITTER_RESOURCES\r\n"
+ "#define _AFX_NO_OLE_RESOURCES\r\n"
+ "#define _AFX_NO_TRACKER_RESOURCES\r\n"
+ "#define _AFX_NO_PROPERTY_RESOURCES\r\n"
+ "\r\n"
+ "#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_FRA)\r\n"
+ "LANGUAGE 12, 1\r\n"
+ "#pragma code_page(1252)\r\n"
+ "#include ""res\\IvyFileMon.rc2"" // ressources non modifiées par Microsoft Visual C++ \r\n"
+ "#include ""afxres.rc"" // Composants standard\r\n"
+ "#endif\r\n"
+ "\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Icon
+//
+
+// Icon with lowest ID value placed first to ensure application icon
+// remains consistent on all systems.
+IDR_MAINFRAME ICON "res\\IvyFileMon.ico"
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Dialog
+//
+
+IDD_ABOUTBOX DIALOGEX 0, 0, 235, 55
+STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION |
+ WS_SYSMENU
+CAPTION "À propos de IvyFileMon"
+FONT 8, "MS Shell Dlg", 0, 0, 0x1
+BEGIN
+ ICON IDR_MAINFRAME,IDC_STATIC,11,17,20,20
+ LTEXT "IvyFileMon version 1.0",IDC_STATIC,40,10,119,8,
+ SS_NOPREFIX
+ LTEXT "Copyright (C) 2002",IDC_STATIC,40,25,119,8
+ DEFPUSHBUTTON "OK",IDOK,178,7,50,16,WS_GROUP
+END
+
+IDD_IVYFILEMON_DIALOG DIALOGEX 0, 0, 217, 200
+STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_VISIBLE |
+ WS_CAPTION | WS_SYSMENU
+EXSTYLE WS_EX_APPWINDOW
+CAPTION "IvyFileMon"
+FONT 8, "MS Shell Dlg", 0, 0, 0x1
+BEGIN
+ EDITTEXT IDC_BUS,45,15,126,12,ES_AUTOHSCROLL
+ EDITTEXT IDC_DIRECTORY,45,32,127,12,ES_AUTOHSCROLL
+ EDITTEXT IDC_PREFIX,45,67,127,12,ES_AUTOHSCROLL
+ LTEXT "Bus:",IDC_STATIC,25,18,15,8
+ LTEXT "Directory:",IDC_STATIC,7,34,33,8
+ LTEXT "Prefix:",IDC_STATIC,18,69,22,8
+ PUSHBUTTON "Start",IDC_START,176,14,34,14
+ EDITTEXT IDC_TEXT,7,88,203,105,ES_MULTILINE | ES_AUTOHSCROLL |
+ ES_READONLY
+ EDITTEXT IDC_EXTENT,45,50,127,12,ES_AUTOHSCROLL
+ LTEXT "extent:",IDC_STATIC,18,52,25,8
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 1,0,0,1
+ PRODUCTVERSION 1,0,0,1
+ FILEFLAGSMASK 0x3fL
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x4L
+ FILETYPE 0x1L
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040c04e4"
+ BEGIN
+ VALUE "CompanyName", "TODO: <Nom de la société>"
+ VALUE "FileDescription", "TODO: <Description du fichier>"
+ VALUE "FileVersion", "1.0.0.1"
+ VALUE "InternalName", "IvyFileMon.exe"
+ VALUE "LegalCopyright", "TODO: (c) <Nom de la société>. Tous droits réservés."
+ VALUE "OriginalFilename", "IvyFileMon.exe"
+ VALUE "ProductName", "TODO: <Nom du produit>"
+ VALUE "ProductVersion", "1.0.0.1"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x40c, 1252
+ END
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// DESIGNINFO
+//
+
+#ifdef APSTUDIO_INVOKED
+GUIDELINES DESIGNINFO
+BEGIN
+ IDD_ABOUTBOX, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 228
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 48
+ END
+
+ IDD_IVYFILEMON_DIALOG, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 210
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 193
+ END
+END
+#endif // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// RT_MANIFEST
+//
+
+IDR_MANIFEST RT_MANIFEST "res\\IvyFileMon.manifest"
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// String Table
+//
+
+STRINGTABLE
+BEGIN
+ IDS_ABOUTBOX "&À propos de IvyFileMon..."
+END
+
+#endif // Français (France) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+#define _AFX_NO_SPLITTER_RESOURCES
+#define _AFX_NO_OLE_RESOURCES
+#define _AFX_NO_TRACKER_RESOURCES
+#define _AFX_NO_PROPERTY_RESOURCES
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_FRA)
+LANGUAGE 12, 1
+#pragma code_page(1252)
+#include "res\IvyFileMon.rc2" // ressources non modifiées par Microsoft Visual C++
+#include "afxres.rc" // Composants standard
+#endif
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
+
diff --git a/IvyFileMon/IvyFileMon.sln b/IvyFileMon/IvyFileMon.sln
new file mode 100644
index 0000000..d23441b
--- /dev/null
+++ b/IvyFileMon/IvyFileMon.sln
@@ -0,0 +1,53 @@
+Microsoft Visual Studio Solution File, Format Version 7.00
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "IvyFileMon", "IvyFileMon.vcproj", "{B50B2407-50BF-4DF3-832E-41EDA9914A4D}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Ivy", "..\Ivy\Ivy.vcproj", "{84E0039A-6721-4B18-9792-E9AE4274AC0E}"
+EndProject
+Project("{54435603-DBB4-11D2-8724-00A0C9A8B90C}") = "InstIvyFileMon", "..\..\..\Install\InstIvyFileMon\InstIvyFileMon.vdproj", "{EFBC2510-93C6-4911-973D-02F63CAAF396}"
+EndProject
+Global
+ GlobalSection(SourceCodeControl) = preSolution
+ SccNumberOfProjects = 4
+ SccProjectUniqueName0 = ..\\Ivy\\Ivy.vcproj
+ SccProjectName0 = \u0022$/Bus/Ivy\u0022,\u0020QPEAAAAA
+ SccLocalPath0 = ..\\Ivy
+ CanCheckoutShared = false
+ SccProjectName1 = \u0022$/Bus/IvyFileMon\u0022,\u0020VTEAAAAA
+ SccLocalPath1 = ..\\..\\..
+ SccProvider1 = MSSCCI:Microsoft\u0020Visual\u0020SourceSafe
+ CanCheckoutShared = false
+ SccProjectFilePathRelativizedFromConnection1 = C++\\Bus\\IvyFileMon\\
+ SolutionUniqueID = {5F419E2D-5A13-4FB3-9817-74A8EB0DE519}
+ SccProjectUniqueName2 = IvyFileMon.vcproj
+ SccLocalPath2 = ..\\..\\..
+ CanCheckoutShared = false
+ SccProjectFilePathRelativizedFromConnection2 = C++\\Bus\\IvyFileMon\\
+ SccProjectUniqueName3 = ..\\..\\..\\Install\\InstIvyFileMon\\InstIvyFileMon.vdproj
+ SccLocalPath3 = ..\\..\\..
+ CanCheckoutShared = false
+ SccProjectFilePathRelativizedFromConnection3 = Install\\InstIvyFileMon\\
+ EndGlobalSection
+ GlobalSection(SolutionConfiguration) = preSolution
+ ConfigName.0 = Debug
+ ConfigName.1 = Release
+ EndGlobalSection
+ GlobalSection(ProjectDependencies) = postSolution
+ {B50B2407-50BF-4DF3-832E-41EDA9914A4D}.0 = {84E0039A-6721-4B18-9792-E9AE4274AC0E}
+ EndGlobalSection
+ GlobalSection(ProjectConfiguration) = postSolution
+ {B50B2407-50BF-4DF3-832E-41EDA9914A4D}.Debug.ActiveCfg = Debug|Win32
+ {B50B2407-50BF-4DF3-832E-41EDA9914A4D}.Debug.Build.0 = Debug|Win32
+ {B50B2407-50BF-4DF3-832E-41EDA9914A4D}.Release.ActiveCfg = Release|Win32
+ {B50B2407-50BF-4DF3-832E-41EDA9914A4D}.Release.Build.0 = Release|Win32
+ {84E0039A-6721-4B18-9792-E9AE4274AC0E}.Debug.ActiveCfg = Debug|Win32
+ {84E0039A-6721-4B18-9792-E9AE4274AC0E}.Debug.Build.0 = Debug|Win32
+ {84E0039A-6721-4B18-9792-E9AE4274AC0E}.Release.ActiveCfg = Release|Win32
+ {84E0039A-6721-4B18-9792-E9AE4274AC0E}.Release.Build.0 = Release|Win32
+ {EFBC2510-93C6-4911-973D-02F63CAAF396}.Debug.ActiveCfg = Debug
+ {EFBC2510-93C6-4911-973D-02F63CAAF396}.Release.ActiveCfg = Release
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ EndGlobalSection
+ GlobalSection(ExtensibilityAddIns) = postSolution
+ EndGlobalSection
+EndGlobal
diff --git a/IvyFileMon/IvyFileMon.vcproj b/IvyFileMon/IvyFileMon.vcproj
new file mode 100644
index 0000000..9ccc796
--- /dev/null
+++ b/IvyFileMon/IvyFileMon.vcproj
@@ -0,0 +1,207 @@
+<?xml version="1.0" encoding = "Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="7.00"
+ Name="IvyFileMon"
+ ProjectGUID="{B50B2407-50BF-4DF3-832E-41EDA9914A4D}"
+ SccProjectName="&quot;$/Bus/IvyFileMon&quot;, VTEAAAAA"
+ SccAuxPath=""
+ SccLocalPath="..\..\.."
+ SccProvider="MSSCCI:Microsoft Visual SourceSafe"
+ Keyword="MFCProj">
+ <Platforms>
+ <Platform
+ Name="Win32"/>
+ </Platforms>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="Debug"
+ IntermediateDirectory="Debug"
+ ConfigurationType="1"
+ UseOfMFC="2"
+ CharacterSet="2">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories="..\Ivy"
+ PreprocessorDefinitions="WIN32;_WINDOWS;_DEBUG"
+ MinimalRebuild="TRUE"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ TreatWChar_tAsBuiltInType="TRUE"
+ UsePrecompiledHeader="3"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="TRUE"
+ DebugInformationFormat="4"/>
+ <Tool
+ Name="VCCustomBuildTool"/>
+ <Tool
+ Name="VCLinkerTool"
+ LinkIncremental="2"
+ GenerateDebugInformation="TRUE"
+ SubSystem="2"
+ TargetMachine="1"/>
+ <Tool
+ Name="VCMIDLTool"
+ PreprocessorDefinitions="_DEBUG"
+ MkTypLibCompatible="FALSE"/>
+ <Tool
+ Name="VCPostBuildEventTool"/>
+ <Tool
+ Name="VCPreBuildEventTool"/>
+ <Tool
+ Name="VCPreLinkEventTool"/>
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="_DEBUG"
+ Culture="1033"
+ AdditionalIncludeDirectories="$(IntDir)"/>
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"/>
+ <Tool
+ Name="VCWebDeploymentTool"/>
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="Release"
+ IntermediateDirectory="Release"
+ ConfigurationType="1"
+ UseOfMFC="2"
+ CharacterSet="2">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ InlineFunctionExpansion="1"
+ OmitFramePointers="TRUE"
+ AdditionalIncludeDirectories="..\Ivy"
+ PreprocessorDefinitions="WIN32;_WINDOWS;NDEBUG"
+ StringPooling="TRUE"
+ MinimalRebuild="FALSE"
+ RuntimeLibrary="2"
+ EnableFunctionLevelLinking="TRUE"
+ TreatWChar_tAsBuiltInType="TRUE"
+ UsePrecompiledHeader="3"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="TRUE"
+ DebugInformationFormat="3"/>
+ <Tool
+ Name="VCCustomBuildTool"/>
+ <Tool
+ Name="VCLinkerTool"
+ LinkIncremental="1"
+ GenerateDebugInformation="TRUE"
+ SubSystem="2"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="1"/>
+ <Tool
+ Name="VCMIDLTool"
+ PreprocessorDefinitions="NDEBUG"
+ MkTypLibCompatible="FALSE"/>
+ <Tool
+ Name="VCPostBuildEventTool"/>
+ <Tool
+ Name="VCPreBuildEventTool"/>
+ <Tool
+ Name="VCPreLinkEventTool"/>
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="NDEBUG"
+ Culture="1033"
+ AdditionalIncludeDirectories="$(IntDir)"/>
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"/>
+ <Tool
+ Name="VCWebDeploymentTool"/>
+ </Configuration>
+ </Configurations>
+ <Files>
+ <Filter
+ Name="Source Files"
+ Filter="cpp;c;cxx;def;odl;idl;hpj;bat;asm">
+ <File
+ RelativePath="DelayedDirectoryChangeHandler.cpp">
+ </File>
+ <File
+ RelativePath="DirectoryChanges.cpp">
+ </File>
+ <File
+ RelativePath="HistoryEdit.cpp">
+ </File>
+ <File
+ RelativePath="IvyFileMon.cpp">
+ </File>
+ <File
+ RelativePath="IvyFileMonDlg.cpp">
+ </File>
+ <File
+ RelativePath="ParseCmdLine.cpp">
+ </File>
+ <File
+ RelativePath="stdafx.cpp">
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="1"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="1"/>
+ </FileConfiguration>
+ </File>
+ </Filter>
+ <Filter
+ Name="Header Files"
+ Filter="h;hpp;hxx;hm;inl;inc">
+ <File
+ RelativePath="DelayedDirectoryChangeHandler.h">
+ </File>
+ <File
+ RelativePath="DirectoryChanges.h">
+ </File>
+ <File
+ RelativePath="HistoryEdit.h">
+ </File>
+ <File
+ RelativePath="IvyFileMon.h">
+ </File>
+ <File
+ RelativePath="IvyFileMonDlg.h">
+ </File>
+ <File
+ RelativePath="ParseCmdLine.h">
+ </File>
+ <File
+ RelativePath="Resource.h">
+ </File>
+ <File
+ RelativePath="stdafx.h">
+ </File>
+ </Filter>
+ <Filter
+ Name="Resource Files"
+ Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;jpg;jpeg;jpe;manifest">
+ <File
+ RelativePath="res\IvyFileMon.ico">
+ </File>
+ <File
+ RelativePath="res\IvyFileMon.manifest">
+ </File>
+ <File
+ RelativePath="IvyFileMon.rc">
+ </File>
+ <File
+ RelativePath="res\IvyFileMon.rc2">
+ </File>
+ </Filter>
+ <File
+ RelativePath="ReadMe.txt">
+ </File>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/IvyFileMon/IvyFileMon.vcproj.vspscc b/IvyFileMon/IvyFileMon.vcproj.vspscc
new file mode 100644
index 0000000..4c02f52
--- /dev/null
+++ b/IvyFileMon/IvyFileMon.vcproj.vspscc
@@ -0,0 +1,10 @@
+""
+{
+"FILE_VERSION" = "9237"
+"ENLISTMENT_CHOICE" = "NEVER"
+"PROJECT_FILE_RELATIVE_PATH" = "relative:C++\\Bus\\IvyFileMon"
+"NUMBER_OF_EXCLUDED_FILES" = "0"
+"ORIGINAL_PROJECT_FILE_PATH" = ""
+"NUMBER_OF_NESTED_PROJECTS" = "0"
+"SOURCE_CONTROL_SETTINGS_PROVIDER" = "PROJECT"
+}
diff --git a/IvyFileMon/IvyFileMon.vsscc b/IvyFileMon/IvyFileMon.vsscc
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/IvyFileMon/IvyFileMon.vsscc
diff --git a/IvyFileMon/IvyFileMon.vssscc b/IvyFileMon/IvyFileMon.vssscc
new file mode 100644
index 0000000..4c02f52
--- /dev/null
+++ b/IvyFileMon/IvyFileMon.vssscc
@@ -0,0 +1,10 @@
+""
+{
+"FILE_VERSION" = "9237"
+"ENLISTMENT_CHOICE" = "NEVER"
+"PROJECT_FILE_RELATIVE_PATH" = "relative:C++\\Bus\\IvyFileMon"
+"NUMBER_OF_EXCLUDED_FILES" = "0"
+"ORIGINAL_PROJECT_FILE_PATH" = ""
+"NUMBER_OF_NESTED_PROJECTS" = "0"
+"SOURCE_CONTROL_SETTINGS_PROVIDER" = "PROJECT"
+}
diff --git a/IvyFileMon/IvyFileMonDlg.cpp b/IvyFileMon/IvyFileMonDlg.cpp
new file mode 100644
index 0000000..719ea66
--- /dev/null
+++ b/IvyFileMon/IvyFileMonDlg.cpp
@@ -0,0 +1,281 @@
+// IvyFileMonDlg.cpp : fichier d'implémentation
+//
+
+#include "stdafx.h"
+#include "IvyFileMon.h"
+#include "IvyFileMonDlg.h"
+#include "ParseCmdLine.h"
+#include "IvyApplication.h"
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#endif
+
+const char * CONVSTR( const CString& str )
+{
+#ifdef UNDER_CE
+
+ static char buffer[4096];
+ int len = str.GetLength();
+ buffer[len] = '\0';
+ if ( len )
+ {
+ int err = WideCharToMultiByte( CP_ACP, 0, str, len, buffer, 4096, NULL, NULL );
+ if ( err == 0 )
+ TRACE(TEXT("Error converting chars %d\n"),GetLastError());
+ }
+ return buffer;
+#else
+ return (LPCSTR) str;
+#endif
+
+}
+
+
+
+// boîte de dialogue CAboutDlg utilisée pour la boîte de dialogue 'À propos de' pour votre application
+
+class CAboutDlg : public CDialog
+{
+public:
+ CAboutDlg();
+
+// Données de la boîte de dialogue
+ enum { IDD = IDD_ABOUTBOX };
+
+ protected:
+ virtual void DoDataExchange(CDataExchange* pDX); // prise en charge de DDX/DDV
+
+// Implémentation
+protected:
+ DECLARE_MESSAGE_MAP()
+};
+
+CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
+{
+}
+
+void CAboutDlg::DoDataExchange(CDataExchange* pDX)
+{
+ CDialog::DoDataExchange(pDX);
+}
+
+BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
+END_MESSAGE_MAP()
+
+
+// boîte de dialogue CIvyFileMonDlg
+
+
+
+CIvyFileMonDlg::CIvyFileMonDlg(CWnd* pParent /*=NULL*/)
+ : CDialog(CIvyFileMonDlg::IDD, pParent)
+ , m_busnumber(_T(""))
+ , m_directory(_T(""))
+ , m_prefix(_T(""))
+ , m_extent(_T(""))
+{
+ m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
+}
+
+void CIvyFileMonDlg::DoDataExchange(CDataExchange* pDX)
+{
+ CDialog::DoDataExchange(pDX);
+ DDX_Text(pDX, IDC_BUS, m_busnumber);
+ DDX_Text(pDX, IDC_DIRECTORY, m_directory);
+ DDX_Text(pDX, IDC_PREFIX, m_prefix);
+ DDX_Control(pDX, IDC_TEXT, m_text);
+ DDX_Text(pDX, IDC_EXTENT, m_extent);
+}
+
+BEGIN_MESSAGE_MAP(CIvyFileMonDlg, CDialog)
+ ON_WM_SYSCOMMAND()
+ ON_WM_PAINT()
+ ON_WM_QUERYDRAGICON()
+ //}}AFX_MSG_MAP
+ ON_BN_CLICKED(IDC_START, OnBnClickedStart)
+END_MESSAGE_MAP()
+
+
+// gestionnaires de messages pour CIvyFileMonDlg
+
+void CIvyFileMonDlg::WriteMessage(const char *format, ...)
+{
+ char str[4096];
+ // format and write the data we were given
+ va_list args;
+ va_start(args, format);
+ vsprintf(str, format, args);
+
+ m_text.AppendString( str );
+}
+
+void CIvyFileMonDlg::OnDirectMessage(IvyApplication *app, int id, const char *arg)
+{
+TRACE(TEXT("Direct Msg Receive %d, %s\n"),id,arg );
+}
+
+void CIvyFileMonDlg::OnApplicationConnected(IvyApplication *app)
+{
+ WriteMessage( "Application: %s ready",(LPCSTR)(app->GetName()) );
+}
+void CIvyFileMonDlg::OnApplicationDisconnected(IvyApplication *app)
+{
+ WriteMessage( "Application: %s bye",(LPCSTR)(app->GetName()) );
+}
+
+
+BOOL CIvyFileMonDlg::OnInitDialog()
+{
+ CDialog::OnInitDialog();
+
+ // Ajouter l'élément de menu "À propos de..." au menu Système.
+
+ // IDM_ABOUTBOX doit se trouver dans la plage des commandes système.
+ ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
+ ASSERT(IDM_ABOUTBOX < 0xF000);
+
+ CMenu* pSysMenu = GetSystemMenu(FALSE);
+ if (pSysMenu != NULL)
+ {
+ CString strAboutMenu;
+ strAboutMenu.LoadString(IDS_ABOUTBOX);
+ if (!strAboutMenu.IsEmpty())
+ {
+ pSysMenu->AppendMenu(MF_SEPARATOR);
+ pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
+ }
+ }
+
+ // Définir l'icône de cette boîte de dialogue. L'infrastructure effectue cela automatiquement
+ // lorsque la fenêtre principale de l'application n'est pas une boîte de dialogue
+ SetIcon(m_hIcon, TRUE); // Définir une grande icône
+ SetIcon(m_hIcon, FALSE); // Définir une petite icône
+
+ // parse command Line Info
+ ParseCmdLine cmd;
+ AfxGetApp()->ParseCommandLine( cmd );
+
+ // Set Argument from Command Line
+ m_busnumber = cmd.m_busNumber;
+ m_directory = cmd.m_directory;
+ m_extent = cmd.m_extent;
+ m_prefix = cmd.m_prefix;
+
+ bus = new Ivy( "IvyFileMon","IvyFileMon Ready",this,FALSE);
+
+// bus->BindMsg("(.*)", BUS_CALLBACK_OF(CTestDlg, IvyCallback ));
+
+ //bus->BindMsg("^S( A=([0-9]+))?( B=([0-9]+))?",cb);
+ m_busnumber = bus->GetDomain( CONVSTR(m_busnumber) );
+ UpdateData(FALSE);
+
+ // force bus start in case of start
+ if ( cmd.m_start )
+ OnBnClickedStart();
+ return TRUE; // retourner TRUE, sauf si vous avez défini le focus sur un contrôle
+}
+void CIvyFileMonDlg::OnCancel()
+{
+ // TODO: Add extra cleanup here
+
+ if ( bus )
+ {
+ bus->stop();
+ delete bus;
+ }
+
+ CDialog::OnCancel();
+}
+void CIvyFileMonDlg::OnSysCommand(UINT nID, LPARAM lParam)
+{
+ if ((nID & 0xFFF0) == IDM_ABOUTBOX)
+ {
+ CAboutDlg dlgAbout;
+ dlgAbout.DoModal();
+ }
+ else
+ {
+ CDialog::OnSysCommand(nID, lParam);
+ }
+}
+
+// Si vous ajoutez un bouton Réduire à votre boîte de dialogue, vous devez utiliser le code ci-dessous
+// pour dessiner l'icône. Pour les applications MFC utilisant le modèle Document/Vue,
+// cela est fait automatiquement par l'infrastructure.
+
+void CIvyFileMonDlg::OnPaint()
+{
+ if (IsIconic())
+ {
+ CPaintDC dc(this); // contexte de périphérique pour la peinture
+
+ SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);
+
+ // Centrer l'icône dans le rectangle client
+ int cxIcon = GetSystemMetrics(SM_CXICON);
+ int cyIcon = GetSystemMetrics(SM_CYICON);
+ CRect rect;
+ GetClientRect(&rect);
+ int x = (rect.Width() - cxIcon + 1) / 2;
+ int y = (rect.Height() - cyIcon + 1) / 2;
+
+ // Dessiner l'icône
+ dc.DrawIcon(x, y, m_hIcon);
+ }
+ else
+ {
+ CDialog::OnPaint();
+ }
+}
+
+// Le système appelle cette fonction pour obtenir le curseur à afficher lorsque l'utilisateur fait glisser
+// la fenêtre réduite.
+HCURSOR CIvyFileMonDlg::OnQueryDragIcon()
+{
+ return static_cast<HCURSOR>(m_hIcon);
+}
+
+void CIvyFileMonDlg::OnBnClickedStart()
+{
+ UpdateData(TRUE);
+ bus->stop();
+ m_busnumber = bus->GetDomain( CONVSTR(m_busnumber) );
+ bus->start(CONVSTR(m_busnumber));
+ watcher.WatchDirectory(m_directory,
+ FILE_NOTIFY_CHANGE_FILE_NAME |FILE_NOTIFY_CHANGE_LAST_WRITE,
+ this,
+ FALSE, //<-- watch sub directories?
+ m_extent, //<-- Include Filter
+ NULL);//<-- Exclude Filter
+ UpdateData(FALSE);
+}
+
+void CIvyFileMonDlg::SendIvyFile(const CString & strFileName)
+{
+ CStdioFile file;
+ CString line;
+
+ TRACE("File Change %s\n", (LPCSTR)strFileName );
+ if ( file.Open( strFileName, CFile::modeRead ) )
+ {
+ WriteMessage( "File %s Change sending file...",(LPCSTR)strFileName );
+ bus->SendMsg("%s FileBegin %s",(LPCSTR)m_prefix, (LPCSTR)strFileName);
+ while( file.ReadString(line) )
+ {
+ bus->SendMsg("%s FileLine %s",(LPCSTR) m_prefix, (LPCSTR)line);
+ }
+ file.Close();
+ bus->SendMsg("%s FileEnd",(LPCSTR) m_prefix);
+ WriteMessage( "File %s Change done",(LPCSTR)strFileName );
+ }
+
+}
+void CIvyFileMonDlg::On_FileAdded(const CString & strFileName)
+{
+ SendIvyFile( strFileName );
+}
+void CIvyFileMonDlg::On_FileModified(const CString & strFileName)
+{
+ SendIvyFile( strFileName );
+} \ No newline at end of file
diff --git a/IvyFileMon/IvyFileMonDlg.h b/IvyFileMon/IvyFileMonDlg.h
new file mode 100644
index 0000000..ffec31a
--- /dev/null
+++ b/IvyFileMon/IvyFileMonDlg.h
@@ -0,0 +1,55 @@
+// IvyFileMonDlg.h : fichier d'en-tête
+//
+
+#pragma once
+
+#include "Ivy.h"
+#include "afxwin.h"
+
+#include "DirectoryChanges.h"
+#include "HistoryEdit.h"
+
+// boîte de dialogue CIvyFileMonDlg
+class CIvyFileMonDlg : public CDialog, public IvyApplicationCallback, public CDirectoryChangeHandler
+{
+// Construction
+public:
+ CIvyFileMonDlg(CWnd* pParent = NULL); // constructeur standard
+
+// Données de la boîte de dialogue
+ enum { IDD = IDD_IVYFILEMON_DIALOG };
+
+ protected:
+ virtual void DoDataExchange(CDataExchange* pDX); // prise en charge de DDX/DDV
+ virtual void OnCancel();
+
+// Implémentation
+protected:
+ HICON m_hIcon;
+
+ Ivy *bus;
+ void WriteMessage(const char * format, ...);
+ void OnApplicationConnected( IvyApplication *app );
+ void OnApplicationDisconnected( IvyApplication *app );
+ void OnDirectMessage( IvyApplication *app, int id, const char *arg );
+ void SendIvyFile( const CString & strFileName );
+
+ // Directory Handler
+ CDirectoryChangeWatcher watcher;
+ virtual void On_FileAdded(const CString & strFileName);
+ virtual void On_FileModified(const CString & strFileName);
+
+ // Fonctions générées de la table des messages
+ virtual BOOL OnInitDialog();
+ afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
+ afx_msg void OnPaint();
+ afx_msg HCURSOR OnQueryDragIcon();
+ DECLARE_MESSAGE_MAP()
+public:
+ CString m_busnumber;
+ CString m_directory;
+ CString m_prefix;
+ afx_msg void OnBnClickedStart();
+ CHistoryEdit m_text;
+ CString m_extent;
+};
diff --git a/IvyFileMon/ParseCmdLine.cpp b/IvyFileMon/ParseCmdLine.cpp
new file mode 100644
index 0000000..fc6a1d8
--- /dev/null
+++ b/IvyFileMon/ParseCmdLine.cpp
@@ -0,0 +1,76 @@
+// ParseCmdLine.cpp: implementation of the ParseCmdLine class.
+//
+//////////////////////////////////////////////////////////////////////
+
+#include "stdafx.h"
+
+#include "ParseCmdLine.h"
+
+#ifdef _DEBUG
+#undef THIS_FILE
+static char THIS_FILE[]=__FILE__;
+#define new DEBUG_NEW
+#endif
+
+//////////////////////////////////////////////////////////////////////
+// Construction/Destruction
+//////////////////////////////////////////////////////////////////////
+
+ParseCmdLine::ParseCmdLine()
+{
+ m_start = TRUE;
+ m_busNumber = "";
+ m_prefix = "IvyFileMon ";
+ m_directory = "c:\\anoto_log\\strokes";
+ m_extent = "*.txt";
+}
+
+ParseCmdLine::~ParseCmdLine()
+{
+
+}
+
+void ParseCmdLine::ParseParam(LPCTSTR lpszParam, BOOL bFlag, BOOL bLast)
+{
+ if (bFlag)
+ {
+ ParseParamFlag(lpszParam);
+ }
+ else
+ ParseParamNotFlag(lpszParam);
+
+}
+
+void ParseCmdLine::ParseParamFlag(LPCTSTR pszParam)
+{
+
+ if (lstrcmpi(pszParam, TEXT("start")) == 0)
+ m_start = TRUE;
+ else if (lstrcmpi(pszParam, TEXT("bus")) == 0)
+ m_shellCommand = BusNumber;
+ else if (lstrcmpi(pszParam, TEXT("directory")) == 0)
+ m_shellCommand = Directory;
+ else if (lstrcmpi(pszParam, TEXT("prefix")) == 0)
+ m_shellCommand = Prefix;
+ else if (lstrcmpi(pszParam, TEXT("extent")) == 0)
+ m_shellCommand = Extent;
+}
+
+void ParseCmdLine::ParseParamNotFlag(LPCTSTR pszParam)
+{
+ switch ( m_shellCommand )
+ {
+ case BusNumber:
+ m_busNumber = pszParam;
+ break;
+ case Directory:
+ m_directory = pszParam;
+ break;
+ case Prefix:
+ m_prefix = pszParam;
+ break;
+ case Extent:
+ m_extent = pszParam;
+ break;
+ }
+}
diff --git a/IvyFileMon/ParseCmdLine.h b/IvyFileMon/ParseCmdLine.h
new file mode 100644
index 0000000..3b9a272
--- /dev/null
+++ b/IvyFileMon/ParseCmdLine.h
@@ -0,0 +1,37 @@
+// TestParseCmdLine.h: interface for the TestParseCmdLine class.
+//
+//////////////////////////////////////////////////////////////////////
+
+#if !defined(AFX_FXParseCMDLINE_H__20232B92_AB99_11D2_898F_00A0245B298A__INCLUDED_)
+#define AFX_FXParseCMDLINE_H__20232B92_AB99_11D2_898F_00A0245B298A__INCLUDED_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+class ParseCmdLine : public CCommandLineInfo
+{
+public:
+
+ BOOL m_start;
+ CString m_busNumber;
+ CString m_directory;
+ CString m_extent;
+ CString m_prefix;
+ virtual void ParseParam( LPCTSTR lpszParam, BOOL bFlag, BOOL bLast );
+ ParseCmdLine();
+ virtual ~ParseCmdLine();
+ enum {
+ BusNumber,
+ Start,
+ Directory,
+ Extent,
+ Prefix
+ }m_shellCommand;
+
+protected:
+ void ParseParamNotFlag(LPCTSTR pszParam);
+ void ParseParamFlag(LPCTSTR pszParam);
+};
+
+#endif // !defined(AFX_FXParseCMDLINE_H__20232B92_AB99_11D2_898F_00A0245B298A__INCLUDED_)
diff --git a/IvyFileMon/ReadMe.txt b/IvyFileMon/ReadMe.txt
new file mode 100644
index 0000000..8ef8ee3
--- /dev/null
+++ b/IvyFileMon/ReadMe.txt
@@ -0,0 +1,87 @@
+=================================================================================================
+ BIBLIOTHÈQUE MICROSOFT FOUNDATION CLASS : Vue d'ensemble du projet IvyFileMon
+=================================================================================================
+
+AppWizard a créé cette application IvyFileMon à votre attention. Cette application
+explique les principes fondamentaux de l'utilisation des classes MFC (Microsoft Foundation Class)
+et constitue également un point de départ pour écrire votre propre application.
+
+Ce fichier fait la synthèse des différents éléments contenus dans chacun des fichiers qui constituent
+votre application IvyFileMon.
+
+IvyFileMon.vcproj
+ Il s'agit du fichier projet principal pour les projets VC++ générés à l'aide d'un Assistant Application.
+ Ce fichier contient des informations sur la version de Visual C++ qui a été utilisée pour générer
+ le fichier ainsi que des informations relatives aux plates-formes, configurations et fonctionnalités
+ sélectionnées dans l'Assistant Application.
+
+IvyFileMon.h
+ Il s'agit du fichier d'en-tête principal pour l'application. Il comporte
+ d'autres en-têtes de projet spécifiques (notamment Resource.h) et déclare la
+ classe d'application CIvyFileMonApp.
+
+IvyFileMon.cpp
+ Il s'agit du fichier source principal pour l'application. Il contient la classe
+ d'application CIvyFileMonApp.
+
+IvyFileMon.rc
+ Ce fichier dresse la liste de toutes les ressources Microsoft Windows utilisées
+ par le programme, telles que les icônes, bitmaps et curseurs qui sont stockés
+ dans le sous-répertoire RES. Vous pouvez modifier ce fichier directement dans Microsoft
+ Visual C++. Vos ressources projet se trouvent dans 1036.
+
+res\IvyFileMon.ico
+ Il s'agit du fichier icône utilisé en tant qu'icône de l'application. Cette icône
+ est incluse dans le fichier de ressources principal IvyFileMon.rc.
+
+res\IvyFileMon.rc2
+ Ce fichier contient les ressources qui ne sont pas modifiées par Microsoft
+ Visual C++. Vous devez y placer toutes les ressources non modifiables par
+ l'Éditeur de ressources.
+
+/////////////////////////////////////////////////////////////////////////////
+
+AppWizard crée une classe de dialogue :
+IvyFileMonDlg.h, IvyFileMonDlg.cpp - la boîte de dialogue
+ Ces fichiers contiennent votre classe CIvyFileMonDlg, qui définit le
+ comportement de la boîte de dialogue principale de votre application. Le modèle de boîte de
+ dialogue se trouve dans le fichier IvyFileMon.rc, modifiable dans Microsoft Visual C++.
+/////////////////////////////////////////////////////////////////////////////
+
+Autres fonctionnalités :
+
+Contrôles ActiveX
+ L'application offre la prise en charge des contrôles ActiveX.
+
+Prise en charge de l'aperçu avant impression et de l'impression
+ AppWizard a généré le code requis pour gérer les commandes Impression, Configuration de l'impression et
+ Aperçu avant impression en appelant des fonctions membres de la classe CView à partir de la bibliothèque MFC.
+/////////////////////////////////////////////////////////////////////////////
+
+Autres fichiers standard :
+
+StdAfx.h, StdAfx.cpp
+ Ces fichiers servent à générer un fichier d'en-tête précompilé (PCH),
+ appelé IvyFileMon.pch, et un fichier de types précompilés StdAfx.obj.
+
+Resource.h
+ Il s'agit du fichier d'en-tête standard, qui définit les ID des nouvelles ressources.
+ Ce fichier est lu et mis à jour par Microsoft Visual C++.
+
+/////////////////////////////////////////////////////////////////////////////
+
+Autres remarques :
+
+AppWizard utilise "TODO:" pour vous signaler les parties du code source
+que vous devez ajouter ou personnaliser.
+
+Si votre application utilise MFC dans une DLL partagée et que la langue de cette
+application n'est pas la même que celle utilisée par le système d'exploitation, vous
+devez copier les ressources localisées MFC70XXX.DLL correspondantes (situées sur le
+CD-ROM Microsoft Visual C++ dans le répertoire Win\System) dans le répertoire system
+ou system32 de votre ordinateur, puis renommer MFC70XXX.DLL en MFCLOC.DLL. ("XXX" est
+l'abréviation de la langue. Par exemple, MFC70DEU.DLL contient les ressources en allemand.)
+Si vous ne copiez pas la version localisée des ressources, certains éléments d'interface de
+votre application seront affichés dans la langue du système d'exploitation.
+
+/////////////////////////////////////////////////////////////////////////////
diff --git a/IvyFileMon/res/IvyFileMon.ico b/IvyFileMon/res/IvyFileMon.ico
new file mode 100644
index 0000000..8a84ca3
--- /dev/null
+++ b/IvyFileMon/res/IvyFileMon.ico
Binary files differ
diff --git a/IvyFileMon/res/IvyFileMon.manifest b/IvyFileMon/res/IvyFileMon.manifest
new file mode 100644
index 0000000..b68a5ce
--- /dev/null
+++ b/IvyFileMon/res/IvyFileMon.manifest
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
+<assemblyIdentity
+ version="1.0.0.0"
+ processorArchitecture="X86"
+ name="Microsoft.Windows.IvyFileMon"
+ type="win32"
+/>
+<description>Tapez ici une description de votre application</description>
+<dependency>
+ <dependentAssembly>
+ <assemblyIdentity
+ type="win32"
+ name="Microsoft.Windows.Common-Controls"
+ version="6.0.0.0"
+ processorArchitecture="X86"
+ publicKeyToken="6595b64144ccf1df"
+ language="*"
+ />
+ </dependentAssembly>
+</dependency>
+</assembly>
diff --git a/IvyFileMon/res/IvyFileMon.rc2 b/IvyFileMon/res/IvyFileMon.rc2
new file mode 100644
index 0000000..1aea3fd
--- /dev/null
+++ b/IvyFileMon/res/IvyFileMon.rc2
@@ -0,0 +1,13 @@
+//
+// Ressources IvyFileMon.RC2 non modifiées directement par Microsoft Visual C++
+//
+
+#ifdef APSTUDIO_INVOKED
+#error this file is not editable by Microsoft Visual C++
+#endif //APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+// Ajoutez ici les ressources modifiées manuellement...
+
+/////////////////////////////////////////////////////////////////////////////
diff --git a/IvyFileMon/resource.h b/IvyFileMon/resource.h
new file mode 100644
index 0000000..5d1642d
--- /dev/null
+++ b/IvyFileMon/resource.h
@@ -0,0 +1,28 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by IvyFileMon.rc
+//
+#define IDR_MANIFEST 1
+#define IDM_ABOUTBOX 0x0010
+#define IDD_ABOUTBOX 100
+#define IDS_ABOUTBOX 101
+#define IDD_IVYFILEMON_DIALOG 102
+#define IDR_MAINFRAME 128
+#define IDC_BUS 1000
+#define IDC_DIRECTORY 1001
+#define IDC_PREFIX 1002
+#define IDC_START 1003
+#define IDC_TEXT 1004
+#define IDC_PREFIX2 1005
+#define IDC_EXTENT 1005
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 129
+#define _APS_NEXT_COMMAND_VALUE 32771
+#define _APS_NEXT_CONTROL_VALUE 1005
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
diff --git a/IvyFileMon/stdafx.cpp b/IvyFileMon/stdafx.cpp
new file mode 100644
index 0000000..9c25d46
--- /dev/null
+++ b/IvyFileMon/stdafx.cpp
@@ -0,0 +1,7 @@
+// stdafx.cpp : le fichier source qui inclut uniquement le fichier Include
+// IvyFileMon.pch sera l'en-tête précompilé
+// stdafx.obj contiendra les informations de type précompilé
+
+#include "stdafx.h"
+
+
diff --git a/IvyFileMon/stdafx.h b/IvyFileMon/stdafx.h
new file mode 100644
index 0000000..768f5e5
--- /dev/null
+++ b/IvyFileMon/stdafx.h
@@ -0,0 +1,42 @@
+// stdafx.h : fichier Include pour les fichiers Include système standard,
+// ou pour les fichiers Include spécifiques au projet qui sont fréquemment utilisés,
+// mais rarement modifiés
+
+#pragma once
+
+#ifndef VC_EXTRALEAN
+#define VC_EXTRALEAN // Exclut les informations rarement utilisées des en-têtes Windows
+#endif
+
+// Modifiez les valeurs suivantes si votre plate-forme cible est antérieure aux plates-formes spécifiées ci-après.
+// Consultez la documentation MSDN pour obtenir des informations récentes sur les valeurs respectives des différentes plates-formes.
+#ifndef WINVER // Permet l'utilisation de fonctionnalités spécifiques à Windows 95 et à Windows NT 4 ou version ultérieure.
+#define WINVER 0x0400 // La valeur appropriée doit être utilisée pour des applications cibles Windows 98 et Windows 2000 ou version ultérieure.
+#endif
+
+#ifndef _WIN32_WINNT // Permet l'utilisation de fonctionnalités spécifiques à Windows NT 4 ou version ultérieure.
+#define _WIN32_WINNT 0x0400 // La valeur appropriée doit être utilisée pour des applications cibles Windows 98 et Windows 2000 ou version ultérieure.
+#endif
+
+#ifndef _WIN32_WINDOWS // Permet l'utilisation de fonctionnalités spécifiques à Windows 98 ou version ultérieure.
+#define _WIN32_WINDOWS 0x0410 // La valeur appropriée doit être utilisée pour des applications cibles Windows Me ou version ultérieure.
+#endif
+
+#ifndef _WIN32_IE // Permet l'utilisation de fonctionnalités spécifiques à Internet Explorer 4.0 ou version ultérieure.
+#define _WIN32_IE 0x0400 // La valeur appropriée doit être utilisée pour des applications cibles Internet Explorer 5.0 ou version ultérieure.
+#endif
+
+#define _ATL_CSTRING_EXPLICIT_CONSTRUCTORS // certains constructeurs CString seront explicites
+
+// désactive le masquage MFC de certains messages d'avertissement courants et par ailleurs souvent ignorés
+#define _AFX_ALL_WARNINGS
+
+#include <afxwin.h> // composants MFC principaux et standard
+#include <afxext.h> // extensions MFC
+#include <afxdisp.h> // classes Automation MFC
+
+#include <afxdtctl.h> // Prise en charge MFC des contrôles communs Internet Explorer 4
+#ifndef _AFX_NO_AFXCMN_SUPPORT
+#include <afxcmn.h> // Prise en charge MFC des contrôles communs Windows
+#endif // _AFX_NO_AFXCMN_SUPPORT
+#include <winsock.h>