protot/3rdparty/RuntimeCompiledCpp/RuntimeCompiler/SimpleFileWatcher/FileWatcherWin32_AltImpl.h

367 lines
11 KiB
C++

//
// Copyright (c) 2013 Márton Tamás
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
// Minor modifications by Doug Binks
// This implementation for FileWatcherWin32 uses FindFirstChangeNotification rather
// than ReadDirectoryChanges
#ifndef _FileWatcherWin32_AltImpl_h_
#define _FileWatcherWin32_AltImpl_h_
#pragma once
#if FILEWATCHER_PLATFORM == FILEWATCHER_PLATFORM_WIN32
#include <iostream>
#include <vector>
#include <string>
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
namespace FW
{
const static int fw_types[] = { FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_NOTIFY_CHANGE_CREATION, FILE_NOTIFY_CHANGE_SIZE, FILE_NOTIFY_CHANGE_FILE_NAME };
const static size_t NUMTYPES = sizeof(fw_types)/sizeof(int);
class FileWatcherWin32_AltImpl
{
struct filedata
{
std::string fname;
LARGE_INTEGER fsize;
FILETIME ftime;
FILETIME fwritetime;
filedata( std::string n = std::string(), LARGE_INTEGER s = LARGE_INTEGER(), FILETIME t = FILETIME(), FILETIME tw = FILETIME())
{
fname = n;
fsize = s;
ftime = t;
fwritetime = tw;
}
};
std::vector< HANDLE > handles[NUMTYPES];
std::vector< std::vector< filedata > > dir_contents;
void get_dir_contents( const std::string& path, std::vector< filedata >& contents )
{
WIN32_FIND_DATAA fd;
HANDLE dir_lister = FindFirstFileA( (path + "\\*").c_str(), &fd );
if( dir_lister == INVALID_HANDLE_VALUE )
{
std::cout << "Couldn't list files." << std::endl;
return;
}
do
{
if( !(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) )
{
LARGE_INTEGER fsize;
fsize.LowPart = fd.nFileSizeLow;
fsize.HighPart = fd.nFileSizeHigh;
FILETIME ftime;
ftime = fd.ftCreationTime;
FILETIME fwritetime;
fwritetime = fd.ftLastWriteTime;
contents.push_back(filedata( fd.cFileName, fsize, ftime, fwritetime ));
}
}
while(FindNextFileA(dir_lister, &fd) != 0);
FindClose(dir_lister);
}
public:
~FileWatcherWin32_AltImpl()
{
for( int c = 0; c < NUMTYPES; ++c )
{
for( size_t d = 0; d != handles[c].size(); ++d )
{
FindCloseChangeNotification(handles[c][d]);
}
}
}
enum type
{
CHANGE_LAST_WRITE = 0, CHANGE_CREATION, CHANGE_SIZE, CHANGE_FILE_NAME, CHANGE_NONE
};
struct fw_event
{
size_t id;
type ty;
fw_event( size_t i = -1, type t = CHANGE_NONE )
{
id = i;
ty = t;
}
};
size_t add( const std::string& path )
{
size_t id = handles[0].size();
for( int c = 0; c < NUMTYPES; ++c )
{
handles[c].push_back( FindFirstChangeNotificationA( path.c_str(), false, fw_types[c] ) );
}
for( int c = 0; c < NUMTYPES; ++c )
{
if( handles[c][id] == INVALID_HANDLE_VALUE )
{
std::cerr << "Couldn't create handle." << std::endl;
return -1;
}
}
dir_contents.resize(id + 1);
get_dir_contents( path, dir_contents[id] );
return id;
}
bool watch(std::vector<fw_event>& ids)
{
for( int c = 0; c < NUMTYPES; ++c )
{
DWORD status = WaitForMultipleObjects(handles[c].size(), &handles[c][0], false, 0);
for( size_t d = 0; d < handles[c].size(); ++d )
{
if( status == WAIT_OBJECT_0 + d )
{
ids.push_back( fw_event( d, (type)c ) );
if( FindNextChangeNotification( handles[c][d] ) == false )
{
return false;
}
}
}
}
return true;
}
std::string get_event_filename( const std::string& path, size_t id, type ty, DWORD& fni, std::string& old_name )
{
static std::vector< filedata > contents;
contents.clear();
get_dir_contents( path, contents );
switch(ty)
{
//change in file write time
//find the not matching write time
case CHANGE_LAST_WRITE:
{
for( auto c = contents.begin(); c != contents.end(); ++c )
{
for( auto d = dir_contents[id].begin(); d != dir_contents[id].end(); ++d )
{
if( c->fname == d->fname &&
( c->fwritetime.dwLowDateTime != d->fwritetime.dwLowDateTime ||
c->fwritetime.dwHighDateTime != d->fwritetime.dwHighDateTime )
)
{
//make sure we 'neutralize' the event
d->fwritetime.dwLowDateTime = c->fwritetime.dwLowDateTime;
d->fwritetime.dwHighDateTime = c->fwritetime.dwHighDateTime;
fni = FILE_ACTION_MODIFIED;
return d->fname;
}
}
}
break;
}
//change in file creation time
//find the not matching creation time
case CHANGE_CREATION:
{
for( auto c = contents.begin(); c != contents.end(); ++c )
{
for( auto d = dir_contents[id].begin(); d != dir_contents[id].end(); ++d )
{
if( c->fname == d->fname &&
( c->ftime.dwLowDateTime != d->ftime.dwLowDateTime ||
c->ftime.dwHighDateTime != d->ftime.dwHighDateTime )
)
{
//make sure we 'neutralize' the event
d->ftime.dwLowDateTime = c->ftime.dwLowDateTime;
d->ftime.dwHighDateTime = c->ftime.dwHighDateTime;
fni = FILE_ACTION_ADDED;
return d->fname;
}
}
}
break;
}
//change in file name
//find the new filename, and add it
//remove the old filename
case CHANGE_FILE_NAME:
{
std::string filename;
bool file_deleted = contents.size() < dir_contents[id].size();
for( auto c = contents.begin(); c != contents.end(); ++c )
{
bool found = false;
for( auto d = dir_contents[id].begin(); d != dir_contents[id].end(); ++d )
{
if( c->fname == d->fname )
{
found = true;
break;
}
}
if( !found )
{
//this is what we are looking for, the new filename
filename = c->fname;
dir_contents[id].push_back(*c); //add the new file
fni = FILE_ACTION_ADDED;
break;
}
}
for( auto c = dir_contents[id].begin(); c != dir_contents[id].end(); ++c )
{
bool found = false;
for( auto d = contents.begin(); d != contents.end(); ++d )
{
if( c->fname == d->fname )
{
found = true;
break;
}
}
if( !found )
{
//this is the old filename
if( file_deleted ) //file removed
{
filename = c->fname;
fni = FILE_ACTION_REMOVED;
}
else
{
old_name = c->fname;
fni = FILE_ACTION_MODIFIED;
}
dir_contents[id].erase(c);
return filename;
}
}
//in case a new file was created
if( !filename.empty() )
{
fni = FILE_ACTION_ADDED;
return filename;
}
break;
}
//change in file size
//find the not matching file size
case CHANGE_SIZE:
{
for( auto c = contents.begin(); c != contents.end(); ++c )
{
for( auto d = dir_contents[id].begin(); d != dir_contents[id].end(); ++d )
{
if( c->fname == d->fname &&
( c->fsize.LowPart != d->fsize.LowPart ||
c->fsize.HighPart != d->fsize.HighPart )
)
{
//make sure we 'neutralize' the event
d->fsize.LowPart = c->fsize.LowPart;
d->fsize.HighPart = c->fsize.HighPart;
fni = FILE_ACTION_MODIFIED;
return d->fname;
}
}
}
//new file
for( auto c = contents.begin(); c != contents.end(); ++c )
{
bool found = false;
for( auto d = dir_contents[id].begin(); d != dir_contents[id].end(); ++d )
{
if( c->fname == d->fname )
{
found = true;
break;
}
}
if( !found )
{
//this is what we are looking for, the new filename
fni = FILE_ACTION_ADDED;
return c->fname;
}
}
break;
}
default:
break;
}
return "";
}
void remove( size_t id )
{
for( int c = 0; c < NUMTYPES; ++c )
{
auto it = handles[c].begin();
auto it2 = dir_contents.begin();
for( int d = 0; d < id; ++d )
{
++it;
++it2;
};
FindCloseChangeNotification( handles[c][id] );
handles[c].erase(it);
dir_contents.erase(it2);
}
}
};
}
#endif
#endif