/* Copyright (C) 2012 Brian Ferguson This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "PluginFileView.h" #define MAX_LINE_LENGTH 4096 #define INVALID_FILE L"/<>\\" #pragma pack(push, 2) typedef struct // 16 bytes { BYTE bWidth; // Width, in pixels, of the image BYTE bHeight; // Height, in pixels, of the image BYTE bColorCount; // Number of colors in image (0 if >=8bpp) BYTE bReserved; // Reserved ( must be 0) WORD wPlanes; // Color Planes WORD wBitCount; // Bits per pixel DWORD dwBytesInRes; // How many bytes in this resource? DWORD dwImageOffset; // Where in the file is this image? } ICONDIRENTRY, *LPICONDIRENTRY; typedef struct // 22 bytes { WORD idReserved; // Reserved (must be 0) WORD idType; // Resource Type (1 for icons) WORD idCount; // How many images? ICONDIRENTRY idEntries[1]; // An entry for each image (idCount of 'em) } ICONDIR, *LPICONDIR; #pragma pack(pop) unsigned __stdcall SystemThreadProc(void* pParam); void EscapeRegex(std::wstring& regex); void GetFolderInfo(std::queue& folderQueue, std::wstring& folder, ParentMeasure* parent, RecursiveType rType); void GetIcon(std::wstring filePath, std::wstring iconPath, IconSize iconSize); HRESULT SaveIcon(HICON hIcon, FILE* fp); std::vector g_ParentMeasures; static CRITICAL_SECTION g_CriticalSection; BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) { switch (fdwReason) { case DLL_PROCESS_ATTACH: InitializeCriticalSection(&g_CriticalSection); // Disable DLL_THREAD_ATTACH and DLL_THREAD_DETACH notification calls. DisableThreadLibraryCalls(hinstDLL); break; case DLL_PROCESS_DETACH: DeleteCriticalSection(&g_CriticalSection); break; } return TRUE; } PLUGIN_EXPORT void Initialize(void** data, void* rm) { ChildMeasure* child = new ChildMeasure; *data = child; } PLUGIN_EXPORT void Reload(void* data, void* rm, double* maxValue) { ChildMeasure* child = (ChildMeasure*)data; void* skin = RmGetSkin(rm); std::wstring path = RmReadString(rm, L"Path", L"", FALSE); if (path[0] == L'[' && path[path.size() - 1] == L']') { path = path.substr(1, path.size() - 2); for (auto iter : g_ParentMeasures) { if (_wcsicmp(iter->name, path.c_str()) == 0 && iter->skin == skin) { child->parent = iter; break; } } if (!child->parent) { RmLog(LOG_ERROR, L"FileView.dll: Invalid Path"); return; } } else { if (!child->parent) { child->parent = new ParentMeasure; child->parent->skin = skin; child->parent->name = RmGetMeasureName(rm); child->parent->ownerChild = child; g_ParentMeasures.push_back(child->parent); } // Add trailing "\" if none exists if (!path.empty() && path[path.size() - 1] != L'\\') { path += L'\\'; } child->parent->path = path; LPCWSTR sort = RmReadString(rm, L"SortType", L"Name"); if (_wcsicmp(sort, L"NAME") == 0) { child->parent->sortType = STYPE_NAME; } else if (_wcsicmp(sort, L"SIZE") == 0) { child->parent->sortType = STYPE_SIZE; } else if (_wcsicmp(sort, L"TYPE") == 0) { child->parent->sortType = STYPE_TYPE; } else if (_wcsicmp(sort, L"DATE") == 0) { child->parent->sortType = STYPE_DATE; LPCWSTR date = RmReadString(rm, L"SortDateType", L"Modified"); if (_wcsicmp(date, L"MODIFIED") == 0) { child->parent->sortDateType = DTYPE_MODIFIED; } else if (_wcsicmp(date, L"CREATED") == 0) { child->parent->sortDateType = DTYPE_CREATED; } else if(_wcsicmp(date, L"ACCESSED") == 0) { child->parent->sortDateType = DTYPE_ACCESSED; } } int count = RmReadInt(rm, L"Count", 1); child->parent->count = count > 0 ? count : 1; int recursive = RmReadInt(rm, L"Recursive", 0); switch (recursive) { default: RmLog(LOG_WARNING, L"Invalid Recursive type"); case 0: child->parent->recursiveType = RECURSIVE_NONE; break; case 1: child->parent->recursiveType = RECURSIVE_PARTIAL; break; case 2: child->parent->recursiveType = RECURSIVE_FULL; break; } child->parent->sortAscending = 0!=RmReadInt(rm, L"SortAscending", 1); child->parent->showDotDot = 0!=RmReadInt(rm, L"ShowDotDot", 1); child->parent->showFile = 0!=RmReadInt(rm, L"ShowFile", 1); child->parent->showFolder = 0!=RmReadInt(rm, L"ShowFolder", 1); child->parent->showHidden = 0!=RmReadInt(rm, L"ShowHidden", 1); child->parent->showSystem = 0!=RmReadInt(rm, L"ShowSystem", 0); child->parent->hideExtension = 0!=RmReadInt(rm, L"HideExtensions", 0); child->parent->extensions = Tokenize(RmReadString(rm, L"Extensions", L""), L";"); child->parent->wildcardSearch = RmReadString(rm, L"WildcardSearch", L"*"); child->parent->finishAction = RmReadString(rm, L"FinishAction", L"", false); } int index = RmReadInt(rm, L"Index", 1) - 1; child->index = index >= 0 ? index : 1; child->ignoreCount = 0!=RmReadInt(rm, L"IgnoreCount", 0); LPCWSTR type = RmReadString(rm, L"Type", L"FOLDERPATH"); if (_wcsicmp(type, L"FOLDERPATH") == 0) { child->type = TYPE_FOLDERPATH; } else if (_wcsicmp(type, L"FOLDERSIZE") == 0) { child->type = TYPE_FOLDERSIZE; } else if (_wcsicmp(type, L"FILECOUNT") == 0) { child->type = TYPE_FILECOUNT; } else if (_wcsicmp(type, L"FOLDERCOUNT") == 0) { child->type = TYPE_FOLDERCOUNT; } else if (_wcsicmp(type, L"FILENAME") == 0) { child->type = TYPE_FILENAME; } else if (_wcsicmp(type, L"FILETYPE") == 0) { child->type = TYPE_FILETYPE; } else if (_wcsicmp(type, L"FILESIZE") == 0) { child->type = TYPE_FILESIZE; } else if (_wcsicmp(type, L"FILEDATE") == 0) { child->type = TYPE_FILEDATE; LPCWSTR date = RmReadString(rm, L"DateType", L"Modified"); if (_wcsicmp(date, L"MODIFIED") == 0) { child->date = DTYPE_MODIFIED; } else if (_wcsicmp(date, L"CREATED") == 0) { child->date = DTYPE_CREATED; } else if(_wcsicmp(date, L"ACCESSED") == 0) { child->date = DTYPE_ACCESSED; } } else if (_wcsicmp(type, L"ICON") == 0) { child->type = TYPE_ICON; std::wstring temp = L"icon"; WCHAR buffer[MAX_PATH]; _itow_s(child->index + 1, buffer, 10); temp += buffer; temp += L".ico"; child->iconPath = RmReadPath(rm, L"IconPath", temp.c_str()); LPCWSTR size = RmReadString(rm, L"IconSize", L"MEDIUM"); if (_wcsicmp(size, L"SMALL") == 0) { child->iconSize = IS_SMALL; } else if (_wcsicmp(size, L"MEDIUM") == 0) { child->iconSize = IS_MEDIUM; } else if (_wcsicmp(size, L"LARGE") == 0) { child->iconSize = IS_LARGE; } else if (_wcsicmp(size, L"EXTRALARGE") == 0) { child->iconSize = IS_EXLARGE; } auto iter = std::find(child->parent->iconChildren.begin(), child->parent->iconChildren.end(), child); if (iter == child->parent->iconChildren.end()) { child->parent->iconChildren.push_back(child); } } else if (_wcsicmp(type, L"FILEPATH") == 0) { child->type = TYPE_FILEPATH; } } PLUGIN_EXPORT double Update(void* data) { ChildMeasure* child = (ChildMeasure*)data; ParentMeasure* parent = child->parent; if (!parent) { return 0.0; } EnterCriticalSection(&g_CriticalSection); if (parent->ownerChild == child && (parent->needsUpdating || parent->needsIcons)) { unsigned int id; HANDLE thread = (HANDLE)_beginthreadex(nullptr, 0, SystemThreadProc, parent, 0, &id); if (thread) { parent->thread = thread; } } int trueIndex = child->ignoreCount ? child->index : ((child->index % parent->count) + parent->indexOffset); double value = 0; if (!parent->files.empty() && trueIndex >= 0 && trueIndex < parent->files.size()) { switch (child->type) { case TYPE_FILESIZE: value = parent->files[trueIndex].size > 0 ? (double)parent->files[trueIndex].size : 0; break; case TYPE_FILEDATE: { FILETIME fTime; SYSTEMTIME stUTC, stLOCAL; ULARGE_INTEGER time; switch (child->date) { case DTYPE_MODIFIED: fTime = parent->files[trueIndex].modifiedTime; break; case DTYPE_CREATED: fTime = parent->files[trueIndex].createdTime; break; case DTYPE_ACCESSED: fTime = parent->files[trueIndex].accessedTime; break; } FileTimeToSystemTime(&fTime, &stUTC); SystemTimeToTzSpecificLocalTime(nullptr, &stUTC, &stLOCAL); SystemTimeToFileTime(&stLOCAL, &fTime); time.LowPart = fTime.dwLowDateTime; time.HighPart = fTime.dwHighDateTime; value = (double)(time.QuadPart / 10000000); } break; } } switch (child->type) { case TYPE_FILECOUNT: value = (double)parent->fileCount; break; case TYPE_FOLDERCOUNT: value = (double)parent->folderCount; break; case TYPE_FOLDERSIZE: value = (double)parent->folderSize; break; } LeaveCriticalSection(&g_CriticalSection); return value; } PLUGIN_EXPORT LPCWSTR GetString(void* data) { ChildMeasure* child = (ChildMeasure*)data; ParentMeasure* parent = child->parent; EnterCriticalSection(&g_CriticalSection); if (!parent) { LeaveCriticalSection(&g_CriticalSection); return L""; } int trueIndex = child->ignoreCount ? child->index : ((child->index % parent->count) + parent->indexOffset); child->strValue = L""; if (!parent->files.empty() && trueIndex >= 0 && trueIndex < parent->files.size()) { switch (child->type) { case TYPE_FILESIZE: if (!parent->files[trueIndex].isFolder) { LeaveCriticalSection(&g_CriticalSection); return nullptr; // Force a numeric return (see the Update function) } break; case TYPE_FILENAME: { std::wstring temp = parent->files[trueIndex].fileName; if (parent->hideExtension && !parent->files[trueIndex].isFolder) { size_t pos = temp.find_last_of(L"."); if (pos != temp.npos) { child->strValue = temp.substr(0, pos); } } else { child->strValue = temp; } } break; case TYPE_FILETYPE: child->strValue = parent->files[trueIndex].ext; break; case TYPE_FILEDATE: { WCHAR* temp = new WCHAR[MAX_LINE_LENGTH]; SYSTEMTIME stUTC, stLOCAL; FILETIME fTime; switch (child->date) { case DTYPE_MODIFIED: fTime = parent->files[trueIndex].modifiedTime; break; case DTYPE_CREATED: fTime = parent->files[trueIndex].createdTime; break; case DTYPE_ACCESSED: fTime = parent->files[trueIndex].accessedTime; break; } if (fTime.dwLowDateTime != 0 && fTime.dwHighDateTime != 0) { FileTimeToSystemTime(&fTime, &stUTC); SystemTimeToTzSpecificLocalTime(nullptr, &stUTC, &stLOCAL); GetDateFormat(LOCALE_USER_DEFAULT, 0, &stLOCAL, nullptr, temp, MAX_LINE_LENGTH); child->strValue = temp; child->strValue += L" "; GetTimeFormat(LOCALE_USER_DEFAULT, 0, &stLOCAL, nullptr, temp, MAX_LINE_LENGTH); child->strValue += temp; } else { child->strValue = L""; } delete [] temp; } break; case TYPE_ICON: child->strValue = child->iconPath; break; case TYPE_FILEPATH: child->strValue = (_wcsicmp(parent->files[trueIndex].fileName.c_str(), L"..") == 0) ? parent->path : parent->files[trueIndex].path + parent->files[trueIndex].fileName; break; } } switch (child->type) { case TYPE_FILECOUNT: case TYPE_FOLDERCOUNT: case TYPE_FOLDERSIZE: LeaveCriticalSection(&g_CriticalSection); return nullptr; // Force numeric return (see the Update function) break; case TYPE_FOLDERPATH: child->strValue = parent->path; break; } LeaveCriticalSection(&g_CriticalSection); return child->strValue.c_str(); } PLUGIN_EXPORT void ExecuteBang(void* data, LPCWSTR args) { ChildMeasure* child = (ChildMeasure*)data; ParentMeasure* parent = child->parent; EnterCriticalSection(&g_CriticalSection); if (!parent || parent->thread) { LeaveCriticalSection(&g_CriticalSection); return; } if (parent->ownerChild == child) { if (parent->files.size() > parent->count) { if (_wcsicmp(args, L"PAGEUP") == 0) { if ((parent->indexOffset - parent->count) >= 0) { parent->indexOffset -= parent->count; parent->needsIcons = true; } else { parent->indexOffset = 0; parent->needsIcons = true; } } else if (_wcsicmp(args, L"PAGEDOWN") == 0) { if ((parent->indexOffset + ( 2 * parent->count)) < parent->files.size()) { parent->indexOffset += parent->count; parent->needsIcons = true; } else { parent->indexOffset = (int)parent->files.size() - parent->count; parent->needsIcons = true; } } else if (_wcsicmp(args, L"INDEXUP") ==0) { if ((parent->indexOffset - 1) >= 0) { --parent->indexOffset; parent->needsIcons = true; } else { parent->indexOffset = 0; parent->needsIcons = true; } } else if (_wcsicmp(args, L"INDEXDOWN") == 0) { if ((parent->indexOffset + parent->count) < parent->files.size()) { ++parent->indexOffset; parent->needsIcons = true; } else { parent->indexOffset = (int)parent->files.size() - parent->count; parent->needsIcons = true; } } } if (_wcsicmp(args, L"UPDATE") == 0) { parent->indexOffset = 0; parent->needsIcons = true; parent->needsUpdating = true; } else if (parent->recursiveType != RECURSIVE_FULL && _wcsicmp(args, L"PREVIOUSFOLDER") == 0) { std::vector path = Tokenize(parent->path, L"\\"); if (path.size() < 2) { parent->path.clear(); } else { parent->path.clear(); for (int i = 0; i < path.size() - 1; ++i) { parent->path += path[i]; parent->path += L"\\"; } } parent->indexOffset = 0; parent->needsUpdating = true; parent->needsIcons = true; } LeaveCriticalSection(&g_CriticalSection); return; } int trueIndex = child->ignoreCount ? child->index : ((child->index % parent->count) + parent->indexOffset); if (!parent->files.empty() && trueIndex >= 0 && trueIndex < parent->files.size()) { if (_wcsicmp(args, L"OPEN") == 0) { std::wstring file = parent->files[trueIndex].path + parent->files[trueIndex].fileName; ShellExecute(nullptr, nullptr, file.c_str(), nullptr, parent->files[trueIndex].path.c_str(), SW_SHOW); } else if (parent->recursiveType != RECURSIVE_FULL && _wcsicmp(args, L"FOLLOWPATH") == 0) { if (_wcsicmp(parent->files[trueIndex].fileName.c_str(), L"..") == 0) { std::vector path = Tokenize(parent->path, L"\\"); if (path.size() < 2) { parent->path.clear(); } else { parent->path.clear(); for (int i = 0; i < path.size() - 1; ++i) { parent->path += path[i]; parent->path += L"\\"; } } parent->indexOffset = 0; parent->needsUpdating = true; parent->needsIcons = true; } else if (parent->files[trueIndex].isFolder) { parent->path += parent->files[trueIndex].fileName; if (parent->path[parent->path.size() - 1] != L'\\') { parent->path += L'\\'; } parent->indexOffset = 0; parent->needsUpdating = true; parent->needsIcons = true; } else { std::wstring file = parent->path + parent->files[trueIndex].fileName; ShellExecute(nullptr, nullptr, file.c_str(), nullptr, parent->path.c_str(), SW_SHOW); } } LeaveCriticalSection(&g_CriticalSection); return; } LeaveCriticalSection(&g_CriticalSection); RmLog(LOG_ERROR, L"FileView.dll: Invalid command"); } PLUGIN_EXPORT void Finalize(void* data) { ChildMeasure* child = (ChildMeasure*)data; ParentMeasure* parent = child->parent; EnterCriticalSection(&g_CriticalSection); if (parent->thread) { TerminateThread(parent->thread, 0); parent->thread = nullptr; } if (parent && parent->ownerChild == child) { CloseHandle(parent->thread); parent->thread = nullptr; delete parent; auto iter = std::find(g_ParentMeasures.begin(), g_ParentMeasures.end(), parent); g_ParentMeasures.erase(iter); } delete child; LeaveCriticalSection(&g_CriticalSection); } unsigned __stdcall SystemThreadProc(void* pParam) { CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE); ParentMeasure* parent = (ParentMeasure*)pParam; EnterCriticalSection(&g_CriticalSection); ParentMeasure* tmp = new ParentMeasure (*parent); parent->needsUpdating = false; // Set to false here in case skin is reloaded parent->needsIcons = false; // Set to false here in case skin is reloaded LeaveCriticalSection(&g_CriticalSection); FileInfo file; if (tmp->needsUpdating) { EnterCriticalSection(&g_CriticalSection); parent->files.clear(); parent->fileCount = 0; parent->folderCount = 0; parent->folderSize = 0; LeaveCriticalSection(&g_CriticalSection); tmp->files.clear(); tmp->fileCount = 0; tmp->folderCount = 0; tmp->folderSize = 0; // If no path is specified, get all the drives instead if (tmp->path == L"" || tmp->path.empty()) { WCHAR drive[4] = L" :\\"; DWORD driveMask = GetLogicalDrives(); for (int i = 0; i < 32; ++i) { if ((driveMask << (31 - i) >> 31) > 0) { drive[0] = i + 'A'; file.fileName = drive; file.isFolder = true; file.size = 0; ++tmp->folderCount; tmp->files.push_back(file); } } } else { if (tmp->showDotDot && tmp->recursiveType != RECURSIVE_FULL) { file.fileName = L".."; file.isFolder = true; tmp->files.push_back(file); } std::queue folderQueue; std::wstring folder = tmp->path; RecursiveType rType = tmp->recursiveType; if (rType == RECURSIVE_FULL && tmp->wildcardSearch != L"*") { EscapeRegex(tmp->wildcardSearch); } GetFolderInfo(folderQueue, folder, tmp, (rType == RECURSIVE_PARTIAL) ? RECURSIVE_NONE : rType); while (rType != RECURSIVE_NONE && !folderQueue.empty()) { folder = folderQueue.front(); GetFolderInfo(folderQueue, folder, tmp, rType); folderQueue.pop(); } } // Sort const int sortAsc = tmp->sortAscending ? 1 : -1; auto begin = ((tmp->path != L"" || !tmp->path.empty()) && (tmp->showDotDot && tmp->recursiveType != RECURSIVE_FULL)) ? tmp->files.begin() + 1: tmp->files.begin(); switch (tmp->sortType) { case STYPE_NAME: std::sort(begin, tmp->files.end(), [&sortAsc](const FileInfo& file1, const FileInfo& file2) -> bool { if (file1.isFolder && file2.isFolder) { return (sortAsc * _wcsicmp(file1.fileName.c_str(), file2.fileName.c_str()) < 0); } else if (!file1.isFolder && !file2.isFolder) { return (sortAsc * _wcsicmp(file1.fileName.c_str(), file2.fileName.c_str()) < 0); } return file1.isFolder; }); break; case STYPE_SIZE: std::sort(begin, tmp->files.end(), [&sortAsc](const FileInfo& file1, const FileInfo& file2) -> bool { if (file1.isFolder && file2.isFolder) { return (sortAsc * _wcsicmp(file1.fileName.c_str(), file2.fileName.c_str()) < 0); } else if (!file1.isFolder && !file2.isFolder) { return (sortAsc > 0) ? (file1.size < file2.size) : (file1.size > file2.size); } return file1.isFolder; }); break; case STYPE_TYPE: std::sort(begin, tmp->files.end(), [&sortAsc](const FileInfo& file1, const FileInfo& file2) -> bool { if (file1.isFolder && file2.isFolder) { return (sortAsc * _wcsicmp(file1.fileName.c_str(), file2.fileName.c_str()) < 0); } else if (!file1.isFolder && !file2.isFolder) { int result = (file1.ext.empty() && file2.ext.empty()) ? 0 : sortAsc * _wcsicmp(file1.ext.c_str(), file2.ext.c_str()); return (0 != result) ? (result < 0) : (sortAsc * _wcsicmp(file1.fileName.c_str(), file2.fileName.c_str()) < 0); } return file1.isFolder; }); break; case STYPE_DATE: switch (tmp->sortDateType) { case DTYPE_MODIFIED: std::sort(begin, tmp->files.end(), [&sortAsc](const FileInfo& file1, const FileInfo& file2) -> bool { if (file1.isFolder && file2.isFolder) { return (sortAsc * CompareFileTime(&file1.modifiedTime, &file2.modifiedTime) < 0); } else if (!file1.isFolder && !file2.isFolder) { return (sortAsc * CompareFileTime(&file1.modifiedTime, &file2.modifiedTime) < 0); } return file1.isFolder; }); break; case DTYPE_CREATED: std::sort(begin, tmp->files.end(), [&sortAsc](const FileInfo& file1, const FileInfo& file2) -> bool { if (file1.isFolder && file2.isFolder) { return (sortAsc * CompareFileTime(&file1.createdTime, &file2.createdTime) < 0); } else if (!file1.isFolder && !file2.isFolder) { return (sortAsc * CompareFileTime(&file1.createdTime, &file2.createdTime) < 0); } return file1.isFolder; }); break; case DTYPE_ACCESSED: std::sort(begin, tmp->files.end(), [&sortAsc](const FileInfo& file1, const FileInfo& file2) -> bool { if (file1.isFolder && file2.isFolder) { return (sortAsc * CompareFileTime(&file1.accessedTime, &file2.accessedTime) < 0); } else if (!file1.isFolder && !file2.isFolder) { return (sortAsc * CompareFileTime(&file1.accessedTime, &file2.accessedTime) < 0); } return file1.isFolder; }); break; } break; } EnterCriticalSection(&g_CriticalSection); parent->files = tmp->files; parent->files.shrink_to_fit(); parent->fileCount = tmp->fileCount; parent->folderCount = tmp->folderCount; parent->folderSize = tmp->folderSize; LeaveCriticalSection(&g_CriticalSection); } if (tmp->needsIcons) { for (auto iter : tmp->iconChildren) { EnterCriticalSection(&g_CriticalSection); int trueIndex = iter->ignoreCount ? iter->index : ((iter->index % iter->parent->count) + iter->parent->indexOffset); if (iter->type == TYPE_ICON && trueIndex >= 0 && trueIndex < tmp->files.size()) { std::wstring filePath = tmp->files[trueIndex].path; filePath += (tmp->files[trueIndex].fileName == L"..") ? L"" :tmp->files[trueIndex].fileName; GetIcon(filePath, iter->iconPath, iter->iconSize); } else if (iter->type == TYPE_ICON) { GetIcon(INVALID_FILE, iter->iconPath, iter->iconSize); } LeaveCriticalSection(&g_CriticalSection); } } EnterCriticalSection(&g_CriticalSection); CloseHandle(parent->thread); parent->thread = nullptr; LeaveCriticalSection(&g_CriticalSection); if (!tmp->finishAction.empty()) { RmExecute(tmp->skin, tmp->finishAction.c_str()); } delete tmp; CoUninitialize(); return 0; } void EscapeRegex(std::wstring& regex) { auto replace = [®ex](std::wstring from, std::wstring to) { size_t start = 0; while ((start = regex.find(from, start)) != std::wstring::npos) { regex.replace(start, from.size(), to); start += to.size(); } }; replace(L"\\", L"\\\\"); replace(L"^", L"\\^"); replace(L"$", L"\\$"); replace(L"|", L"\\|"); replace(L"(", L"\\("); replace(L")", L"\\)"); replace(L"[", L"\\["); replace(L"]", L"\\]"); replace(L".", L"\\."); replace(L"+", L"\\+"); replace(L"/", L"\\/"); replace(L"&", L"\\&"); replace(L"{", L"\\{"); replace(L"}", L"\\}"); replace(L"*", L".*"); replace(L"?", L"."); } void GetFolderInfo(std::queue& folderQueue, std::wstring& folder, ParentMeasure* parent, RecursiveType rType) { std::wstring path = folder; folder += (rType == RECURSIVE_NONE) ? parent->wildcardSearch : L"*"; WIN32_FIND_DATA fd; HANDLE find = FindFirstFileEx(folder.c_str(), FindExInfoStandard, &fd, FindExSearchNameMatch, nullptr, 0); if (find != INVALID_HANDLE_VALUE) { do { FileInfo file; file.fileName = fd.cFileName; if (_wcsicmp(file.fileName.c_str(), L".") == 0 || _wcsicmp(file.fileName.c_str(), L"..") == 0) { continue; } file.isFolder = (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) > 0; bool isHidden = (fd.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) > 0; bool isSystem = (fd.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM) > 0; if (rType == RECURSIVE_FULL && parent->wildcardSearch != L"*" && !file.isFolder) { std::wregex pattern(parent->wildcardSearch, std::wregex::icase | std::wregex::optimize); if (!std::regex_match(file.fileName, pattern)) { continue; } } if ((rType != RECURSIVE_PARTIAL) && ((rType != RECURSIVE_FULL && !parent->showFile && !file.isFolder) || (rType != RECURSIVE_FULL && !parent->showFolder && file.isFolder) || (!parent->showHidden && isHidden) ||(!parent->showSystem && isSystem))) { continue; } if (rType != RECURSIVE_PARTIAL && !file.isFolder) { size_t pos = file.fileName.find_last_of(L"."); if (pos != std::wstring::npos) { file.ext = file.fileName.substr(pos + 1); if (parent->extensions.size() > 0) { bool found = false; for (auto iter : parent->extensions) { if (_wcsicmp(iter.c_str(), file.ext.c_str()) == 0) { found = true; break; } } if (!found) { continue; } } } else if (parent->extensions.size() > 0) { continue; } } if (file.isFolder) { if (rType != RECURSIVE_FULL) { ++parent->folderCount; } folderQueue.push(path + file.fileName + L"\\"); } else { ++parent->fileCount; file.size = ((UINT64)fd.nFileSizeHigh << 32) + fd.nFileSizeLow; } parent->folderSize += file.size; file.createdTime = fd.ftCreationTime; file.modifiedTime = fd.ftLastWriteTime; file.accessedTime = fd.ftLastAccessTime; file.path = path; if (rType == RECURSIVE_NONE || (rType == RECURSIVE_FULL && !file.isFolder)) { parent->files.push_back(file); } } while (FindNextFile(find, &fd)); FindClose(find); } } void GetIcon(std::wstring filePath, std::wstring iconPath, IconSize iconSize) { SHFILEINFO shFileInfo; HICON icon = nullptr; HIMAGELIST* hImageList = nullptr; FILE* fp = nullptr; // Special case for .url files if (filePath.size() > 3 && _wcsicmp(filePath.substr(filePath.size() - 4).c_str(), L".URL") == 0) { WCHAR buffer[MAX_PATH] = L""; GetPrivateProfileString(L"InternetShortcut", L"IconFile", L"", buffer, sizeof(buffer), filePath.c_str()); if (*buffer) { std::wstring file = buffer; int iconIndex = 0; GetPrivateProfileString(L"InternetShortcut", L"IconIndex", L"-1", buffer, sizeof(buffer), filePath.c_str()); if (buffer != L"-1") { iconIndex = _wtoi(buffer); } int size = 16; switch (iconSize) { case IS_EXLARGE: size = 256; break; case IS_LARGE: size = 48; break; case IS_MEDIUM: size = 32; break; } PrivateExtractIcons(file.c_str(), iconIndex, size, size, &icon, nullptr, 1, LR_LOADTRANSPARENT); } else { std::wstring browser; WCHAR buffer[MAX_PATH]; DWORD size = MAX_PATH; HKEY hKey; RegOpenKeyEx(HKEY_CLASSES_ROOT, L"http\\shell\\open\\command", 0, KEY_QUERY_VALUE, &hKey); RegQueryValueEx(hKey, nullptr, nullptr, nullptr, (LPBYTE)buffer, &size); RegCloseKey(hKey); //Strip quotes if (buffer[0] == L'"') { browser = buffer; browser = browser.substr(1); size_t pos = browser.find_first_of(L'"'); browser = browser.substr(0, pos); } filePath = browser; } } if (icon == nullptr) { SHGetFileInfo(filePath.c_str(), 0, &shFileInfo, sizeof(shFileInfo), SHGFI_SYSICONINDEX); SHGetImageList(iconSize, IID_IImageList, (void**) &hImageList); ((IImageList*)hImageList)->GetIcon(shFileInfo.iIcon, ILD_TRANSPARENT, &icon); } errno_t error = _wfopen_s(&fp, iconPath.c_str(), L"wb"); if (filePath == INVALID_FILE || icon == nullptr || (error == 0 && !SaveIcon(icon, fp))) { fwrite(iconPath.c_str(), 1, 1, fp); // Clears previous icon fclose(fp); } DestroyIcon(icon); } HRESULT SaveIcon(HICON hIcon, FILE* fp) { ICONINFO iconInfo; BITMAP bmColor; BITMAP bmMask; if (!fp || nullptr == hIcon || !GetIconInfo(hIcon, &iconInfo) || !GetObject(iconInfo.hbmColor, sizeof(bmColor), &bmColor) || !GetObject(iconInfo.hbmMask, sizeof(bmMask), &bmMask)) return false; // support only 16/32 bit icon now if (bmColor.bmBitsPixel != 16 && bmColor.bmBitsPixel != 32) return false; HDC dc = GetDC(nullptr); BYTE bmiBytes[sizeof(BITMAPINFOHEADER) + 256 * sizeof(RGBQUAD)]; BITMAPINFO* bmi = (BITMAPINFO*)bmiBytes; // color bits memset(bmi, 0, sizeof(BITMAPINFO)); bmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); GetDIBits(dc, iconInfo.hbmColor, 0, bmColor.bmHeight, nullptr, bmi, DIB_RGB_COLORS); int colorBytesCount = bmi->bmiHeader.biSizeImage; BYTE* colorBits = new BYTE[colorBytesCount]; GetDIBits(dc, iconInfo.hbmColor, 0, bmColor.bmHeight, colorBits, bmi, DIB_RGB_COLORS); // mask bits memset(bmi, 0, sizeof(BITMAPINFO)); bmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); GetDIBits(dc, iconInfo.hbmMask, 0, bmMask.bmHeight, nullptr, bmi, DIB_RGB_COLORS); int maskBytesCount = bmi->bmiHeader.biSizeImage; BYTE* maskBits = new BYTE[maskBytesCount]; GetDIBits(dc, iconInfo.hbmMask, 0, bmMask.bmHeight, maskBits, bmi, DIB_RGB_COLORS); ReleaseDC(nullptr, dc); // icon data BITMAPINFOHEADER bmihIcon; memset(&bmihIcon, 0, sizeof(bmihIcon)); bmihIcon.biSize = sizeof(BITMAPINFOHEADER); bmihIcon.biWidth = bmColor.bmWidth; bmihIcon.biHeight = bmColor.bmHeight * 2; // icXOR + icAND bmihIcon.biPlanes = bmColor.bmPlanes; bmihIcon.biBitCount = bmColor.bmBitsPixel; bmihIcon.biSizeImage = colorBytesCount + maskBytesCount; // icon header ICONDIR dir; dir.idReserved = 0; // must be 0 dir.idType = 1; // 1 for icons dir.idCount = 1; dir.idEntries[0].bWidth = (BYTE)bmColor.bmWidth; dir.idEntries[0].bHeight = (BYTE)bmColor.bmHeight; dir.idEntries[0].bColorCount = 0; // 0 if >= 8bpp dir.idEntries[0].bReserved = 0; // must be 0 dir.idEntries[0].wPlanes = bmColor.bmPlanes; dir.idEntries[0].wBitCount = bmColor.bmBitsPixel; dir.idEntries[0].dwBytesInRes = sizeof(bmihIcon) + bmihIcon.biSizeImage; dir.idEntries[0].dwImageOffset = sizeof(ICONDIR); fwrite(&dir, sizeof(dir), 1, fp); fwrite(&bmihIcon, sizeof(bmihIcon), 1, fp); fwrite(colorBits, colorBytesCount, 1, fp); fwrite(maskBits, maskBytesCount, 1, fp); // Clean up DeleteObject(iconInfo.hbmColor); DeleteObject(iconInfo.hbmMask); delete[] colorBits; delete[] maskBits; fclose(fp); return true; }