From 5bb56f9993fa30197c6a93f83fa6cbea81e870c9 Mon Sep 17 00:00:00 2001 From: fcolin Date: Thu, 1 Feb 2007 13:07:28 +0000 Subject: Utilisateur : Fcolin Date : 13/12/06 Heure : 12:00 Créé Commentaire: (vss 1) --- IvyFileMon/C++/Bus/IvyFileMon/IvyFileMon.vsscc | Bin 0 -> 46 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 IvyFileMon/C++/Bus/IvyFileMon/IvyFileMon.vsscc (limited to 'IvyFileMon') diff --git a/IvyFileMon/C++/Bus/IvyFileMon/IvyFileMon.vsscc b/IvyFileMon/C++/Bus/IvyFileMon/IvyFileMon.vsscc new file mode 100644 index 0000000..8fdac15 Binary files /dev/null and b/IvyFileMon/C++/Bus/IvyFileMon/IvyFileMon.vsscc differ -- cgit v1.1 From eb34526ff7c034a120096db157278184ff946913 Mon Sep 17 00:00:00 2001 From: fcolin Date: Thu, 1 Feb 2007 13:07:29 +0000 Subject: Utilisateur : Fcolin Date : 8/11/02 Heure : 11:50 Créé Commentaire: (vss 1) --- IvyFileMon/DelayedDirectoryChangeHandler.cpp | 1414 ++++++++++++++++++++++++++ 1 file changed, 1414 insertions(+) create mode 100644 IvyFileMon/DelayedDirectoryChangeHandler.cpp (limited to 'IvyFileMon') 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 //for _beginthreadex + +#include // 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(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( 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(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(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( 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 -- cgit v1.1 From e072d4de3aefd1c69c9cc4689bb93f476c2163bd Mon Sep 17 00:00:00 2001 From: fcolin Date: Thu, 1 Feb 2007 13:07:31 +0000 Subject: Utilisateur : Fcolin Date : 8/11/02 Heure : 11:50 Créé Commentaire: (vss 1) --- IvyFileMon/DelayedDirectoryChangeHandler.h | 336 +++++++++++++++++++++++++++++ 1 file changed, 336 insertions(+) create mode 100644 IvyFileMon/DelayedDirectoryChangeHandler.h (limited to 'IvyFileMon') 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_) -- cgit v1.1 From 9df8a3d6305f2c4913ca3b7bd649b221687286f9 Mon Sep 17 00:00:00 2001 From: fcolin Date: Thu, 1 Feb 2007 13:07:34 +0000 Subject: Utilisateur : Fcolin Date : 8/11/02 Heure : 11:50 Créé Commentaire: (vss 1) --- IvyFileMon/DirectoryChanges.cpp | 2026 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 2026 insertions(+) create mode 100644 IvyFileMon/DirectoryChanges.cpp (limited to 'IvyFileMon') 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( ( 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. + // + }; + 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(&m_csDirWatchInfo), TRUE); + ASSERT( lock.IsLocked() ); + int i; + if( GetDirWatchInfo(strDirName, i) ) + return TRUE; + return FALSE; +} + +int CDirectoryChangeWatcher::NumWatchedDirectories()const +{ + CSingleLock lock(const_cast(&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(&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(&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(&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(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(-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)|.....| + |_______________________________________________________| + /-\ + | + 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)| ... | + |__________________________________________________________________________________________________________| + + 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 -- cgit v1.1 From 46d3304a3773732d535f19be422e666295d3dafd Mon Sep 17 00:00:00 2001 From: fcolin Date: Thu, 1 Feb 2007 13:07:36 +0000 Subject: Utilisateur : Fcolin Date : 8/11/02 Heure : 11:50 Créé Commentaire: (vss 1) --- IvyFileMon/DirectoryChanges.h | 493 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 493 insertions(+) create mode 100644 IvyFileMon/DirectoryChanges.h (limited to 'IvyFileMon') 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 +#include + +#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 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_) -- cgit v1.1 From d1ebbd33ea973c15f07dc0bbc545c9124d53cb03 Mon Sep 17 00:00:00 2001 From: fcolin Date: Thu, 1 Feb 2007 13:07:38 +0000 Subject: Utilisateur : Fcolin Date : 8/11/02 Heure : 11:50 Créé Commentaire: (vss 1) --- IvyFileMon/HistoryEdit.cpp | 82 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 IvyFileMon/HistoryEdit.cpp (limited to 'IvyFileMon') 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 -- cgit v1.1 From 6129e8b06c16eef9d439840305f9c77517aad28a Mon Sep 17 00:00:00 2001 From: fcolin Date: Thu, 1 Feb 2007 13:07:40 +0000 Subject: Utilisateur : Fcolin Date : 8/11/02 Heure : 11:50 Créé Commentaire: (vss 1) --- IvyFileMon/HistoryEdit.h | 65 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 IvyFileMon/HistoryEdit.h (limited to 'IvyFileMon') 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 -- cgit v1.1 From 671faa94ac7408f63a4c737c92a54e6c43cab78f Mon Sep 17 00:00:00 2001 From: fcolin Date: Thu, 1 Feb 2007 13:07:42 +0000 Subject: Utilisateur : Fcolin Date : 10/02/04 Heure : 17:12 Créé Commentaire: (vss 1) --- IvyFileMon/InstIvyFileMon/InstIvyFileMon.vdproj | 795 ++++++++++++++++++++++++ 1 file changed, 795 insertions(+) create mode 100644 IvyFileMon/InstIvyFileMon/InstIvyFileMon.vdproj (limited to 'IvyFileMon') 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:\\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:\\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:\\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:\\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:\\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:\\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:\\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:\\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:\\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:\\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:\\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:\\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" + { + } + } +} -- cgit v1.1 From 4ac98d2789245bb3ae881adc3bae83f7f081f5da Mon Sep 17 00:00:00 2001 From: fcolin Date: Thu, 1 Feb 2007 13:07:44 +0000 Subject: Utilisateur : Fcolin Date : 10/02/04 Heure : 17:12 Créé Commentaire: (vss 1) --- IvyFileMon/InstIvyFileMon/InstIvyFileMon.vdproj.vspscc | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 IvyFileMon/InstIvyFileMon/InstIvyFileMon.vdproj.vspscc (limited to 'IvyFileMon') 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" +} -- cgit v1.1 From 3e149af9b8d7ce2e59c347d283ca193818c1143c Mon Sep 17 00:00:00 2001 From: fcolin Date: Thu, 1 Feb 2007 13:07:46 +0000 Subject: Utilisateur : Fcolin Date : 8/11/02 Heure : 11:50 Créé Commentaire: (vss 1) --- IvyFileMon/IvyFileMon.cpp | 65 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 IvyFileMon/IvyFileMon.cpp (limited to 'IvyFileMon') 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; +} -- cgit v1.1 From 06779a8325e53d8ea4027181839f10418094a776 Mon Sep 17 00:00:00 2001 From: fcolin Date: Thu, 1 Feb 2007 13:07:48 +0000 Subject: Utilisateur : Fcolin Date : 8/11/02 Heure : 11:50 Créé Commentaire: (vss 1) --- IvyFileMon/IvyFileMon.h | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 IvyFileMon/IvyFileMon.h (limited to 'IvyFileMon') 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; -- cgit v1.1 From ea14e8fc808bf238054993fcff4da3c4fefa3567 Mon Sep 17 00:00:00 2001 From: fcolin Date: Thu, 1 Feb 2007 13:07:50 +0000 Subject: Utilisateur : Fcolin Date : 8/11/02 Heure : 11:50 Créé Commentaire: (vss 1) --- IvyFileMon/IvyFileMon.rc | 215 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 215 insertions(+) create mode 100644 IvyFileMon/IvyFileMon.rc (limited to 'IvyFileMon') 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: " + VALUE "FileDescription", "TODO: " + VALUE "FileVersion", "1.0.0.1" + VALUE "InternalName", "IvyFileMon.exe" + VALUE "LegalCopyright", "TODO: (c) . Tous droits réservés." + VALUE "OriginalFilename", "IvyFileMon.exe" + VALUE "ProductName", "TODO: " + 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 + -- cgit v1.1 From 889a7ccfe07adba9bb857622bfbfc95706acea60 Mon Sep 17 00:00:00 2001 From: fcolin Date: Thu, 1 Feb 2007 13:07:52 +0000 Subject: Utilisateur : Fcolin Date : 8/11/02 Heure : 11:50 Créé Commentaire: (vss 1) --- IvyFileMon/IvyFileMon.sln | 53 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 IvyFileMon/IvyFileMon.sln (limited to 'IvyFileMon') 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 -- cgit v1.1 From 4aa144b2483d8cf4f17e37c3214e6a58fa135e5d Mon Sep 17 00:00:00 2001 From: fcolin Date: Thu, 1 Feb 2007 13:07:54 +0000 Subject: Utilisateur : Fcolin Date : 8/11/02 Heure : 11:50 Créé Commentaire: (vss 1) --- IvyFileMon/IvyFileMon.vcproj | 207 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 207 insertions(+) create mode 100644 IvyFileMon/IvyFileMon.vcproj (limited to 'IvyFileMon') 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 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -- cgit v1.1 From 42fcd6c9b206b836111cd971979e9a9112631f8c Mon Sep 17 00:00:00 2001 From: fcolin Date: Thu, 1 Feb 2007 13:07:56 +0000 Subject: Utilisateur : Fcolin Date : 8/11/02 Heure : 11:50 Créé Commentaire: (vss 1) --- IvyFileMon/IvyFileMon.vcproj.vspscc | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 IvyFileMon/IvyFileMon.vcproj.vspscc (limited to 'IvyFileMon') 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" +} -- cgit v1.1 From a6a51d2542f3f0d9097c31b48fa8ab0a6482182a Mon Sep 17 00:00:00 2001 From: fcolin Date: Thu, 1 Feb 2007 13:07:58 +0000 Subject: Utilisateur : Fcolin Date : 8/11/02 Heure : 11:50 Créé Commentaire: (vss 1) --- IvyFileMon/IvyFileMon.vsscc | Bin 0 -> 46 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 IvyFileMon/IvyFileMon.vsscc (limited to 'IvyFileMon') diff --git a/IvyFileMon/IvyFileMon.vsscc b/IvyFileMon/IvyFileMon.vsscc new file mode 100644 index 0000000..8fdac15 Binary files /dev/null and b/IvyFileMon/IvyFileMon.vsscc differ -- cgit v1.1 From fc282dd1d7ed8e0a9af79f7ea3e92b37b51c337a Mon Sep 17 00:00:00 2001 From: fcolin Date: Thu, 1 Feb 2007 13:07:59 +0000 Subject: Utilisateur : Fcolin Date : 13/12/06 Heure : 12:02 Archivé dans $/Bus/IvyFileMon Commentaire: (vss 2) --- IvyFileMon/IvyFileMon.vsscc | Bin 46 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) (limited to 'IvyFileMon') diff --git a/IvyFileMon/IvyFileMon.vsscc b/IvyFileMon/IvyFileMon.vsscc index 8fdac15..e69de29 100644 Binary files a/IvyFileMon/IvyFileMon.vsscc and b/IvyFileMon/IvyFileMon.vsscc differ -- cgit v1.1 From 1cb87e9c6664cb4b8f0c2433a3a664b62148356b Mon Sep 17 00:00:00 2001 From: fcolin Date: Thu, 1 Feb 2007 13:08:02 +0000 Subject: Utilisateur : Fcolin Date : 8/11/02 Heure : 11:50 Créé Commentaire: (vss 1) --- IvyFileMon/IvyFileMon.vssscc | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 IvyFileMon/IvyFileMon.vssscc (limited to 'IvyFileMon') 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" +} -- cgit v1.1 From 3ac84251ed49473c02cc388574b5be73316e3dca Mon Sep 17 00:00:00 2001 From: fcolin Date: Thu, 1 Feb 2007 13:08:04 +0000 Subject: Utilisateur : Fcolin Date : 8/11/02 Heure : 11:50 Créé Commentaire: (vss 1) --- IvyFileMon/IvyFileMonDlg.cpp | 281 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 281 insertions(+) create mode 100644 IvyFileMon/IvyFileMonDlg.cpp (limited to 'IvyFileMon') 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(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(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 -- cgit v1.1 From 2375783687e0c29cb687eb29e55692a1b503fb1b Mon Sep 17 00:00:00 2001 From: fcolin Date: Thu, 1 Feb 2007 13:08:05 +0000 Subject: Utilisateur : Fcolin Date : 8/11/02 Heure : 11:50 Créé Commentaire: (vss 1) --- IvyFileMon/IvyFileMonDlg.h | 55 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 IvyFileMon/IvyFileMonDlg.h (limited to 'IvyFileMon') 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; +}; -- cgit v1.1 From 8310b749001f3376a92e4ff688d5c27602bd662d Mon Sep 17 00:00:00 2001 From: fcolin Date: Thu, 1 Feb 2007 13:08:08 +0000 Subject: Utilisateur : Fcolin Date : 8/11/02 Heure : 11:50 Créé Commentaire: (vss 1) --- IvyFileMon/ParseCmdLine.cpp | 76 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 IvyFileMon/ParseCmdLine.cpp (limited to 'IvyFileMon') 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; + } +} -- cgit v1.1 From fbdc27d3ad67db5fec3cca3df4d397c9707f3405 Mon Sep 17 00:00:00 2001 From: fcolin Date: Thu, 1 Feb 2007 13:08:09 +0000 Subject: Utilisateur : Fcolin Date : 8/11/02 Heure : 11:50 Créé Commentaire: (vss 1) --- IvyFileMon/ParseCmdLine.h | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 IvyFileMon/ParseCmdLine.h (limited to 'IvyFileMon') 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_) -- cgit v1.1 From ba860ebd814e95be1c66191e022fe23111132494 Mon Sep 17 00:00:00 2001 From: fcolin Date: Thu, 1 Feb 2007 13:08:12 +0000 Subject: Utilisateur : Fcolin Date : 8/11/02 Heure : 11:50 Créé Commentaire: (vss 1) --- IvyFileMon/ReadMe.txt | 87 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 IvyFileMon/ReadMe.txt (limited to 'IvyFileMon') 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. + +///////////////////////////////////////////////////////////////////////////// -- cgit v1.1 From c62920d3d9ab33eb69fc21594a28eb4a9a24bafb Mon Sep 17 00:00:00 2001 From: fcolin Date: Thu, 1 Feb 2007 13:08:14 +0000 Subject: Utilisateur : Fcolin Date : 8/11/02 Heure : 11:50 Créé Commentaire: (vss 1) --- IvyFileMon/res/IvyFileMon.ico | Bin 0 -> 21630 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 IvyFileMon/res/IvyFileMon.ico (limited to 'IvyFileMon') diff --git a/IvyFileMon/res/IvyFileMon.ico b/IvyFileMon/res/IvyFileMon.ico new file mode 100644 index 0000000..8a84ca3 Binary files /dev/null and b/IvyFileMon/res/IvyFileMon.ico differ -- cgit v1.1 From a7af71b683dd1d642847ca77bb06e6b9e9bb375c Mon Sep 17 00:00:00 2001 From: fcolin Date: Thu, 1 Feb 2007 13:08:16 +0000 Subject: Utilisateur : Fcolin Date : 8/11/02 Heure : 11:50 Créé Commentaire: (vss 1) --- IvyFileMon/res/IvyFileMon.manifest | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 IvyFileMon/res/IvyFileMon.manifest (limited to 'IvyFileMon') 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 @@ + + + +Tapez ici une description de votre application + + + + + + -- cgit v1.1 From d3fa1a356c6ff06ddafc3d70b085635fb4a8120e Mon Sep 17 00:00:00 2001 From: fcolin Date: Thu, 1 Feb 2007 13:08:17 +0000 Subject: Utilisateur : Fcolin Date : 8/11/02 Heure : 11:50 Créé Commentaire: (vss 1) --- IvyFileMon/res/IvyFileMon.rc2 | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 IvyFileMon/res/IvyFileMon.rc2 (limited to 'IvyFileMon') 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... + +///////////////////////////////////////////////////////////////////////////// -- cgit v1.1 From 6197a791ac149a3c5725812e11a7a3ef908130dc Mon Sep 17 00:00:00 2001 From: fcolin Date: Thu, 1 Feb 2007 13:08:19 +0000 Subject: Utilisateur : Fcolin Date : 8/11/02 Heure : 11:50 Créé Commentaire: (vss 1) --- IvyFileMon/resource.h | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 IvyFileMon/resource.h (limited to 'IvyFileMon') 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 -- cgit v1.1 From 32f91ed8b10e1c3a71dd835fc412b1ffcafbb963 Mon Sep 17 00:00:00 2001 From: fcolin Date: Thu, 1 Feb 2007 13:08:21 +0000 Subject: Utilisateur : Fcolin Date : 8/11/02 Heure : 11:50 Créé Commentaire: (vss 1) --- IvyFileMon/stdafx.cpp | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 IvyFileMon/stdafx.cpp (limited to 'IvyFileMon') 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" + + -- cgit v1.1 From 7eaafdc194b3b0c7ab16a8182275498f86e58eaf Mon Sep 17 00:00:00 2001 From: fcolin Date: Thu, 1 Feb 2007 13:08:23 +0000 Subject: Utilisateur : Fcolin Date : 8/11/02 Heure : 11:50 Créé Commentaire: (vss 1) --- IvyFileMon/stdafx.h | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 IvyFileMon/stdafx.h (limited to 'IvyFileMon') 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 // composants MFC principaux et standard +#include // extensions MFC +#include // classes Automation MFC + +#include // Prise en charge MFC des contrôles communs Internet Explorer 4 +#ifndef _AFX_NO_AFXCMN_SUPPORT +#include // Prise en charge MFC des contrôles communs Windows +#endif // _AFX_NO_AFXCMN_SUPPORT +#include -- cgit v1.1