Skip to content

Instantly share code, notes, and snippets.

@yuyoyuppe
Created July 10, 2020 22:30
Show Gist options
  • Select an option

  • Save yuyoyuppe/f86aa1fbb118162dc98c25741598cb42 to your computer and use it in GitHub Desktop.

Select an option

Save yuyoyuppe/f86aa1fbb118162dc98c25741598cb42 to your computer and use it in GitHub Desktop.
#include <Windows.h>
#include <optional>
#include <Shlwapi.h>
#include <string_view>
#include <functional>
#include <atomic>
namespace timeutil
{
inline time_t from_filetime(const FILETIME& ft)
{
ULARGE_INTEGER ull;
ull.LowPart = ft.dwLowDateTime;
ull.HighPart = ft.dwHighDateTime;
return ull.QuadPart / 10000000ULL - 11644473600ULL;
}
}
struct FileWatchCancellationToken
{
std::atomic_bool cancelled = false;
};
std::optional<time_t> GetFileLastWriteTime(std::wstring_view filepath)
{
auto handle = CreateFileW(filepath.data(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
if (FAILED(GetLastError()))
{
return std::nullopt;
}
FILETIME lastWrite;
GetFileTime(handle, nullptr, nullptr, &lastWrite);
if (FAILED(GetLastError()))
{
return std::nullopt;
}
CloseHandle(handle);
return timeutil::from_filetime(lastWrite);
}
void WatchFileChanges(std::wstring_view filepath, std::function<void()> callback, FileWatchCancellationToken& cancelToken)
{
std::wstring fileToWatch{ filepath };
std::wstring directoryToWatch{ fileToWatch };
PathRemoveFileSpecW(directoryToWatch.data());
std::optional<time_t> currentWriteTime = GetFileLastWriteTime(fileToWatch);
HANDLE watcherHandle = FindFirstChangeNotificationW(
directoryToWatch.c_str(),
false,
FILE_NOTIFY_CHANGE_LAST_WRITE);
while (!cancelToken.cancelled)
{
constexpr DWORD waitPeriod = 5000;
if (WaitForSingleObject(watcherHandle, waitPeriod) == WAIT_OBJECT_0)
{
std::optional<time_t> refreshedWriteTime = GetFileLastWriteTime(fileToWatch);
const bool firstWritetime = !currentWriteTime && refreshedWriteTime;
const bool writeTimeChanged = currentWriteTime && refreshedWriteTime && *currentWriteTime != *refreshedWriteTime;
if (writeTimeChanged || firstWritetime)
{
callback();
if (refreshedWriteTime)
{
currentWriteTime = refreshedWriteTime;
}
}
FindNextChangeNotification(watcherHandle);
}
}
FindCloseChangeNotification(watcherHandle);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment