forked from MrBeanCpp/Dr-Folder
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathutils.cpp
More file actions
184 lines (155 loc) · 6.78 KB
/
utils.cpp
File metadata and controls
184 lines (155 loc) · 6.78 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
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
#include "utils.h"
#include <QDebug>
#include <QFile>
#include <shlobj.h>
#include <shlwapi.h>
#include <QDirIterator>
#include <QtWinExtras>
#include <QFileIconProvider>
// On NTFS file systems, ownership and permissions checking is disabled by default for performance reasons
extern Q_CORE_EXPORT int qt_ntfs_permission_lookup;
bool Util::isFolderWirtable(const QString& path)
{
// Permission checking is then turned on and off by incrementing and decrementing qt_ntfs_permission_lookup by 1
qt_ntfs_permission_lookup++;
bool res = QFileInfo(path).isWritable(); // 与程序是否为管理员权限有关
qt_ntfs_permission_lookup--;
return res;
}
bool Util::setFolderIcon(const QString &folderPath, const QString &iconPath, int index)
{
if (!isFolderWirtable(folderPath)) {
qWarning() << "No write permission";
return false;
}
SHFOLDERCUSTOMSETTINGS fcs = {0}; // 初始化所有成员为0
fcs.dwSize = sizeof(SHFOLDERCUSTOMSETTINGS);
fcs.dwMask = FCSM_ICONFILE;
auto iconWStr = QDir::toNativeSeparators(iconPath).toStdWString(); // IMPORTANT: 不能写为 iconPath.toStdWString().c_str(),因为返回的是临时对象,导致指针无效
fcs.pszIconFile = LPWSTR(iconWStr.c_str());
fcs.cchIconFile = 0;
fcs.iIconIndex = index;
// 这里返回临时对象指针没事,因为语句没结束不会被释放
HRESULT hr = SHGetSetFolderCustomSettings(&fcs, QDir::toNativeSeparators(folderPath).toStdWString().c_str(), FCS_FORCEWRITE);
// 即便没有写入权限,这个API也会返回 0,但是实际上并没有写入,靠
if (FAILED(hr)) {
qWarning() << "Failed to set folder icon";
return false;
}
return true;
}
QStringList Util::getExeFiles(const QString& dirPath) {
QStringList res;
QDir dir(dirPath);
if (!dir.exists()) return res;
dir.setNameFilters(QStringList() << "*.exe");
QStringList files = dir.entryList(QDir::Files | QDir::NoDotAndDotDot);
// 若根目录没有exe文件 & 只有一个子目录,则进入子目录查找exe文件 (只进一层)
if (files.isEmpty()) {
auto dirs = dir.entryList(QDir::AllDirs | QDir::NoDotAndDotDot); // AllDirs 不受到 nameFilters 影响
if (dirs.size() == 1) {
dir.cd(dirs[0]);
files = dir.entryList(QDir::Files | QDir::NoDotAndDotDot);
}
}
// 获取绝对路径
for (const auto& name : qAsConst(files)) {
if (name.startsWith("unins", Qt::CaseInsensitive) || name.startsWith("卸载")) continue; // 忽略卸载程序
res.push_back(dir.absoluteFilePath(name));
}
// 再搜索一下 bin 目录, 应该不会有bin/bin/bin吧,递归没事
if (dir.cd("bin"))
res.append(getExeFiles(dir.absolutePath()));
return res;
}
// 重置文件夹图标
bool Util::restoreFolderIcon(const QString& folderPath)
{
QFile iniFile(folderPath + "/desktop.ini");
if (!iniFile.exists()) return true;
// delete desktop.ini
iniFile.remove();
// remove attrib
// 神奇,folderPath是左斜杠也没事
return PathUnmakeSystemFolder(folderPath.toStdWString().c_str());
// 很奇怪 设置图标的时候 PathMakeSystemFolder 不会刷新图标,但是Unmake会刷新
// 而且 Unmake不会删除 desktop.ini
}
// 直接读取 desktop.ini 的话遇到编码问题
QString Util::getFolderIconPath(const QString& folderPath) {
SHFILEINFO shFileInfo;
ZeroMemory(&shFileInfo, sizeof(SHFILEINFO));
// Convert QString to std::wstring
// 必须要把分隔符转为windows的分隔符,否则会出现找不到文件的情况
std::wstring folderPathW = QDir::toNativeSeparators(folderPath).toStdWString();
// Get the icon location for the folder
if (SHGetFileInfo(folderPathW.c_str(), 0, &shFileInfo, sizeof(SHFILEINFO), SHGFI_ICONLOCATION)) {
return QString::fromWCharArray(shFileInfo.szDisplayName);
} else {
return QString();
}
}
// 在没有缓存的情况下(重启),无论是什么方法,都不可避免要读取icon,都很慢
bool Util::hasCustomIcon(const QString& exePath)
{
HMODULE hModule = LoadLibraryEx(exePath.toStdWString().c_str(), NULL, LOAD_LIBRARY_AS_DATAFILE);
if (hModule == NULL) {
qWarning() << "Failed to load library:" << GetLastError() << exePath;
return false;
}
bool hasIcon = false;
auto enumProc = [](HMODULE hModule, LPCWSTR lpType, LPWSTR lpName, LONG_PTR lParam) -> BOOL {
Q_UNUSED(hModule);
Q_UNUSED(lpType);
Q_UNUSED(lpName);
bool* result = (bool*)(lParam);
*result = true;
return FALSE; // Stop enumeration after finding the first icon
};
// 也可以通过返回值判断,但是enumProc不能返回FALSE
EnumResourceNames(hModule, RT_GROUP_ICON, enumProc, LONG_PTR(&hasIcon));
FreeLibrary(hModule);
return hasIcon;
}
// slow
bool Util::isUsingDefaultIcon(const QString& exePath)
{
UINT iconCount = ExtractIconEx(exePath.toStdWString().c_str(), -1, NULL, NULL, 0);
return iconCount == 0;
}
// slow
bool Util::isDefaultExeIcon(const QIcon& icon) {
static const QSize IconSize(16, 16);
static auto defaultIcon = []() -> QIcon {
QFileIconProvider iconProvider;
return iconProvider.icon(QFileInfo("invalid_path_mrbeanc.exe"));
}().pixmap(IconSize).toImage();
return icon.pixmap(IconSize).toImage() == defaultIcon;
}
// QFileIconProvider::icon会缓存,导致无法更新图标,故采用 SHGetFileInfo
// 但是QFileIconProvider::icon优化更好,速度更快,所以只在必要的地方手动调用 SHGetFileInfo
// 其实QFileIconProvider::icon().pixmap()也是调用SHGetFileInfo
// .icon()其实并没有获取到图像,只是传入了IconEngine,真正获取图像是在.pixmap()时
// 更快的原因可能是采用了多线程获取pixmap,或者分区域绘制
// 通过源码可以发现,QFileIconProvider只会缓存Folder图标,不缓存文件图标
// 缓存方式为:QPixmapCache,所以调用clear()静态方法就可以清空
QIcon Util::getFileIcon(QString filePath) {
filePath = QDir::toNativeSeparators(filePath); // IMPORTANT: 否则会找不到文件
CoInitialize(NULL); // important for SHGetFileInfo,否则失败
SHFILEINFO sfi;
memset(&sfi, 0, sizeof(SHFILEINFO));
QIcon icon;
if (SHGetFileInfo(filePath.toStdWString().c_str(), 0, &sfi, sizeof(SHFILEINFO), SHGFI_ICON)) {
icon = QtWin::fromHICON(sfi.hIcon);
DestroyIcon(sfi.hIcon);
}
CoUninitialize();
return icon;
}
// 对于绝对路径,QDir::exists() 不可靠(for判断是否inDir)
bool Util::isInDir(const QString& filePath, const QString& dirPath)
{
auto _filePath = QDir::toNativeSeparators(filePath);
auto _dirPath = QDir::toNativeSeparators(dirPath);
return _filePath.startsWith(_dirPath);
}