-
-
Notifications
You must be signed in to change notification settings - Fork 6
/
windowsWalker.cpp
116 lines (99 loc) · 3.65 KB
/
windowsWalker.cpp
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
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
// SPDX-FileCopyrightText: 2022 Harald Sitter <[email protected]>
#include "windowsWalker.h"
#include <QDebug>
#include <QScopeGuard>
#include <QString>
WindowsWalker::WindowsWalker(const QByteArray &path)
: m_path(path)
, m_pathW(QString::fromUtf8(m_path).toStdWString())
{
if (path.isEmpty()) {
return;
}
const std::wstring filespec = m_pathW + L"\\*";
m_handle = FindFirstFileW(filespec.c_str(), &m_fileinfo);
if (m_handle == INVALID_HANDLE_VALUE) {
const DWORD errorCode = GetLastError();
qWarning() << "Failed to open" << path << ':' << errorCode << GetLastErrorAsString(errorCode);
} else {
if (m_fileinfo.cFileName == std::wstring(L".") || m_fileinfo.cFileName == std::wstring(L"..")) {
// stream past . and ..
next();
} else {
updateEntry();
}
}
}
WindowsWalker::~WindowsWalker()
{
close();
}
void WindowsWalker::close()
{
if (m_handle != INVALID_HANDLE_VALUE) {
FindClose(m_handle);
m_handle = INVALID_HANDLE_VALUE;
}
}
void WindowsWalker::next()
{
while (true) {
m_entry = {}; // reset
if (FindNextFileW(m_handle, &m_fileinfo) == 0) { // error
const DWORD errorCode = GetLastError();
if (errorCode == ERROR_NO_MORE_FILES) {
// qDebug() << "no more files";
close();
return;
} else {
qWarning() << m_path << ':' << errorCode << GetLastErrorAsString(errorCode);
}
} else {
if (m_fileinfo.cFileName == std::wstring(L".") || m_fileinfo.cFileName == std::wstring(L"..")) {
continue;
}
}
updateEntry();
return;
}
}
QString WindowsWalker::GetLastErrorAsString(DWORD error)
{
Q_ASSERT(error != NO_ERROR && error != ERROR_SUCCESS);
LPWSTR buffer = nullptr;
auto freeBuffer = qScopeGuard([buffer] {
LocalFree(buffer);
});
size_t size = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
nullptr,
error,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPWSTR)&buffer,
0,
nullptr);
return QString::fromWCharArray(buffer);
}
void WindowsWalker::updateEntry()
{
m_entry = {}; // reset
const std::wstring name(m_fileinfo.cFileName);
m_entry.name = QString::fromStdWString(name).toUtf8();
auto new_path = m_pathW + L"/" + name;
// https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getfileattributesexw
WIN32_FILE_ATTRIBUTE_DATA fileAttributeData;
BOOL result = GetFileAttributesExW(new_path.c_str(), GetFileExInfoStandard, (LPVOID)&fileAttributeData);
if (result == 0) {
qWarning() << "failed to get attributes for" << QString::fromStdWString(new_path);
return;
}
const auto attributes = fileAttributeData.dwFileAttributes;
// Reparse points are symlinks or NTFS junctions.
m_entry.isSkipable = attributes & FILE_ATTRIBUTE_REPARSE_POINT || attributes & FILE_ATTRIBUTE_TEMPORARY;
m_entry.isDir = attributes & FILE_ATTRIBUTE_DIRECTORY;
m_entry.isFile = !m_entry.isSkipable && !m_entry.isDir; // fileness is implicit in win32 api
ULARGE_INTEGER ulargeInt;
ulargeInt.HighPart = fileAttributeData.nFileSizeHigh;
ulargeInt.LowPart = fileAttributeData.nFileSizeLow;
m_entry.size = ulargeInt.QuadPart;
}