Filesystem Notification

  • Thread starter Thread starter vivekian
  • Start date Start date
V

vivekian

Guest
Hi,

Writing a small application which requires the application to be
informed when the content / filename / location of a particular file
changes. Is there a Win32 class available which does this ?

Thanks in advance,
vivekian
 
Re: Filesystem Notification

On Apr 7, 3:48 pm, vivekian <vivekase...@gmail.com> wrote:
> Hi,
>
> Writing a small application which requires the application to be
> informed when the  content / filename / location of a particular file
> changes. Is there a Win32 class available which does this ?
>
> Thanks in advance,
> vivekian



Hi,

You can use the following API to retrieve information that
describes the changes within the specified directory:

ReadDirectoryChangesW()

http://msdn2.microsoft.com/en-us/library/aa365465(VS.85).aspx

Kellie.
 
Re: Filesystem Notification

"Kellie Fitton" <KELLIEFITTON@yahoo.com> wrote in message
news:60ab34b1-ef43-4c37-b1bb-9579e052bbe3@a5g2000prg.googlegroups.com...
On Apr 7, 3:48 pm, vivekian <vivekase...@gmail.com> wrote:
> Hi,
>
> Writing a small application which requires the application to be
> informed when the content / filename / location of a particular file
> changes. Is there a Win32 class available which does this ?
>
> Thanks in advance,
> vivekian



Take a look at the FindFirstChangeNotification API. In the MSDN library,
the path is Win32 and COM Development -> System Services -> File Services ->
File Systems ->Directory Management -> Using Directory Management ->
Obtaining Directory Change Notifications. If you are coding in dotNet, take
a look at the System.IO.FileSystemWatcher class which is a wrapper for the
FindFirstChangeNotification API functions.

Mike.
 
Re: Filesystem Notification

Hello!
You wrote on Mon, 7 Apr 2008 15:48:19 -0700 (PDT):

v> Writing a small application which requires the application to be
v> informed when the content / filename / location of a particular file
v> changes. Is there a Win32 class available which does this ?

