RecycleManager.dll:

- Fixed that continuous high CPU usage occurs when recycle bin has a lot of root items
- Removed support for the Drives option
This commit is contained in:
Birunthan Mohanathas 2012-02-13 16:45:11 +00:00
parent a9ee55b194
commit afa1c7cc20
2 changed files with 228 additions and 74 deletions

View File

@ -7,7 +7,7 @@
// //
VS_VERSION_INFO VERSIONINFO VS_VERSION_INFO VERSIONINFO
FILEVERSION 1,0,0,0 FILEVERSION 1,0,1,0
PRODUCTVERSION PRODUCTVER PRODUCTVERSION PRODUCTVER
FILEFLAGSMASK 0x17L FILEFLAGSMASK 0x17L
#ifdef _DEBUG #ifdef _DEBUG
@ -23,7 +23,7 @@ BEGIN
BEGIN BEGIN
BLOCK "040904E4" BLOCK "040904E4"
BEGIN BEGIN
VALUE "FileVersion", "1.0.0.0" VALUE "FileVersion", "1.0.1.0"
VALUE "LegalCopyright", "© 2010 - gschoppe" VALUE "LegalCopyright", "© 2010 - gschoppe"
VALUE "OriginalFilename", "RecycleManager.dll" VALUE "OriginalFilename", "RecycleManager.dll"
VALUE "ProductName", "Rainmeter" VALUE "ProductName", "Rainmeter"

View File

@ -16,32 +16,46 @@
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/ */
#include <windows.h> #define WIN32_LEAN_AND_MEAN
#include <stdlib.h> #include <Windows.h>
#include <stdio.h> #include <ShellAPI.h>
#include "../../Library/RawString.h" #include <ShlObj.h>
#include "../../Library/Export.h" // Rainmeter's exported functions #include <process.h>
#include <vector>
#include "../../Library/Export.h" // Rainmeter's exported functions
#include "../../Library/DisableThreadLibraryCalls.h" // contains DllMain entry point #include "../../Library/DisableThreadLibraryCalls.h" // contains DllMain entry point
// System resources that can be counted
enum MEASURETYPE
{
NUMRECYCLE,
SIZERECYCLE
};
struct MeasureData struct MeasureData
{ {
MEASURETYPE type; bool count;
CRawString drives;
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<BinData> 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) PLUGIN_EXPORT void Initialize(void** data, void* rm)
{ {
MeasureData* measure = new MeasureData; MeasureData* measure = new MeasureData;
*data = measure; *data = measure;
++g_InstanceCount;
} }
PLUGIN_EXPORT void Reload(void* data, void* rm, double* maxValue) 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"); LPCWSTR value = RmReadString(rm, L"RecycleType", L"COUNT");
if (_wcsicmp(L"COUNT", value) == 0) if (_wcsicmp(L"COUNT", value) == 0)
{ {
measure->type = NUMRECYCLE; measure->count = true;
} }
else if (_wcsicmp(L"SIZE", value) == 0) else if (_wcsicmp(L"SIZE", value) == 0)
{ {
measure->type = SIZERECYCLE; measure->count = false;
} }
else 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)); _snwprintf_s(buffer, _TRUNCATE, L"RecycleManager.dll: RecycleType=%s is not valid in [%s]", value, RmGetMeasureName(rm));
RmLog(LOG_ERROR, buffer); 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) PLUGIN_EXPORT double Update(void* data)
{ {
MeasureData* measure = (MeasureData*)data; MeasureData* measure = (MeasureData*)data;
double retVal = 0;
SHQUERYRBINFO rbi = {0}; if (!g_Thread)
rbi.cbSize = sizeof(SHQUERYRBINFO);
if (measure->drives.empty())
{ {
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) buffer[index] = buffer[i];
{ ++index;
retVal = (double)rbi.i64Size; // size in bytes
}
else if (measure->type == NUMRECYCLE)
{
retVal = (double)rbi.i64NumItems; // number of items in bin
}
} }
} buffer[index] = L'\0';
else
{ const WCHAR DRIVE_HANDLED = 1;
WCHAR* drives = _wcsdup(measure->drives.c_str()); bool changed = false;
WCHAR* token = wcstok(drives, L"|"); auto iter = g_BinData.begin();
while (token) 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 FILETIME lastAccess;
} GetFileTime(data.directory, NULL, &lastAccess, NULL);
else if (measure->type == NUMRECYCLE) if (data.lastAccess.dwHighDateTime != lastAccess.dwHighDateTime ||
{ data.lastAccess.dwLowDateTime != lastAccess.dwLowDateTime)
retVal += (double)rbi.i64NumItems; // number of items in bin {
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) PLUGIN_EXPORT void Finalize(void* data)
{ {
MeasureData* measure = (MeasureData*)data; MeasureData* measure = (MeasureData*)data;
delete measure; 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) PLUGIN_EXPORT void ExecuteBang(void* data, LPCWSTR args)
{ {
MeasureData* measure = (MeasureData*)data; 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) if (_wcsicmp(args, L"EmptyBin") == 0)
{ {
emptyBin(0); SHEmptyRecycleBin(NULL, NULL, 0);
} }
else if (_wcsicmp(args, L"EmptyBinSilent") == 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) 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); 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;
}