/// \file file_change_notify.h
/// Copyright (C) 2009 Grisha Spivak - rusted dreams, distributed under the MIT License
#ifndef file_change_notify_h
#define file_change_notify_h
#include <boost/noncopyable.hpp>
#include <boost/filesystem.hpp>
#include <boost/thread.hpp>
#include <windows.h>
namespace wnd_system
{
// watches for file modification and posts specified windows message to specified window
class file_change_notifier : boost::noncopyable
{
public:
file_change_notifier( boost::filesystem::path const & filepath, HWND hwnd, unsigned message_code );
~file_change_notifier();
private:
HANDLE dir_changed_, wait_end_;
boost::thread waiter_;
};
}
#endif //file_change_notify_h
/// \file file_change_notify.cpp
/// Copyright (C) 2009 Grisha Spivak - rusted dreams, distributed under the MIT License
#include <stdexcept>
#include "file_change_notify.h"
namespace
{
struct wait_thread
{
HANDLE dir_changed_;
HANDLE wait_end_;
boost::filesystem::path filepath_;
std::time_t last_write_;
HWND hwnd_;
unsigned message_code_;
void operator()()
{
HANDLE handles[] = { dir_changed_, wait_end_ };
for( ; ; )
{
switch( WaitForMultipleObjects( 2, handles, false, INFINITE ) )
{
case WAIT_OBJECT_0:
{
Sleep( 50 ); // need to sleep a little to prevent getting old last_write_time
try
{
std::time_t last_write = boost::filesystem::last_write_time( filepath_ );
if( last_write != last_write_ )
{
PostMessageA( hwnd_, message_code_, 0, 0 );
last_write_ = last_write;
}
}
catch( ... )
{
}
FindNextChangeNotification( dir_changed_ );
}
break;
case WAIT_OBJECT_0 + 1:
return;
}
}
}
};
HANDLE create_dir_change_notification( boost::filesystem::path const & filepath )
{
HANDLE h = FindFirstChangeNotificationA(
filepath.parent_path().string().c_str(), false, FILE_NOTIFY_CHANGE_LAST_WRITE );
if( h == INVALID_HANDLE_VALUE )
throw std::runtime_error( "FindFirstChangeNotificationA failed" );
return h;
}
}
wnd_system::file_change_notifier::file_change_notifier(
boost::filesystem::path const & filepath, HWND hwnd, unsigned message_code )
: dir_changed_( create_dir_change_notification( filepath ) ),
wait_end_( CreateEventA( 0, false, false, 0 ) )
{
if( wait_end_ == INVALID_HANDLE_VALUE )
{
FindCloseChangeNotification( dir_changed_ );
throw std::runtime_error( "CreateEventA failed" );
}
try
{
wait_thread waiter =
{
dir_changed_,
wait_end_,
filepath,
boost::filesystem::last_write_time( filepath ),
hwnd,
message_code
};
waiter_ = boost::thread( waiter );
}
catch( ... )
{
CloseHandle( wait_end_ );
FindCloseChangeNotification( dir_changed_ );
throw;
}
}
wnd_system::file_change_notifier::~file_change_notifier()
{
SetEvent( wait_end_ );
waiter_.join();
CloseHandle( wait_end_ );
FindCloseChangeNotification( dir_changed_ );
}