mirror of
https://github.com/chibicitiberiu/rainmeter-studio.git
synced 2024-02-24 04:33:31 +00:00
1243 lines
33 KiB
C++
1243 lines
33 KiB
C++
/*
|
|
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"
|
|
#include "../../Common/StringUtil.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 GetFolderInfo(std::queue<std::wstring>& folderQueue, std::wstring& folder, ParentMeasure* parent, RecursiveType rType);
|
|
void GetIcon(std::wstring filePath, const std::wstring& iconPath, IconSize iconSize);
|
|
HRESULT SaveIcon(HICON hIcon, FILE* fp);
|
|
|
|
static std::vector<ParentMeasure*> g_ParentMeasures;
|
|
static CRITICAL_SECTION g_CriticalSection;
|
|
static std::string g_SysProperties;
|
|
|
|
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;
|
|
|
|
if (g_SysProperties.empty())
|
|
{
|
|
std::wstring dir = RmReplaceVariables(rm, L"%WINDIR%");
|
|
dir.append(L"\\system32\\control.exe");
|
|
dir.append(IsWindowsVistaOrGreater() ? L" system" : L" sysdm.cpl");
|
|
|
|
g_SysProperties = StringUtil::Narrow(dir);
|
|
}
|
|
}
|
|
|
|
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)
|
|
{
|
|
RmLogF(rm, LOG_ERROR, L"Invalid Path: \"%s\"", path.c_str());
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!child->parent)
|
|
{
|
|
child->parent = new ParentMeasure;
|
|
child->parent->skin = skin;
|
|
child->parent->name = RmGetMeasureName(rm);
|
|
child->parent->ownerChild = child;
|
|
child->parent->hwnd = RmGetSkinWindow(rm);
|
|
child->parent->rm = rm;
|
|
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->thread && 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 < (int)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 < (int)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;
|
|
}
|
|
|
|
auto runFile = [&](std::wstring fileName, std::wstring dir, bool isProperty) -> void
|
|
{
|
|
// Display computer system properties
|
|
if (isProperty && dir.empty() && fileName.empty())
|
|
{
|
|
WinExec(g_SysProperties.c_str(), SW_SHOWNORMAL);
|
|
return;
|
|
}
|
|
|
|
CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
|
|
|
|
std::wstring file = dir + fileName;
|
|
SHELLEXECUTEINFO si = {sizeof(SHELLEXECUTEINFO)};
|
|
|
|
si.lpVerb = isProperty ? L"properties" : nullptr;
|
|
si.lpFile = file.c_str();
|
|
si.nShow = SW_SHOWNORMAL;
|
|
si.lpDirectory = dir.c_str();
|
|
si.fMask = SEE_MASK_FLAG_NO_UI | SEE_MASK_ASYNCOK;
|
|
|
|
if (isProperty) si.fMask |= SEE_MASK_INVOKEIDLIST;
|
|
|
|
ShellExecuteEx(&si);
|
|
CoUninitialize();
|
|
};
|
|
|
|
// Parent only commands
|
|
if (parent->ownerChild == child)
|
|
{
|
|
if ((int)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)) < (int)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) < (int)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 (_wcsicmp(args, L"CONTEXTMENU") == 0)
|
|
{
|
|
if (!ShowContextMenu(parent->hwnd, parent->path))
|
|
{
|
|
RmLogF(parent->rm, LOG_ERROR, L"Cannot open context menu for \"%s\"", parent->path.c_str());
|
|
}
|
|
}
|
|
else if (_wcsicmp(args, L"PROPERTIES") == 0)
|
|
{
|
|
runFile(L"", parent->path, true);
|
|
}
|
|
else if (parent->recursiveType != RECURSIVE_FULL && _wcsicmp(args, L"PREVIOUSFOLDER") == 0)
|
|
{
|
|
GetParentFolder(parent->path);
|
|
|
|
parent->indexOffset = 0;
|
|
parent->needsUpdating = true;
|
|
parent->needsIcons = true;
|
|
}
|
|
else
|
|
{
|
|
// Special commands that allow for a user defined file/folder
|
|
std::wstring arg = args;
|
|
std::wstring::size_type pos = arg.find_first_of(L' ');
|
|
if (pos != std::wstring::npos)
|
|
{
|
|
arg = arg.substr(pos);
|
|
if (!arg.empty())
|
|
{
|
|
arg.erase(0, 1); // Skip the space
|
|
|
|
if (_wcsnicmp(args, L"CONTEXTMENU", 11) == 0)
|
|
{
|
|
if (!ShowContextMenu(parent->hwnd, arg))
|
|
{
|
|
RmLogF(parent->rm, LOG_ERROR, L"Cannot open context menu for \"%s\"", arg.c_str());
|
|
}
|
|
}
|
|
else if (_wcsnicmp(args, L"PROPERTIES", 10) == 0)
|
|
{
|
|
runFile(arg, L"", true);
|
|
}
|
|
else
|
|
{
|
|
RmLogF(parent->rm, LOG_WARNING, L"!CommandMeasure: Unknown path: %s", arg.c_str());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
RmLogF(parent->rm, LOG_WARNING, L"!CommandMeasure: Unknown command: %s", args);
|
|
}
|
|
}
|
|
}
|
|
|
|
LeaveCriticalSection(&g_CriticalSection);
|
|
return;
|
|
}
|
|
|
|
// Child only commands
|
|
int trueIndex = child->ignoreCount ? child->index : ((child->index % parent->count) + parent->indexOffset);
|
|
if (!parent->files.empty() && trueIndex >= 0 && trueIndex < (int)parent->files.size())
|
|
{
|
|
if (_wcsicmp(args, L"OPEN") == 0)
|
|
{
|
|
runFile(parent->files[trueIndex].fileName, parent->files[trueIndex].path, false);
|
|
}
|
|
else if (_wcsicmp(args, L"CONTEXTMENU") == 0)
|
|
{
|
|
std::wstring path = parent->files[trueIndex].path;
|
|
std::wstring fileName = parent->files[trueIndex].fileName;
|
|
|
|
if (_wcsicmp(fileName.c_str(), L"..") == 0)
|
|
{
|
|
path = parent->path;
|
|
GetParentFolder(path);
|
|
fileName = L"";
|
|
}
|
|
|
|
path.append(fileName);
|
|
|
|
if (!ShowContextMenu(parent->hwnd, path))
|
|
{
|
|
RmLogF(parent->rm, LOG_ERROR, L"Cannot open context menu for \"%s\"", path.c_str());
|
|
}
|
|
}
|
|
else if (_wcsicmp(args, L"PROPERTIES") == 0)
|
|
{
|
|
std::wstring path = parent->files[trueIndex].path;
|
|
std::wstring fileName = parent->files[trueIndex].fileName;
|
|
|
|
if (_wcsicmp(fileName.c_str(), L"..") == 0)
|
|
{
|
|
path = parent->path;
|
|
GetParentFolder(path);
|
|
fileName = L"";
|
|
}
|
|
|
|
runFile(fileName, path, true);
|
|
}
|
|
else if (parent->recursiveType != RECURSIVE_FULL && _wcsicmp(args, L"FOLLOWPATH") == 0)
|
|
{
|
|
if (_wcsicmp(parent->files[trueIndex].fileName.c_str(), L"..") == 0)
|
|
{
|
|
GetParentFolder(parent->path);
|
|
|
|
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
|
|
{
|
|
runFile(parent->files[trueIndex].fileName, parent->files[trueIndex].path, false);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
RmLogF(parent->rm, LOG_WARNING, L"!CommandMeasure: Unknown command: %s", args);
|
|
}
|
|
|
|
LeaveCriticalSection(&g_CriticalSection);
|
|
return;
|
|
}
|
|
|
|
LeaveCriticalSection(&g_CriticalSection);
|
|
RmLogF(parent->rm, LOG_WARNING, L"!CommandMeasure: Unknown command: %s", args);
|
|
}
|
|
|
|
PLUGIN_EXPORT void Finalize(void* data)
|
|
{
|
|
ChildMeasure* child = (ChildMeasure*)data;
|
|
ParentMeasure* parent = child->parent;
|
|
|
|
EnterCriticalSection(&g_CriticalSection);
|
|
if (parent && parent->ownerChild == child)
|
|
{
|
|
if (parent->thread)
|
|
{
|
|
TerminateThread(parent->thread, 0);
|
|
parent->thread = nullptr;
|
|
}
|
|
|
|
auto iter = std::find(g_ParentMeasures.begin(), g_ParentMeasures.end(), parent);
|
|
g_ParentMeasures.erase(iter);
|
|
|
|
delete parent;
|
|
}
|
|
|
|
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.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<std::wstring> folderQueue;
|
|
std::wstring folder = tmp->path;
|
|
|
|
RecursiveType rType = tmp->recursiveType;
|
|
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.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 < (int)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 GetFolderInfo(std::queue<std::wstring>& 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)
|
|
{
|
|
if (!PathMatchSpec(file.fileName.c_str(), parent->wildcardSearch.c_str()))
|
|
{
|
|
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, const 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;
|
|
} |