You can try FindFirstChangeNotification API as mentioned, but it seems that
it won't be enough for your task as it won't give you information that your
file has been moved or renamed.
You can check CallbackFilter ( http://www.eldos.com/cbflt/ ) that would let
you track file system events in real-time and give complete control over the
events.

With best regards,
Eugene Mayevski
http://mayevski.blogspot.com/
 
Re: Filesystem Notification

On Apr 8, 9:52 am, "Eugene Mayevski" <mayev...@eldos.com> wrote:
> Hello!
> You wrote  on Mon, 7 Apr 2008 15:48:19 -0700 (PDT):
>
>  v> Writing a small application which requires the application to be
>  v> informed when the  content / filename / location of a particular file
>  v> changes. Is there aWin32class available which does this ?
>
> You can try FindFirstChangeNotification API as mentioned, but it seems that
> it won't be enough for your task as it won't give you information that your
> file has been moved or renamed.
> You can check CallbackFilter (http://www.eldos.com/cbflt/) that would let
> you track file system events in real-time and give complete control over the
> events.
>
> With best regards,
> Eugene Mayevskihttp://mayevski.blogspot.com/


Thanks a bunch for all the pointers !!
 
Re: Filesystem Notification

On 8 avr, 16:52, "Eugene Mayevski" <mayev...@eldos.com> wrote:

> You can try FindFirstChangeNotification API as mentioned, but it seems that
> it won't be enough for your task as it won't give you information that your
> file has been moved or renamed.
> You can check CallbackFilter (http://www.eldos.com/cbflt/) that would let
> you track file system events in real-time and give complete control over the
> events.


SHChangeNotifyRegister() does this...
 
Re: Filesystem Notification

Hello!
You wrote on Tue, 8 Apr 2008 08:10:24 -0700 (PDT):

CA> SHChangeNotifyRegister() does this...

"This function is available through Microsoft Windows XP Service Pack 2
(SP2) and Windows Server 2003"

Also, this gives only notification and not control.

With best regards,
Eugene Mayevski
http://mayevski.blogspot.com/
 
Re: Filesystem Notification

On 8 avr, 17:35, "Eugene Mayevski" <mayev...@eldos.com> wrote:
> Hello!
> You wrote  on Tue, 8 Apr 2008 08:10:24 -0700 (PDT):
>
>  CA> SHChangeNotifyRegister() does this...
>
> "This function is available through Microsoft Windows XP Service Pack 2
> (SP2) and Windows Server 2003"


It's available since NT4 (dynamic call), but was not documented
before...
 
Re: Filesystem Notification

On Apr 7, 7:45 pm, "Michael D. Ober" <obermd.@.alum.mit.edu.nospam.>
wrote:
> "Kellie Fitton" <KELLIEFIT...@yahoo.com> wrote in message
>
> news:60ab34b1-ef43-4c37-b1bb-9579e052bbe3@a5g2000prg.googlegroups.com...
> On Apr 7, 3:48 pm, vivekian <vivekase...@gmail.com> wrote:
>
> > Hi,

>
> > Writing a small application which requires the application to be
> > informed when the content / filename / location of a particular file
> > changes. Is there a Win32 class available which does this ?

>
> > Thanks in advance,
> > vivekian

>
> Take a look at the FindFirstChangeNotification API. In the MSDN library,
> the path is Win32 and COM Development -> System Services -> File Services ->
> File Systems ->Directory Management -> Using Directory Management ->
> Obtaining Directory Change Notifications. If you are coding in dotNet, take
> a look at the System.IO.FileSystemWatcher class which is a wrapper for the
> FindFirstChangeNotification API functions.
>
> Mike.


I imagine coding in dotNet would require the .NET platform to become a
prerequisite for the application ?

~vivekian
 
Re: Filesystem Notification

"vivekian" <vivekaseeja@gmail.com> wrote in message
news:f4e4bced-805c-4dc4-a00e-4e8e533b04a4@u36g2000prf.googlegroups.com...
> On Apr 7, 7:45 pm, "Michael D. Ober" <obermd.@.alum.mit.edu.nospam.>
> wrote:
>> "Kellie Fitton" <KELLIEFIT...@yahoo.com> wrote in message
>>
>> news:60ab34b1-ef43-4c37-b1bb-9579e052bbe3@a5g2000prg.googlegroups.com...
>> On Apr 7, 3:48 pm, vivekian <vivekase...@gmail.com> wrote:
>>
>> > Hi,

>>
>> > Writing a small application which requires the application to be
>> > informed when the content / filename / location of a particular file
>> > changes. Is there a Win32 class available which does this ?

>>
>> > Thanks in advance,
>> > vivekian

>>
>> Take a look at the FindFirstChangeNotification API. In the MSDN library,
>> the path is Win32 and COM Development -> System Services -> File
>> Services ->
>> File Systems ->Directory Management -> Using Directory Management ->
>> Obtaining Directory Change Notifications. If you are coding in dotNet,
>> take
>> a look at the System.IO.FileSystemWatcher class which is a wrapper for
>> the
>> FindFirstChangeNotification API functions.
>>
>> Mike.

>
> I imagine coding in dotNet would require the .NET platform to become a
> prerequisite for the application ?
>



True, which is why I listed both methods. The question of monitoring the
file system appears periodically in the dotnet programming groups as well.
Your post wasn't clear what your development environment was. Also, the
FindFirstChangeNotification supports renames, creates, closes, changes, and
deletes. The one drawback is that the file system must be on an NT4 or
Windows 95 or later system. Samba doesn't support this API.

Mike.

Mike.
 
Re: Filesystem Notification

On 8 Apr., 00:48, vivekian <vivekase...@gmail.com> wrote:
> Hi,
>
> Writing a small application which requires the application to be
> informed when the  content / filename / location of a particular file
> changes. Is there a Win32 class available which does this ?


I wrote my own which tells me that a file was modified,
moved or deleted. However, if the file was moved, it
does not find out the new location and does not monitor
the file with it's new filename.

Here is my code:


/////////////////////////////////////////////////////////////////////////////
// Copyright:
// Uwe Kotyczka <uwe.kotyczka@web.de>
// created: February 2000
//
/////////////////////////////////////////////////////////////////////////////
// WatchInThreadBase.h : Schnittstelle der Klasse CWatchInThreadBase
//
/////////////////////////////////////////////////////////////////////////////

#if !
defined(AFX_WATCHINTHREADBASE_H__7C0A94EE_0665_11D5_8CB4_00001CD5E4D1__INCLUDED_)
#define
AFX_WATCHINTHREADBASE_H__7C0A94EE_0665_11D5_8CB4_00001CD5E4D1__INCLUDED_

#if defined(_MSC_VER) && (_MSC_VER >= 1020)
#pragma once
#endif


class AFX_EXT_CLASS_GENERAL CWatchInThreadBase
{
public:
CWatchInThreadBase();

// Attribute
public:
HANDLE m_hEventStartWatch;
HANDLE m_hEventWatchDone;
HANDLE m_hEventKillWatchThread;
HANDLE m_hEventWatchThreadKilled;

HWND m_hwndNotifyFileChanged;
CString m_strPathNameWatch;

protected:
CWinThread* m_pWatchWorkerThread;

BOOL m_bFileLost;

// Überladungen
// Vom Klassenassistenten generierte Überladungen virtueller
Funktionen
//{{AFX_VIRTUAL(CWatchInThreadBase)
public:
virtual void SetFileLostFlag(BOOL bFileLost = TRUE);
virtual BOOL IsFileLost();
protected:
virtual BOOL DoFileAlarm(int nCause, LPCTSTR szPathName);
virtual BOOL PostFileAlarmMsg(UINT message, int nCause) = 0;
//}}AFX_VIRTUAL

// Implementierung
public:
virtual ~CWatchInThreadBase();

void ShutDownWatchThreadSafely();
protected:
BOOL FileWatch(LPCTSTR szPathName);
};

inline void CWatchInThreadBase::SetFileLostFlag(BOOL bFileLost)
{ ASSERT(this != NULL); m_bFileLost = bFileLost; }
inline BOOL CWatchInThreadBase::IsFileLost()
{ ASSERT(this != NULL); return m_bFileLost; }
inline BOOL CWatchInThreadBase::PostFileAlarmMsg(UINT message, int
nCause)
{ ASSERT(FALSE); UNREFERENCED_PARAMETER(message);
UNREFERENCED_PARAMETER(nCause); return FALSE; }

/////////////////////////////////////////////////////////////////////////////

//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ fügt unmittelbar vor der vorhergehenden Zeile
zusätzliche Deklarationen ein.

#endif // !
defined(AFX_WATCHINTHREADBASE_H__7C0A94EE_0665_11D5_8CB4_00001CD5E4D1__INCLUDED_)



/////////////////////////////////////////////////////////////////////////////
// Copyright:
// Uwe Kotyczka <uwe.kotyczka@web.de>
// created: February 2000
//
/////////////////////////////////////////////////////////////////////////////
// WatchInThreadBase.cpp : Implementierung der Klasse
CWatchInThreadBase
//

#include "StdAfx.h"
#include "GlWinApp.h"
#include "WatchInThreadBase.h"
#include "Helper.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CWatchInThreadBase

CWatchInThreadBase::CWatchInThreadBase()
{
m_hEventStartWatch = CreateEvent(NULL, FALSE, FALSE, NULL); // auto
reset, initially reset
m_hEventWatchDone = CreateEvent(NULL, TRUE, TRUE, NULL); // manual
reset, initially set
m_hEventKillWatchThread = CreateEvent(NULL, FALSE, FALSE, NULL); //
auto reset, initially reset
m_hEventWatchThreadKilled = CreateEvent(NULL, FALSE, FALSE, NULL); //
auto reset, initially reset

m_hwndNotifyFileChanged = NULL;

m_pWatchWorkerThread = NULL;
m_bFileLost = FALSE;
}

CWatchInThreadBase::~CWatchInThreadBase()
{
ShutDownWatchThreadSafely();

CloseHandle(m_hEventStartWatch);
CloseHandle(m_hEventWatchDone);
CloseHandle(m_hEventKillWatchThread);
CloseHandle(m_hEventWatchThreadKilled);
}

void CWatchInThreadBase::ShutDownWatchThreadSafely()
{
// It's a good idea to wait for the worker thread to notify via a
// "thread killed" event that it has killed itself. Otherwise, in the
case
// where the app is terminating, is possible (even if unlikely) that
it
// will detect a memory leak of the CWinThread object before the
// CWinThread object has had a chance to auto-delete itself.

DWORD dwExitCode;
HANDLE hThread = m_pWatchWorkerThread ? m_pWatchWorkerThread-
>m_hThread : NULL;

if (m_pWatchWorkerThread != NULL &&
GetExitCodeThread(hThread, &dwExitCode) &&
dwExitCode == STILL_ACTIVE)
{
// Kill the worker thread by setting the "watch done" and "kill
thread" events.
SetEvent(m_hEventKillWatchThread);
SetEvent(m_hEventWatchDone);
SetEvent(m_hEventStartWatch);
//WaitForSingleObject(m_hEventWatchThreadKilled, INFINITE);
while (WaitForSingleObject(m_hEventWatchThreadKilled, 1) !=
WAIT_OBJECT_0)
{
// Allow the calling thread to handle messages. This might be
useful e.g. if
// the worker thread pops up a MessageBox, which must be closed
before it can
// finish itself.
if (!RunExtraLoop())
return;
}

// Next lines seems to be nessesary.
// When exiting the app it happens that the main thread is finshed
// before the worker thread has ended itself. To avoid a memory
// leak we give the worker thread some more time to end itself.
while (GetExitCodeThread(hThread, &dwExitCode) &&
dwExitCode == STILL_ACTIVE)
{
WaitForSingleObject(m_hEventWatchThreadKilled, 1);
}

// Reset it to be able to start a new worker thread by
"WatchInThread"
m_pWatchWorkerThread = NULL;
}
}

BOOL CWatchInThreadBase::FileWatch(LPCTSTR szPathName)
{
// Note: In order to get a correct behaviour in a SDI environment
// we start file watching in OnOpenDocument *BEFORE* the path name is
set
// correctly. This is due to the fact that an overwritten
OnOpenDocument in
// a derived class may return FALSE.
// So when the path name has changed a short time after file watching
has been started
// we stop this FileWatch procedure and restart a new one.
DWORD dwResult = WAIT_TIMEOUT, dwLastError;

// open the docfile
CFile file;
BOOL bRet = file.Open(szPathName, CFile::modeRead |
CFile::shareDenyNone);

if (bRet)
{
// save the last write time
FILETIME ftLastWriteTime;
BY_HANDLE_FILE_INFORMATION fileinfo;

if (!GetFileInformationByHandle((HANDLE)file.m_hFile, &fileinfo))
{
file.Abort();
return FALSE;
}

ftLastWriteTime = fileinfo.ftLastWriteTime;
file.Close();

// determine the path
CString strPath = szPathName;
strPath = strPath.Left(strPath.ReverseFind('\\'));
ASSERT(!strPath.IsEmpty());

// endless loop
while (TRUE)
{
// get file notify event
HANDLE hFileChanged = FindFirstChangeNotification(strPath, FALSE,
FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_SIZE |
FILE_NOTIFY_CHANGE_FILE_NAME);

// wait for request to abort watching or for file change event
HANDLE hWaitEvents[] = { m_hEventWatchDone, hFileChanged };
//DWORD dwResult = WaitForMultipleObjects(2, hWaitEvents, FALSE,
INFINITE);
while (TRUE)
{
// some devices seem to NOT support FindFirstChangeNotification
// so we take only the first handle into account then
dwResult = WaitForMultipleObjects(hFileChanged ==
INVALID_HANDLE_VALUE ? 1 : 2, hWaitEvents, FALSE, 10);

if (dwResult == WAIT_OBJECT_0 + 0 || dwResult == WAIT_OBJECT_0 +
1)
{
break;
}
}
FindCloseChangeNotification(hFileChanged);

// some file in the directory changed
if (dwResult == WAIT_OBJECT_0 + 1)
{
// retrieve docfile's write time
BOOL bSuccess = file.Open(szPathName, CFile::modeRead |
CFile::shareDenyNone);
dwLastError = GetLastError();
if (!bSuccess && dwLastError == ERROR_FILE_NOT_FOUND)
{
// Try it a second time, some network devices report
ERROR_FILE_NOT_FOUND
// even if the file was just written.
bSuccess = file.Open(szPathName, CFile::modeRead |
CFile::shareDenyNone);
dwLastError = GetLastError();
}
if (bSuccess)
{
if (!GetFileInformationByHandle((HANDLE)file.m_hFile, &fileinfo))
{
file.Abort();
}
else
{
// detect if changes made
BOOL bAlarm = (ftLastWriteTime.dwHighDateTime !=
fileinfo.ftLastWriteTime.dwHighDateTime ||
ftLastWriteTime.dwLowDateTime !=
fileinfo.ftLastWriteTime.dwLowDateTime ||
IsFileLost());

// resave last time
ftLastWriteTime = fileinfo.ftLastWriteTime;
file.Close();
SetFileLostFlag(FALSE);

if (bAlarm)
{
if (!DoFileAlarm(FA_FILE_WRITTEN, szPathName))
{
// leave the outer loop if watching was
// aborted while being in DoFileAlarm.
break;
}
}
}
}
else if (!IsFileLost() && dwLastError == ERROR_FILE_NOT_FOUND)
{
SetFileLostFlag(TRUE);
if (!DoFileAlarm(FA_FILE_LOST, szPathName))
{
// leave the outer loop if watching was
// aborted while being in DoFileAlarm.
break;
}
}
else
{
TRACE1("FileWatch: An error occured (%d).\n", dwLastError);
}
}
else
break;
}
}
return bRet;
}

BOOL CWatchInThreadBase::DoFileAlarm(int nCause, LPCTSTR szPathName)
{
BOOL bInvert = FALSE;

// alarm
MessageBeep(MB_ICONEXCLAMATION);

while (TRUE)
{
if (WaitForSingleObject(m_hEventWatchDone, 500) == WAIT_OBJECT_0)
{
if (bInvert)
FlashWindow(m_hwndNotifyFileChanged, FALSE);

return FALSE;
}
else if (!IsIconic(m_hwndNotifyFileChanged) &&
IsWindowEnabled(m_hwndNotifyFileChanged))
{
if (bInvert)
FlashWindow(m_hwndNotifyFileChanged, FALSE);

if (GetForegroundWindow() == m_hwndNotifyFileChanged)
{
// Note: We don't call OnFileAlarm in the watching worker thread.
In order to
// reopen the document it will be necessary to update the views,
which can only
// be done by the creating thread. So we have to post a message to
the main thread.
// If the main thread is blocked by a MessageBox, then this
message might get lost.
// To avoid that we make sure that the main window is enabled
before posting the
// message.
m_strPathNameWatch = szPathName;
PostFileAlarmMsg(WM_FILE_ALARM, nCause);
break;
}
}
else
{
bInvert = !bInvert;
FlashWindow(m_hwndNotifyFileChanged, bInvert);
}
}
return TRUE;
}

It is just an abstract class, so you have to derive
from it. I usually use by deriving from both CDocument
and CWatchInThreadBase.

HTH
 
Re: Filesystem Notification

On Apr 9, 3:52 am, Uwe Kotyczka <uwe.kotyc...@web.de> wrote:
> On 8 Apr., 00:48, vivekian <vivekase...@gmail.com> wrote:
>
> > Hi,

>
> > Writing a small application which requires the application to be
> > informed when the  content / filename / location of a particular file
> > changes. Is there a Win32 class available which does this ?

>
> I wrote my own which tells me that a file was modified,
> moved or deleted. However, if the file was moved, it
> does not find out the new location and does not monitor
> the file with it's new filename.
>
> Here is my code:
>
> /////////////////////////////////////////////////////////////////////////////
> // Copyright:
> // Uwe Kotyczka <uwe.kotyc...@web.de>
> // created: February 2000
> //
> /////////////////////////////////////////////////////////////////////////////
> // WatchInThreadBase.h : Schnittstelle der Klasse CWatchInThreadBase
> //
> /////////////////////////////////////////////////////////////////////////////
>
> #if !
> defined(AFX_WATCHINTHREADBASE_H__7C0A94EE_0665_11D5_8CB4_00001CD5E4D1__INCLUDED_)
> #define
> AFX_WATCHINTHREADBASE_H__7C0A94EE_0665_11D5_8CB4_00001CD5E4D1__INCLUDED_
>
> #if defined(_MSC_VER) && (_MSC_VER >= 1020)
> #pragma once
> #endif
>
> class AFX_EXT_CLASS_GENERAL CWatchInThreadBase
> {
> public:
>         CWatchInThreadBase();
>
> // Attribute
> public:
>         HANDLE m_hEventStartWatch;
>         HANDLE m_hEventWatchDone;
>         HANDLE m_hEventKillWatchThread;
>         HANDLE m_hEventWatchThreadKilled;
>
>         HWND m_hwndNotifyFileChanged;
>         CString m_strPathNameWatch;
>
> protected:
>         CWinThread* m_pWatchWorkerThread;
>
>         BOOL m_bFileLost;
>
> // Überladungen
>         // Vom Klassenassistenten generierte Überladungen virtueller
> Funktionen
>         //{{AFX_VIRTUAL(CWatchInThreadBase)
>         public:
>         virtual void SetFileLostFlag(BOOL bFileLost = TRUE);
>         virtual BOOL IsFileLost();
>         protected:
>         virtual BOOL DoFileAlarm(int nCause, LPCTSTR szPathName);
>         virtual BOOL PostFileAlarmMsg(UINT message, int nCause) = 0;
>         //}}AFX_VIRTUAL
>
> // Implementierung
> public:
>         virtual ~CWatchInThreadBase();
>
>         void ShutDownWatchThreadSafely();
> protected:
>         BOOL FileWatch(LPCTSTR szPathName);
>
> };
>
> inline void CWatchInThreadBase::SetFileLostFlag(BOOL bFileLost)
>         { ASSERT(this != NULL); m_bFileLost = bFileLost; }
> inline BOOL CWatchInThreadBase::IsFileLost()
>         { ASSERT(this != NULL); return m_bFileLost; }
> inline BOOL CWatchInThreadBase::PostFileAlarmMsg(UINT message, int
> nCause)
>         { ASSERT(FALSE); UNREFERENCED_PARAMETER(message);
> UNREFERENCED_PARAMETER(nCause); return FALSE; }
>
> /////////////////////////////////////////////////////////////////////////////
>
> //{{AFX_INSERT_LOCATION}}
> // Microsoft Visual C++ fügt unmittelbar vor der vorhergehenden Zeile
> zusätzliche Deklarationen ein.
>
> #endif // !
> defined(AFX_WATCHINTHREADBASE_H__7C0A94EE_0665_11D5_8CB4_00001CD5E4D1__INCLUDED_)
>
> /////////////////////////////////////////////////////////////////////////////
> // Copyright:
> // Uwe Kotyczka <uwe.kotyc...@web.de>
> // created: February 2000
> //
> /////////////////////////////////////////////////////////////////////////////
> // WatchInThreadBase.cpp : Implementierung der Klasse
> CWatchInThreadBase
> //
>
> #include "StdAfx.h"
> #include "GlWinApp.h"
> #include "WatchInThreadBase.h"
> #include "Helper.h"
>
> #ifdef _DEBUG
> #define new DEBUG_NEW
> #undef THIS_FILE
> static char THIS_FILE[] = __FILE__;
> #endif
>
> /////////////////////////////////////////////////////////////////////////////
> // CWatchInThreadBase
>
> CWatchInThreadBase::CWatchInThreadBase()
> {
>         m_hEventStartWatch = CreateEvent(NULL, FALSE, FALSE, NULL);                     // auto
> reset, initially reset
>         m_hEventWatchDone = CreateEvent(NULL, TRUE, TRUE, NULL);                        // manual
> reset, initially set
>         m_hEventKillWatchThread = CreateEvent(NULL, FALSE, FALSE, NULL);        //
> auto reset, initially reset
>         m_hEventWatchThreadKilled = CreateEvent(NULL, FALSE, FALSE, NULL);      //
> auto reset, initially reset
>
>         m_hwndNotifyFileChanged = NULL;
>
>         m_pWatchWorkerThread = NULL;
>         m_bFileLost = FALSE;
>
> }
>
> CWatchInThreadBase::~CWatchInThreadBase()
> {
>         ShutDownWatchThreadSafely();
>
>         CloseHandle(m_hEventStartWatch);
>         CloseHandle(m_hEventWatchDone);
>         CloseHandle(m_hEventKillWatchThread);
>         CloseHandle(m_hEventWatchThreadKilled);
>
> }
>
> void CWatchInThreadBase::ShutDownWatchThreadSafely()
> {
>         // It's a good idea to wait for the worker thread to notify via a
>         // "thread killed" event that it has killed itself. Otherwise, in the
> case
>         // where the app is terminating, is possible (even if unlikely) that
> it
>         // will detect a memory leak of the CWinThread object before the
>         // CWinThread object has had a chance to auto-delete itself.
>
>         DWORD dwExitCode;
>         HANDLE hThread = m_pWatchWorkerThread ? m_pWatchWorkerThread->m_hThread : NULL;
>
>         if (m_pWatchWorkerThread != NULL &&
>                 GetExitCodeThread(hThread, &dwExitCode) &&
>                 dwExitCode == STILL_ACTIVE)
>         {
>                 // Kill the worker thread by setting the "watch done" and "kill
> thread" events.
>                 SetEvent(m_hEventKillWatchThread);
>                 SetEvent(m_hEventWatchDone);
>                 SetEvent(m_hEventStartWatch);
>                 //WaitForSingleObject(m_hEventWatchThreadKilled, INFINITE);
>                 while (WaitForSingleObject(m_hEventWatchThreadKilled, 1) !=
> WAIT_OBJECT_0)
>                 {
>                         // Allow the calling thread to handle messages. This might be
> useful e.g. if
>                         // the worker thread pops up a MessageBox, which must be closed
> before it can
>                         // finish itself.
>                         if (!RunExtraLoop())
>                                 return;
>                 }
>
>                 // Next lines seems to be nessesary.
>                 // When exiting the app it happens that the main thread is finshed
>                 // before the worker thread has ended itself. To avoid a memory
>                 // leak we give the worker thread some more time to end itself.
>                 while (GetExitCodeThread(hThread, &dwExitCode) &&
>                         dwExitCode == STILL_ACTIVE)
>                 {
>                         WaitForSingleObject(m_hEventWatchThreadKilled, 1);
>                 }
>
>                 // Reset it to be able to start a new worker thread by
> "WatchInThread"
>                 m_pWatchWorkerThread = NULL;
>         }
>
> }
>
> BOOL CWatchInThreadBase::FileWatch(LPCTSTR szPathName)
> {
>         // Note: In order to get a correct behaviour in a SDI environment
>         //      we start file watching in OnOpenDocument *BEFORE* the path name is
> set
>         //      correctly. This is due to the fact that an overwritten
> OnOpenDocument in
>         //      a derived class may return FALSE.
>         //      So when the path name has changed a short time after file watching
> has been started
>         //      we stop this FileWatch procedure and restart a new one.
>         DWORD dwResult = WAIT_TIMEOUT, dwLastError;
>
>         // open the docfile
>         CFile file;
>         BOOL bRet = file.Open(szPathName, CFile::modeRead |
> CFile::shareDenyNone);
>
>         if (bRet)
>         {
>                 // save the last write time
>                 FILETIME ftLastWriteTime;
>                 BY_HANDLE_FILE_INFORMATION fileinfo;
>
>                 if (!GetFileInformationByHandle((HANDLE)file.m_hFile, &fileinfo))
>                 {
>                         file.Abort();
>                         return FALSE;
>                 }
>
>                 ftLastWriteTime = fileinfo.ftLastWriteTime;
>                 file.Close();
>
>                 // determine the path
>                 CString strPath = szPathName;
>                 strPath = strPath.Left(strPath.ReverseFind('\\'));
>                 ASSERT(!strPath.IsEmpty());
>
>                 // endless loop
>                 while (TRUE)
>                 {
>                         // get file notify event
>                         HANDLE hFileChanged = FindFirstChangeNotification(strPath, FALSE,
> FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_SIZE |
> FILE_NOTIFY_CHANGE_FILE_NAME);
>
>                         // wait for request to abort watching or for file change event
>                         HANDLE hWaitEvents[] = { m_hEventWatchDone, hFileChanged };
>                         //DWORD dwResult = WaitForMultipleObjects(2, hWaitEvents, FALSE,
> INFINITE);
>                         while (TRUE)
>                         {
>                                 // some devices seem to NOT support FindFirstChangeNotification
>                                 // so we take only the first handle into account then
>                                 dwResult = WaitForMultipleObjects(hFileChanged ==
> INVALID_HANDLE_VALUE ? 1 : 2, hWaitEvents, FALSE, 10);
>
>                                 if (dwResult == WAIT_OBJECT_0 + 0 || dwResult == WAIT_OBJECT_0 +
> 1)
>                                 {
>                                         break;
>                                 }
>                         }
>                         FindCloseChangeNotification(hFileChanged);
>
>                         // some file in the directory changed
>                         if (dwResult == WAIT_OBJECT_0 + 1)
>                         {
>                                 // retrieve docfile's write time
>                                 BOOL bSuccess = file.Open(szPathName, CFile::modeRead |
> CFile::shareDenyNone);
>                                 dwLastError = GetLastError();
>                                 if (!bSuccess && dwLastError == ERROR_FILE_NOT_FOUND)
>                                 {
>                                         // Try it a second time, some network devices report
> ERROR_FILE_NOT_FOUND
>                                         // even if the file was just written.
>                                         bSuccess = file.Open(szPathName, CFile::modeRead |
> CFile::shareDenyNone);
>                                         dwLastError = GetLastError();
>                                 }
>                                 if (bSuccess)
>                                 {
>                                         if (!GetFileInformationByHandle((HANDLE)file.m_hFile, &fileinfo))
>                                         {
>                                                 file.Abort();
>                                         }
>                                         else
>                                         {
>                                                 // detect if changes made
>                                                 BOOL bAlarm = (ftLastWriteTime.dwHighDateTime !=
> fileinfo.ftLastWriteTime.dwHighDateTime ||
>                                                                                 ftLastWriteTime.dwLowDateTime !=
> fileinfo.ftLastWriteTime.dwLowDateTime ||
>                                                                                 IsFileLost());
>
>                                                 // resave last time
>                                                 ftLastWriteTime = fileinfo.ftLastWriteTime;
>                                                 file.Close();
>                                                 SetFileLostFlag(FALSE);
>
>                                                 if (bAlarm)
>                                                 {
>                                                         if (!DoFileAlarm(FA_FILE_WRITTEN, szPathName))
>                                                         {
>                                                                 // leave the outer loop if watching was
>                                                                 // aborted while being in DoFileAlarm.
>                                                                 break;
>                                                         }
>                                                 }
>                                         }
>                                 }
>                                 else if (!IsFileLost() && dwLastError == ERROR_FILE_NOT_FOUND)
>                                 {
>                                         SetFileLostFlag(TRUE);
>                                         if (!DoFileAlarm(FA_FILE_LOST, szPathName))
>                                         {
>                                                 // leave the outer loop if watching was
>                                                 // aborted while being in DoFileAlarm.
>                                                 break;
>                                         }
>                                 }
>                                 else
>                                 {
>                                         TRACE1("FileWatch: An error occured (%d).\n", dwLastError);
>                                 }
>                         }
>                         else
>                                 break;
>                 }
>         }
>         return bRet;
>
> }
>
> BOOL CWatchInThreadBase::DoFileAlarm(int nCause, LPCTSTR szPathName)
> {
>         BOOL bInvert = FALSE;
>
>         // alarm
>         MessageBeep(MB_ICONEXCLAMATION);
>
>         while (TRUE)
>         {
>                 if (WaitForSingleObject(m_hEventWatchDone, 500) == WAIT_OBJECT_0)
>                 {
>                         if (bInvert)
>                                 FlashWindow(m_hwndNotifyFileChanged, FALSE);
>
>                         return FALSE;
>                 }
>                 else if (!IsIconic(m_hwndNotifyFileChanged) &&
>                         IsWindowEnabled(m_hwndNotifyFileChanged))
>                 {
>                         if (bInvert)
>                                 FlashWindow(m_hwndNotifyFileChanged, FALSE);
>
>                         if (GetForegroundWindow() == m_hwndNotifyFileChanged)
>                         {
>                                 // Note: We don't call OnFileAlarm in the watching worker thread.
> In order to
>                                 //  reopen the document it will be necessary to update the views,
> which can only
>                                 //      be done by the creating thread.
> ...
>
> read more »


Thanks for the code and thanks to everyone for all the pointers !!
Great help.
 
Back
Top