diff --git a/Plugins/PluginRecycleManager/PluginRecycleManager.rc b/Plugins/PluginRecycleManager/PluginRecycleManager.rc index 5bb043f2..630cfcde 100644 --- a/Plugins/PluginRecycleManager/PluginRecycleManager.rc +++ b/Plugins/PluginRecycleManager/PluginRecycleManager.rc @@ -7,7 +7,7 @@ // VS_VERSION_INFO VERSIONINFO - FILEVERSION 1,0,0,0 + FILEVERSION 1,0,1,0 PRODUCTVERSION PRODUCTVER FILEFLAGSMASK 0x17L #ifdef _DEBUG @@ -23,7 +23,7 @@ BEGIN BEGIN BLOCK "040904E4" BEGIN - VALUE "FileVersion", "1.0.0.0" + VALUE "FileVersion", "1.0.1.0" VALUE "LegalCopyright", "© 2010 - gschoppe" VALUE "OriginalFilename", "RecycleManager.dll" VALUE "ProductName", "Rainmeter" diff --git a/Plugins/PluginRecycleManager/RecycleManager.cpp b/Plugins/PluginRecycleManager/RecycleManager.cpp index 1edcd511..e636d47e 100644 --- a/Plugins/PluginRecycleManager/RecycleManager.cpp +++ b/Plugins/PluginRecycleManager/RecycleManager.cpp @@ -16,32 +16,46 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#include -#include -#include -#include "../../Library/RawString.h" -#include "../../Library/Export.h" // Rainmeter's exported functions +#define WIN32_LEAN_AND_MEAN +#include +#include +#include +#include +#include +#include "../../Library/Export.h" // Rainmeter's exported functions #include "../../Library/DisableThreadLibraryCalls.h" // contains DllMain entry point -// System resources that can be counted -enum MEASURETYPE -{ - NUMRECYCLE, - SIZERECYCLE -}; - struct MeasureData { - MEASURETYPE type; - CRawString drives; + bool count; - MeasureData() : type(NUMRECYCLE) {} + MeasureData() : count(false) {} }; +struct BinData +{ + FILETIME lastAccess; + HANDLE directory; + WCHAR drive; +}; + +unsigned int __stdcall QueryRecycleBinThreadProc(void* pParam); +HRESULT GetFolderCLSID(LPCWSTR pszPath, CLSID* pathCLSID); +HANDLE GetRecycleBinHandle(WCHAR drive); + +std::vector g_BinData; +double g_BinCount = 0; +double g_BinSize = 0; + +UINT g_InstanceCount = 0; +HANDLE g_Thread = NULL; + PLUGIN_EXPORT void Initialize(void** data, void* rm) { MeasureData* measure = new MeasureData; *data = measure; + + ++g_InstanceCount; } PLUGIN_EXPORT void Reload(void* data, void* rm, double* maxValue) @@ -51,11 +65,11 @@ PLUGIN_EXPORT void Reload(void* data, void* rm, double* maxValue) LPCWSTR value = RmReadString(rm, L"RecycleType", L"COUNT"); if (_wcsicmp(L"COUNT", value) == 0) { - measure->type = NUMRECYCLE; + measure->count = true; } else if (_wcsicmp(L"SIZE", value) == 0) { - measure->type = SIZERECYCLE; + measure->count = false; } else { @@ -63,99 +77,239 @@ PLUGIN_EXPORT void Reload(void* data, void* rm, double* maxValue) _snwprintf_s(buffer, _TRUNCATE, L"RecycleManager.dll: RecycleType=%s is not valid in [%s]", value, RmGetMeasureName(rm)); RmLog(LOG_ERROR, buffer); } - - value = RmReadString(rm, L"Drives", L"ALL"); - measure->drives = (_wcsicmp(value, L"ALL") == 0) ? NULL : value; } PLUGIN_EXPORT double Update(void* data) { MeasureData* measure = (MeasureData*)data; - double retVal = 0; - SHQUERYRBINFO rbi = {0}; - rbi.cbSize = sizeof(SHQUERYRBINFO); - - if (measure->drives.empty()) + if (!g_Thread) { - if (SHQueryRecycleBin(NULL, &rbi) == S_OK) + WCHAR buffer[128]; + DWORD len = GetLogicalDriveStrings(128, buffer); + + // Convert "A:\\\0B:\\\0" -> "AB\0" + int index = 0; + for (int i = 0; i < len; i += 4) { - if (measure->type == SIZERECYCLE) - { - retVal = (double)rbi.i64Size; // size in bytes - } - else if (measure->type == NUMRECYCLE) - { - retVal = (double)rbi.i64NumItems; // number of items in bin - } + buffer[index] = buffer[i]; + ++index; } - } - else - { - WCHAR* drives = _wcsdup(measure->drives.c_str()); - WCHAR* token = wcstok(drives, L"|"); - while (token) + buffer[index] = L'\0'; + + const WCHAR DRIVE_HANDLED = 1; + bool changed = false; + auto iter = g_BinData.begin(); + while (iter != g_BinData.end()) { - if (SHQueryRecycleBin(token, &rbi) == S_OK) + BinData& data = (*iter); + + WCHAR* pos = wcschr(buffer, data.drive); + if (pos != NULL) { - if (measure->type == SIZERECYCLE) + *pos = DRIVE_HANDLED; + ++iter; + + if (data.lastAccess.dwHighDateTime != 0xFFFFFFFF && + data.lastAccess.dwLowDateTime != 0xFFFFFFFF) { - retVal += (double)rbi.i64Size; // size in bytes - } - else if (measure->type == NUMRECYCLE) - { - retVal += (double)rbi.i64NumItems; // number of items in bin + FILETIME lastAccess; + GetFileTime(data.directory, NULL, &lastAccess, NULL); + if (data.lastAccess.dwHighDateTime != lastAccess.dwHighDateTime || + data.lastAccess.dwLowDateTime != lastAccess.dwLowDateTime) + { + data.lastAccess.dwHighDateTime = lastAccess.dwHighDateTime; + data.lastAccess.dwLowDateTime = lastAccess.dwLowDateTime; + changed = true; + } } } + else + { + // Drive removed + if (data.directory) + { + CloseHandle(data.directory); + } - token = wcstok(NULL, L"|"); + iter = g_BinData.erase(iter); + } } - free(drives); + + for (int i = 0; i < index; ++i) + { + if (buffer[i] != DRIVE_HANDLED) + { + // New drive + BinData data = {0}; + data.directory = GetRecycleBinHandle(buffer[i]); + data.drive = buffer[i]; + data.lastAccess.dwHighDateTime = + data.lastAccess.dwLowDateTime = data.directory ? 0 : 0xFFFFFFFF; + + g_BinData.push_back(data); + } + } + + if (changed) + { + g_Thread = (HANDLE)_beginthreadex(NULL, 64 * 1024, QueryRecycleBinThreadProc, NULL, 0, NULL); + } + } - return retVal; + return measure->count ? g_BinCount : g_BinSize; } PLUGIN_EXPORT void Finalize(void* data) { MeasureData* measure = (MeasureData*)data; delete measure; + + --g_InstanceCount; + if (g_InstanceCount == 0) + { + for (auto iter = g_BinData.cbegin(); iter != g_BinData.cend(); ++iter) + { + if ((*iter).directory) + { + CloseHandle((*iter).directory); + } + } + + WaitForSingleObject(g_Thread, INFINITE); + } } PLUGIN_EXPORT void ExecuteBang(void* data, LPCWSTR args) { MeasureData* measure = (MeasureData*)data; - auto emptyBin = [&](DWORD flags) - { - if (measure->drives.empty()) - { - SHEmptyRecycleBin(NULL, NULL, flags); - } - else - { - WCHAR* drives = _wcsdup(measure->drives.c_str()); - WCHAR* token = wcstok(drives, L"|"); - while (token) - { - SHEmptyRecycleBin(NULL, token, flags); - token = wcstok(NULL, L"|"); - } - free(drives); - } - }; - if (_wcsicmp(args, L"EmptyBin") == 0) { - emptyBin(0); + SHEmptyRecycleBin(NULL, NULL, 0); } else if (_wcsicmp(args, L"EmptyBinSilent") == 0) { - emptyBin(SHERB_NOCONFIRMATION | SHERB_NOPROGRESSUI | SHERB_NOSOUND); + SHEmptyRecycleBin(NULL, NULL, SHERB_NOCONFIRMATION | SHERB_NOPROGRESSUI | SHERB_NOSOUND); } else if (_wcsicmp(args, L"OpenBin") == 0) { - // Open the Recycle Bin folder ShellExecute(NULL, L"open", L"explorer.exe", L"/N,::{645FF040-5081-101B-9F08-00AA002F954E}", NULL, SW_SHOW); } } + +unsigned int __stdcall QueryRecycleBinThreadProc(void* pParam) +{ + SHQUERYRBINFO rbi = {0}; + rbi.cbSize = sizeof(SHQUERYRBINFO); + SHQueryRecycleBin(NULL, &rbi); + g_BinCount = (double)rbi.i64NumItems; + g_BinSize = (double)rbi.i64Size; + + g_Thread = NULL; + + _endthreadex(0); + return 0; +} + +HRESULT GetFolderCLSID(LPCWSTR path, CLSID* clsid) +{ + LPITEMIDLIST pidl; + HRESULT hr = SHParseDisplayName(path, NULL, &pidl, 0, NULL); + if (SUCCEEDED(hr)) + { + IShellFolder* sf; + LPCITEMIDLIST pidlLast; + hr = SHBindToParent(pidl, IID_IShellFolder, (void**)&sf, &pidlLast); + if (SUCCEEDED(hr)) + { + SHDESCRIPTIONID did; + hr = SHGetDataFromIDList(sf, pidlLast, SHGDFIL_DESCRIPTIONID, &did, sizeof(did)); + *clsid = did.clsid; + + sf->Release(); + } + + CoTaskMemFree(pidl); + } + return hr; +} + +HANDLE GetRecycleBinHandle(WCHAR drive) +{ + WCHAR search[] = L"\0:\\*"; + search[0] = drive; + + WIN32_FIND_DATA fd; + HANDLE hSearch = FindFirstFile(search, &fd); + if (hSearch != INVALID_HANDLE_VALUE) + { + bool found = false; + std::wstring path; + + do + { + if ((fd.dwFileAttributes & + (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_DIRECTORY)) == 0) + { + continue; + } + + WCHAR buffer[MAX_PATH]; + int len = _snwprintf_s(buffer, _TRUNCATE, L"%c:\\%s\\*", drive, fd.cFileName); + + // Find the SID-specific child directory + HANDLE hChildSearch = FindFirstFile(buffer, &fd); + if (hChildSearch != INVALID_HANDLE_VALUE) + { + do + { + if (!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) || (fd.cFileName[0] == L'.')) + { + continue; + } + + path.assign(buffer, len - 1); + path += fd.cFileName; + + CLSID id; + HRESULT hr = GetFolderCLSID(path.c_str(), &id); + if (SUCCEEDED(hr) && IsEqualGUID(CLSID_RecycleBin, id)) + { + found = true; + break; + } + } + while (FindNextFile(hChildSearch, &fd)); + + FindClose(hChildSearch); + + if (found) + { + // Break out of main loop + break; + } + } + } + while (FindNextFile(hSearch, &fd)); + + FindClose(hSearch); + + if (found) + { + HANDLE hDir = CreateFile( + path.c_str(), + GENERIC_READ, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, + OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS, + NULL); + + return hDir; + } + } + + return NULL; +}