Skip to content

Commit 7c860f1

Browse files
authored
Merge pull request #99 from ianhattendorf/feature/longpaths
Support watching long paths on Windows (path length > 259)
2 parents 8e7e951 + 2afdf0c commit 7c860f1

File tree

3 files changed

+51
-6
lines changed

3 files changed

+51
-6
lines changed

includes/win32/Watcher.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
class Watcher
1515
{
1616
public:
17-
Watcher(std::shared_ptr<EventQueue> queue, HANDLE dirHandle, const std::wstring &path);
17+
Watcher(std::shared_ptr<EventQueue> queue, HANDLE dirHandle, const std::wstring &path, bool pathWasNtPrefixed);
1818
~Watcher();
1919

2020
bool isRunning() const { return mRunning; }
@@ -42,6 +42,7 @@ class Watcher
4242
const std::wstring mPath;
4343
std::shared_ptr<EventQueue> mQueue;
4444
HANDLE mDirectoryHandle;
45+
bool mPathWasNtPrefixed;
4546

4647
std::vector<BYTE> mReadBuffer, mWriteBuffer;
4748
OVERLAPPED mOverlapped;

src/win32/Controller.cpp

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,26 @@
11
#include "../includes/win32/Controller.h"
22

3+
static bool isNtPath(const std::wstring &path) {
4+
return path.rfind(L"\\\\?\\", 0) == 0 || path.rfind(L"\\??\\", 0) == 0;
5+
}
6+
7+
static std::wstring prefixWithNtPath(const std::wstring &path) {
8+
const ULONG widePathLength = GetFullPathNameW(path.c_str(), 0, nullptr, nullptr);
9+
if (widePathLength == 0) {
10+
return path;
11+
}
12+
13+
std::wstring ntPathString;
14+
ntPathString.resize(widePathLength - 1);
15+
if (GetFullPathNameW(path.c_str(), widePathLength, &(ntPathString[0]), nullptr) != widePathLength - 1) {
16+
return path;
17+
}
18+
19+
return ntPathString.rfind(L"\\\\", 0) == 0
20+
? ntPathString.replace(0, 2, L"\\\\?\\UNC\\")
21+
: ntPathString.replace(0, 0, L"\\\\?\\");
22+
}
23+
324
static std::wstring convertMultiByteToWideChar(const std::string &multiByte) {
425
const int wlen = MultiByteToWideChar(CP_UTF8, 0, multiByte.data(), -1, 0, 0);
526

@@ -8,11 +29,13 @@ static std::wstring convertMultiByteToWideChar(const std::string &multiByte) {
829
}
930

1031
std::wstring wideString;
11-
wideString.resize(wlen-1);
32+
wideString.resize(wlen - 1);
33+
1234
int failureToResolveUTF8 = MultiByteToWideChar(CP_UTF8, 0, multiByte.data(), -1, &(wideString[0]), wlen);
1335
if (failureToResolveUTF8 == 0) {
1436
return std::wstring();
1537
}
38+
1639
return wideString;
1740
}
1841

@@ -35,13 +58,18 @@ Controller::Controller(std::shared_ptr<EventQueue> queue, const std::string &pat
3558
: mDirectoryHandle(INVALID_HANDLE_VALUE)
3659
{
3760
auto widePath = convertMultiByteToWideChar(path);
61+
const bool isNt = isNtPath(widePath);
62+
if (!isNt) {
63+
// We convert to an NT Path to support paths > MAX_PATH
64+
widePath = prefixWithNtPath(widePath);
65+
}
3866
mDirectoryHandle = openDirectory(widePath);
3967

4068
if (mDirectoryHandle == INVALID_HANDLE_VALUE) {
4169
return;
4270
}
4371

44-
mWatcher.reset(new Watcher(queue, mDirectoryHandle, widePath));
72+
mWatcher.reset(new Watcher(queue, mDirectoryHandle, widePath, isNt));
4573
}
4674

4775
Controller::~Controller() {
@@ -63,5 +91,5 @@ bool Controller::hasErrored() {
6391
}
6492

6593
bool Controller::isWatching() {
66-
return mWatcher->isRunning();
94+
return !hasErrored() && mWatcher->isRunning();
6795
}

src/win32/Watcher.cpp

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,15 @@
22

33
#include <sstream>
44

5+
static
6+
void stripNTPrefix(std::wstring &path) {
7+
if (path.rfind(L"\\\\?\\UNC\\", 0) != std::wstring::npos) {
8+
path.replace(0, 7, L"\\");
9+
} else if (path.rfind(L"\\\\?\\", 0) != std::wstring::npos) {
10+
path.erase(0, 4);
11+
}
12+
}
13+
514
static
615
std::wstring getWStringFileName(LPWSTR cFileName, DWORD length) {
716
LPWSTR nullTerminatedFileName = new WCHAR[length + 1]();
@@ -24,6 +33,12 @@ std::string Watcher::getUTF8Directory(std::wstring path) {
2433
}
2534

2635
std::wstring uft16DirectoryString = utf16DirectoryStream.str();
36+
if (!mPathWasNtPrefixed) {
37+
// If we were the ones that prefixed the path, we should strip it
38+
// before returning it to the user
39+
stripNTPrefix(uft16DirectoryString);
40+
}
41+
2742
int utf8length = WideCharToMultiByte(
2843
CP_UTF8,
2944
0,
@@ -89,11 +104,12 @@ std::string getUTF8FileName(std::wstring path) {
89104
return utf8Directory;
90105
}
91106

92-
Watcher::Watcher(std::shared_ptr<EventQueue> queue, HANDLE dirHandle, const std::wstring &path)
107+
Watcher::Watcher(std::shared_ptr<EventQueue> queue, HANDLE dirHandle, const std::wstring &path, bool pathWasNtPrefixed)
93108
: mRunning(false),
94109
mDirectoryHandle(dirHandle),
95110
mQueue(queue),
96-
mPath(path)
111+
mPath(path),
112+
mPathWasNtPrefixed(pathWasNtPrefixed)
97113
{
98114
ZeroMemory(&mOverlapped, sizeof(OVERLAPPED));
99115
mOverlapped.hEvent = this;

0 commit comments

Comments
 (0)