-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathfile_watcher.hpp
More file actions
151 lines (130 loc) · 4.41 KB
/
file_watcher.hpp
File metadata and controls
151 lines (130 loc) · 4.41 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
// Copyright (c) Nicolas VENTER All rights reserved.
#pragma once
typedef enum
{
added = 1,
removed,
modified,
renamed_old,
renamed_new,
} file_watcher_event_type;
static const char* file_watcher_event_type_str[] = {"", "added", "removed", "modified", "renamed_old", "renamed_new"};
#define FILE_WATCHER_FILE_EVENT_PARAM const char *file, file_watcher_event_type event, bool is_directory, void *data
typedef void (*file_watcher_callback)(FILE_WATCHER_FILE_EVENT_PARAM);
#ifdef __cplusplus
extern "C"
{
#endif
// example callback function
void file_watcher_print_file_event(FILE_WATCHER_FILE_EVENT_PARAM);
// blocking function
// NULL can be passed for bWatching and data
void file_watcher_watch_sync(
const char* folder_path, bool b_recursive, file_watcher_callback callback, bool* bWatching, void* data);
// non blocking function, change the value of bWatching to stop the thread
// NULL can be passed for bWatching and data
void file_watcher_watch_async(
const char* folder_path, bool b_recursive, file_watcher_callback callback, bool* bWatching, void* data);
#ifdef __cplusplus
}
#include <set>
#include <string>
class EventFilter
{
public:
bool operator()(const std::string& formattedPath, const file_watcher_event_type filewatchEventType);
private:
std::set<std::string> toIgnoreSet;
};
#ifdef FILE_WATCHER_IMPLEMENTATION
#include "Windows.h"
#include <filesystem>
#include <iostream>
#include <thread>
void file_watcher_print_file_event(FILE_WATCHER_FILE_EVENT_PARAM)
{
std::cout << (is_directory ? "[DIR] " : "[FILE] ") << std::filesystem::path(file).string() << " "
<< file_watcher_event_type_str[event] << std::endl;
}
bool EventFilter::operator()(const std::string& formattedPath, const file_watcher_event_type filewatchEventType)
{
auto it = toIgnoreSet.find(formattedPath);
switch (filewatchEventType)
{
case file_watcher_event_type::removed:
case file_watcher_event_type::renamed_old:
if (it != toIgnoreSet.end()) toIgnoreSet.erase(it);
return true;
case file_watcher_event_type::renamed_new:
return true;
case file_watcher_event_type::modified: // pattern: modified, modified
case file_watcher_event_type::added: // pattern: added, modified
if (it == toIgnoreSet.end())
{
toIgnoreSet.insert(formattedPath);
return true;
}
else
{
toIgnoreSet.erase(it);
return false;
}
default:
return true;
}
}
void file_watcher_watch_sync(
const char* folder_path, bool b_recursive, file_watcher_callback callback, bool* bWatching, void* data)
{
if (!bWatching) bWatching = new bool(true);
*bWatching = true;
static const DWORD timeout = 100000; // 100 seconds timeout
std::string formattedFolderPath = std::filesystem::absolute(folder_path).string();
HANDLE hDirectory = CreateFileA(formattedFolderPath.c_str(),
FILE_LIST_DIRECTORY,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
NULL,
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED,
NULL);
char buffer[4096];
OVERLAPPED overlapped;
overlapped.hEvent = CreateEvent(NULL, FALSE, 0, NULL);
EventFilter eventFilter;
while (*bWatching)
{
ReadDirectoryChangesW(hDirectory,
buffer,
sizeof(buffer),
b_recursive,
FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME | FILE_NOTIFY_CHANGE_LAST_WRITE
| FILE_NOTIFY_CHANGE_CREATION,
NULL,
&overlapped,
NULL);
if (!WaitForSingleObject(overlapped.hEvent, timeout) && *bWatching)
{
DWORD bytesReturned;
GetOverlappedResult(hDirectory, &overlapped, &bytesReturned, FALSE);
FILE_NOTIFY_INFORMATION* info;
DWORD offset = 0;
do
{
info = (FILE_NOTIFY_INFORMATION*)(buffer + offset);
std::wstring fileName(info->FileName, info->FileNameLength / sizeof(wchar_t));
std::string formattedPath = formattedFolderPath + "\\" + std::string(fileName.begin(), fileName.end());
bool is_directory = std::filesystem::is_directory(formattedPath);
if (is_directory || eventFilter(formattedPath, static_cast<file_watcher_event_type>(info->Action)))
callback(formattedPath.c_str(), static_cast<file_watcher_event_type>(info->Action), is_directory, data);
offset += info->NextEntryOffset;
} while (info->NextEntryOffset);
}
}
}
void file_watcher_watch_async(
const char* folder_path, bool b_recursive, file_watcher_callback callback, bool* bWatching, void* data)
{
std::thread(file_watcher_watch_sync, folder_path, b_recursive, callback, bWatching, data).detach();
}
#endif // FILE_WATCHER_IMPLEMENTATION
#endif // __cplusplus