mirror of
https://github.com/chibicitiberiu/rainmeter-studio.git
synced 2024-02-24 04:33:31 +00:00
1407 lines
35 KiB
C++
1407 lines
35 KiB
C++
/*
|
|
Copyright (C) 2010 spx
|
|
|
|
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 "StdAfx.h"
|
|
#include "System.h"
|
|
#include "Litestep.h"
|
|
#include "Rainmeter.h"
|
|
#include "MeterWindow.h"
|
|
#include "MeasureNet.h"
|
|
#include "Error.h"
|
|
#include "../Common/PathUtil.h"
|
|
|
|
using namespace Gdiplus;
|
|
|
|
#define DEBUG_VERBOSE (0) // Set 1 if you need verbose logging.
|
|
|
|
#define ZPOS_FLAGS (SWP_NOMOVE | SWP_NOSIZE | SWP_NOOWNERZORDER | SWP_NOACTIVATE | SWP_NOSENDCHANGING)
|
|
|
|
enum TIMER
|
|
{
|
|
TIMER_SHOWDESKTOP = 1,
|
|
TIMER_RESUME = 2
|
|
};
|
|
enum INTERVAL
|
|
{
|
|
INTERVAL_SHOWDESKTOP = 250,
|
|
INTERVAL_RESTOREWINDOWS = 100,
|
|
INTERVAL_RESUME = 1000
|
|
};
|
|
|
|
MultiMonitorInfo System::c_Monitors = { 0 };
|
|
|
|
HWND System::c_Window = nullptr;
|
|
HWND System::c_HelperWindow = nullptr;
|
|
|
|
HWINEVENTHOOK System::c_WinEventHook = nullptr;
|
|
|
|
bool System::c_ShowDesktop = false;
|
|
|
|
std::wstring System::c_WorkingDirectory;
|
|
|
|
std::vector<std::wstring> System::c_IniFileMappings;
|
|
|
|
/*
|
|
** Creates a helper window to detect changes in the system.
|
|
**
|
|
*/
|
|
void System::Initialize(HINSTANCE instance)
|
|
{
|
|
WNDCLASS wc = {0};
|
|
wc.lpfnWndProc = (WNDPROC)WndProc;
|
|
wc.hInstance = instance;
|
|
wc.lpszClassName = L"RainmeterSystem";
|
|
ATOM className = RegisterClass(&wc);
|
|
|
|
c_Window = CreateWindowEx(
|
|
WS_EX_TOOLWINDOW,
|
|
MAKEINTATOM(className),
|
|
L"System",
|
|
WS_POPUP | WS_DISABLED,
|
|
CW_USEDEFAULT,
|
|
CW_USEDEFAULT,
|
|
CW_USEDEFAULT,
|
|
CW_USEDEFAULT,
|
|
nullptr,
|
|
nullptr,
|
|
instance,
|
|
nullptr);
|
|
|
|
c_HelperWindow = CreateWindowEx(
|
|
WS_EX_TOOLWINDOW,
|
|
MAKEINTATOM(className),
|
|
L"PositioningHelper",
|
|
WS_POPUP | WS_DISABLED,
|
|
CW_USEDEFAULT,
|
|
CW_USEDEFAULT,
|
|
CW_USEDEFAULT,
|
|
CW_USEDEFAULT,
|
|
nullptr,
|
|
nullptr,
|
|
instance,
|
|
nullptr);
|
|
|
|
SetWindowPos(c_Window, HWND_BOTTOM, 0, 0, 0, 0, ZPOS_FLAGS);
|
|
SetWindowPos(c_HelperWindow, HWND_BOTTOM, 0, 0, 0, 0, ZPOS_FLAGS);
|
|
|
|
c_Monitors.monitors.reserve(4);
|
|
SetMultiMonitorInfo();
|
|
|
|
WCHAR directory[MAX_PATH];
|
|
DWORD len = GetCurrentDirectory(MAX_PATH, directory);
|
|
c_WorkingDirectory.assign(directory, len <= MAX_PATH ? len : 0);
|
|
|
|
c_WinEventHook = SetWinEventHook(
|
|
EVENT_SYSTEM_FOREGROUND,
|
|
EVENT_SYSTEM_FOREGROUND,
|
|
nullptr,
|
|
MyWinEventProc,
|
|
0,
|
|
0,
|
|
WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNPROCESS);
|
|
|
|
SetTimer(c_Window, TIMER_SHOWDESKTOP, INTERVAL_SHOWDESKTOP, nullptr);
|
|
}
|
|
|
|
/*
|
|
** Destroys a window.
|
|
**
|
|
*/
|
|
void System::Finalize()
|
|
{
|
|
KillTimer(c_Window, TIMER_SHOWDESKTOP);
|
|
KillTimer(c_Window, TIMER_RESUME);
|
|
|
|
if (c_WinEventHook)
|
|
{
|
|
UnhookWinEvent(c_WinEventHook);
|
|
c_WinEventHook = nullptr;
|
|
}
|
|
|
|
if (c_HelperWindow)
|
|
{
|
|
DestroyWindow(c_HelperWindow);
|
|
c_HelperWindow = nullptr;
|
|
}
|
|
|
|
if (c_Window)
|
|
{
|
|
DestroyWindow(c_Window);
|
|
c_Window = nullptr;
|
|
}
|
|
}
|
|
|
|
/*
|
|
** Retrieves the multi-monitor information.
|
|
**
|
|
*/
|
|
BOOL CALLBACK MyInfoEnumProc(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData)
|
|
{
|
|
MultiMonitorInfo* m = (MultiMonitorInfo*)dwData;
|
|
|
|
MONITORINFOEX info;
|
|
info.cbSize = sizeof(MONITORINFOEX);
|
|
GetMonitorInfo(hMonitor, &info);
|
|
|
|
if (Rainmeter::GetInstance().GetDebug())
|
|
{
|
|
LogDebug(info.szDevice);
|
|
LogDebugF(L" Flags : %s(0x%08X)", (info.dwFlags & MONITORINFOF_PRIMARY) ? L"PRIMARY " : L"", info.dwFlags);
|
|
LogDebugF(L" Handle : 0x%p", hMonitor);
|
|
LogDebugF(L" ScrArea : L=%i, T=%i, R=%i, B=%i (W=%i, H=%i)",
|
|
lprcMonitor->left, lprcMonitor->top, lprcMonitor->right, lprcMonitor->bottom,
|
|
lprcMonitor->right - lprcMonitor->left, lprcMonitor->bottom - lprcMonitor->top);
|
|
LogDebugF(L" WorkArea : L=%i, T=%i, R=%i, B=%i (W=%i, H=%i)",
|
|
info.rcWork.left, info.rcWork.top, info.rcWork.right, info.rcWork.bottom,
|
|
info.rcWork.right - info.rcWork.left, info.rcWork.bottom - info.rcWork.top);
|
|
}
|
|
if (m == nullptr) return TRUE;
|
|
|
|
if (m->useEnumDisplayDevices)
|
|
{
|
|
for (auto iter = m->monitors.begin(); iter != m->monitors.end(); ++iter)
|
|
{
|
|
if ((*iter).handle == nullptr && _wcsicmp(info.szDevice, (*iter).deviceName.c_str()) == 0)
|
|
{
|
|
(*iter).handle = hMonitor;
|
|
(*iter).screen = *lprcMonitor;
|
|
(*iter).work = info.rcWork;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else // use only EnumDisplayMonitors
|
|
{
|
|
MonitorInfo monitor;
|
|
monitor.active = true;
|
|
|
|
monitor.handle = hMonitor;
|
|
monitor.screen = *lprcMonitor;
|
|
monitor.work = info.rcWork;
|
|
|
|
monitor.deviceName = info.szDevice; // E.g. "\\.\DISPLAY1"
|
|
|
|
// Get the monitor name (E.g. "Generic Non-PnP Monitor")
|
|
DISPLAY_DEVICE ddm = {sizeof(DISPLAY_DEVICE)};
|
|
DWORD dwMon = 0;
|
|
while (EnumDisplayDevices(info.szDevice, dwMon++, &ddm, 0))
|
|
{
|
|
if (ddm.StateFlags & DISPLAY_DEVICE_ACTIVE && ddm.StateFlags & DISPLAY_DEVICE_ATTACHED)
|
|
{
|
|
monitor.monitorName.assign(ddm.DeviceString, wcsnlen(ddm.DeviceString, _countof(ddm.DeviceString)));
|
|
break;
|
|
}
|
|
}
|
|
|
|
m->monitors.push_back(monitor);
|
|
|
|
if (info.dwFlags & MONITORINFOF_PRIMARY)
|
|
{
|
|
// It's primary monitor!
|
|
m->primary = (int)m->monitors.size();
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
** Returns the number of monitors.
|
|
**
|
|
*/
|
|
size_t System::GetMonitorCount()
|
|
{
|
|
if (c_Monitors.monitors.empty())
|
|
{
|
|
SetMultiMonitorInfo();
|
|
}
|
|
return c_Monitors.monitors.size();
|
|
}
|
|
|
|
/*
|
|
** Sets the multi-monitor information.
|
|
**
|
|
*/
|
|
void System::SetMultiMonitorInfo()
|
|
{
|
|
std::vector<MonitorInfo>& monitors = c_Monitors.monitors;
|
|
bool logging = Rainmeter::GetInstance().GetDebug();
|
|
|
|
c_Monitors.vsT = GetSystemMetrics(SM_YVIRTUALSCREEN);
|
|
c_Monitors.vsL = GetSystemMetrics(SM_XVIRTUALSCREEN);
|
|
c_Monitors.vsH = GetSystemMetrics(SM_CYVIRTUALSCREEN);
|
|
c_Monitors.vsW = GetSystemMetrics(SM_CXVIRTUALSCREEN);
|
|
|
|
c_Monitors.primary = 1; // If primary screen is not found, 1st screen is assumed as primary screen.
|
|
|
|
c_Monitors.useEnumDisplayDevices = true;
|
|
c_Monitors.useEnumDisplayMonitors = false;
|
|
|
|
if (logging)
|
|
{
|
|
LogDebug(L"------------------------------");
|
|
LogDebug(L"* EnumDisplayDevices / EnumDisplaySettings API");
|
|
}
|
|
|
|
DISPLAY_DEVICE dd = {sizeof(DISPLAY_DEVICE)};
|
|
|
|
if (EnumDisplayDevices(nullptr, 0, &dd, 0))
|
|
{
|
|
DWORD dwDevice = 0;
|
|
|
|
do
|
|
{
|
|
std::wstring msg;
|
|
|
|
std::wstring deviceName(dd.DeviceName, wcsnlen(dd.DeviceName, _countof(dd.DeviceName)));
|
|
std::wstring deviceString;
|
|
|
|
if (logging)
|
|
{
|
|
deviceString.assign(dd.DeviceString, wcsnlen(dd.DeviceString, _countof(dd.DeviceString)));
|
|
|
|
LogDebug(deviceName.c_str());
|
|
|
|
if (dd.StateFlags & DISPLAY_DEVICE_ACTIVE)
|
|
{
|
|
msg += L"ACTIVE ";
|
|
}
|
|
if (dd.StateFlags & DISPLAY_DEVICE_MULTI_DRIVER)
|
|
{
|
|
msg += L"MULTI ";
|
|
}
|
|
if (dd.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE)
|
|
{
|
|
msg += L"PRIMARY ";
|
|
}
|
|
if (dd.StateFlags & DISPLAY_DEVICE_MIRRORING_DRIVER)
|
|
{
|
|
msg += L"MIRROR ";
|
|
}
|
|
if (dd.StateFlags & DISPLAY_DEVICE_VGA_COMPATIBLE)
|
|
{
|
|
msg += L"VGA ";
|
|
}
|
|
if (dd.StateFlags & DISPLAY_DEVICE_REMOVABLE)
|
|
{
|
|
msg += L"REMOVABLE ";
|
|
}
|
|
if (dd.StateFlags & DISPLAY_DEVICE_MODESPRUNED)
|
|
{
|
|
msg += L"PRUNED ";
|
|
}
|
|
if (dd.StateFlags & DISPLAY_DEVICE_REMOTE)
|
|
{
|
|
msg += L"REMOTE ";
|
|
}
|
|
if (dd.StateFlags & DISPLAY_DEVICE_DISCONNECT)
|
|
{
|
|
msg += L"DISCONNECT ";
|
|
}
|
|
}
|
|
|
|
if ((dd.StateFlags & DISPLAY_DEVICE_MIRRORING_DRIVER) == 0)
|
|
{
|
|
MonitorInfo monitor = {0};
|
|
|
|
monitor.handle = nullptr;
|
|
monitor.deviceName = deviceName; // E.g. "\\.\DISPLAY1"
|
|
|
|
// Get the monitor name (E.g. "Generic Non-PnP Monitor")
|
|
DISPLAY_DEVICE ddm = {sizeof(DISPLAY_DEVICE)};
|
|
DWORD dwMon = 0;
|
|
while (EnumDisplayDevices(deviceName.c_str(), dwMon++, &ddm, 0))
|
|
{
|
|
if (ddm.StateFlags & DISPLAY_DEVICE_ACTIVE && ddm.StateFlags & DISPLAY_DEVICE_ATTACHED)
|
|
{
|
|
monitor.monitorName.assign(ddm.DeviceString, wcsnlen(ddm.DeviceString, _countof(ddm.DeviceString)));
|
|
|
|
if (logging)
|
|
{
|
|
LogDebugF(L" Name : %s", monitor.monitorName.c_str());
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (logging)
|
|
{
|
|
LogDebugF(L" Adapter : %s", deviceString.c_str());
|
|
LogDebugF(L" Flags : %s(0x%08X)", msg.c_str(), dd.StateFlags);
|
|
}
|
|
|
|
if (dd.StateFlags & DISPLAY_DEVICE_ACTIVE)
|
|
{
|
|
monitor.active = true;
|
|
|
|
DEVMODE dm = {0};
|
|
dm.dmSize = sizeof(DEVMODE);
|
|
|
|
if (EnumDisplaySettings(deviceName.c_str(), ENUM_CURRENT_SETTINGS, &dm))
|
|
{
|
|
POINT pos = {dm.dmPosition.x, dm.dmPosition.y};
|
|
monitor.handle = MonitorFromPoint(pos, MONITOR_DEFAULTTONULL);
|
|
|
|
if (logging)
|
|
{
|
|
LogDebugF(L" Handle : 0x%p", monitor.handle);
|
|
}
|
|
}
|
|
|
|
if (monitor.handle != nullptr)
|
|
{
|
|
MONITORINFO info = {sizeof(MONITORINFO)};
|
|
GetMonitorInfo(monitor.handle, &info);
|
|
|
|
monitor.screen = info.rcMonitor;
|
|
monitor.work = info.rcWork;
|
|
|
|
if (logging)
|
|
{
|
|
LogDebugF(L" ScrArea : L=%i, T=%i, R=%i, B=%i (W=%i, H=%i)",
|
|
info.rcMonitor.left, info.rcMonitor.top, info.rcMonitor.right, info.rcMonitor.bottom,
|
|
info.rcMonitor.right - info.rcMonitor.left, info.rcMonitor.bottom - info.rcMonitor.top);
|
|
LogDebugF(L" WorkArea : L=%i, T=%i, R=%i, B=%i (W=%i, H=%i)",
|
|
info.rcWork.left, info.rcWork.top, info.rcWork.right, info.rcWork.bottom,
|
|
info.rcWork.right - info.rcWork.left, info.rcWork.bottom - info.rcWork.top);
|
|
}
|
|
}
|
|
else // monitor not found
|
|
{
|
|
c_Monitors.useEnumDisplayMonitors = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
monitor.active = false;
|
|
}
|
|
|
|
monitors.push_back(monitor);
|
|
|
|
if (dd.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE)
|
|
{
|
|
// It's primary monitor!
|
|
c_Monitors.primary = (int)monitors.size();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (logging)
|
|
{
|
|
LogDebugF(L" Adapter : %s", deviceString.c_str());
|
|
LogDebugF(L" Flags : %s(0x%08X)", msg.c_str(), dd.StateFlags);
|
|
}
|
|
}
|
|
++dwDevice;
|
|
}
|
|
while (EnumDisplayDevices(nullptr, dwDevice, &dd, 0));
|
|
}
|
|
|
|
if (monitors.empty()) // Failed to enumerate the non-mirroring monitors
|
|
{
|
|
LogWarning(L"Failed to enumerate the non-mirroring monitors. Only EnumDisplayMonitors is used instead.");
|
|
c_Monitors.useEnumDisplayDevices = false;
|
|
c_Monitors.useEnumDisplayMonitors = true;
|
|
}
|
|
|
|
if (logging)
|
|
{
|
|
LogDebug(L"------------------------------");
|
|
LogDebug(L"* EnumDisplayMonitors API");
|
|
}
|
|
|
|
if (c_Monitors.useEnumDisplayMonitors)
|
|
{
|
|
EnumDisplayMonitors(nullptr, nullptr, MyInfoEnumProc, (LPARAM)(&c_Monitors));
|
|
|
|
if (monitors.empty()) // Failed to enumerate the monitors
|
|
{
|
|
LogWarning(L"Failed to enumerate monitors. Using dummy monitor info.");
|
|
c_Monitors.useEnumDisplayMonitors = false;
|
|
|
|
MonitorInfo monitor;
|
|
monitor.active = true;
|
|
|
|
POINT pos = {0, 0};
|
|
monitor.handle = MonitorFromPoint(pos, MONITOR_DEFAULTTOPRIMARY);
|
|
monitor.screen.left = 0;
|
|
monitor.screen.top = 0;
|
|
monitor.screen.right = GetSystemMetrics(SM_CXSCREEN);
|
|
monitor.screen.bottom = GetSystemMetrics(SM_CYSCREEN);
|
|
if (SystemParametersInfo(SPI_GETWORKAREA, 0, &(monitor.work), 0) == 0) // failed
|
|
{
|
|
monitor.work = monitor.screen;
|
|
}
|
|
|
|
monitor.deviceName = L"DUMMY";
|
|
|
|
monitors.push_back(monitor);
|
|
|
|
c_Monitors.primary = 1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (logging)
|
|
{
|
|
EnumDisplayMonitors(nullptr, nullptr, MyInfoEnumProc, (LPARAM)nullptr); // Only logging
|
|
}
|
|
}
|
|
|
|
if (logging)
|
|
{
|
|
LogDebug(L"------------------------------");
|
|
|
|
std::wstring method = L"* METHOD: ";
|
|
if (c_Monitors.useEnumDisplayDevices)
|
|
{
|
|
method += L"EnumDisplayDevices + ";
|
|
method += c_Monitors.useEnumDisplayMonitors ? L"EnumDisplayMonitors Mode" : L"EnumDisplaySettings Mode";
|
|
}
|
|
else
|
|
{
|
|
method += c_Monitors.useEnumDisplayMonitors ? L"EnumDisplayMonitors Mode" : L"Dummy Mode";
|
|
}
|
|
LogDebug(method.c_str());
|
|
|
|
LogDebugF(L"* MONITORS: Count=%i, Primary=@%i", (int)monitors.size(), c_Monitors.primary);
|
|
LogDebug(L"@0: Virtual screen");
|
|
LogDebugF(L" L=%i, T=%i, R=%i, B=%i (W=%i, H=%i)",
|
|
c_Monitors.vsL, c_Monitors.vsT, c_Monitors.vsL + c_Monitors.vsW, c_Monitors.vsT + c_Monitors.vsH,
|
|
c_Monitors.vsW, c_Monitors.vsH);
|
|
|
|
int i = 1;
|
|
for (auto iter = monitors.cbegin(); iter != monitors.cend(); ++iter, ++i)
|
|
{
|
|
if ((*iter).active)
|
|
{
|
|
LogDebugF(L"@%i: %s (active), MonitorName: %s", i, (*iter).deviceName.c_str(), (*iter).monitorName.c_str());
|
|
LogDebugF(L" L=%i, T=%i, R=%i, B=%i (W=%i, H=%i)",
|
|
(*iter).screen.left, (*iter).screen.top, (*iter).screen.right, (*iter).screen.bottom,
|
|
(*iter).screen.right - (*iter).screen.left, (*iter).screen.bottom - (*iter).screen.top);
|
|
}
|
|
else
|
|
{
|
|
LogDebugF(L"@%i: %s (inactive), MonitorName: %s", i, (*iter).deviceName.c_str(), (*iter).monitorName.c_str());
|
|
}
|
|
}
|
|
LogDebug(L"------------------------------");
|
|
}
|
|
}
|
|
|
|
/*
|
|
** Updates the workarea information.
|
|
**
|
|
*/
|
|
void System::UpdateWorkareaInfo()
|
|
{
|
|
std::vector<MonitorInfo>& monitors = c_Monitors.monitors;
|
|
|
|
if (monitors.empty())
|
|
{
|
|
SetMultiMonitorInfo();
|
|
return;
|
|
}
|
|
|
|
int i = 1;
|
|
for (auto iter = monitors.begin(); iter != monitors.end(); ++iter, ++i)
|
|
{
|
|
if ((*iter).active && (*iter).handle != nullptr)
|
|
{
|
|
MONITORINFO info = {sizeof(MONITORINFO)};
|
|
GetMonitorInfo((*iter).handle, &info);
|
|
|
|
(*iter).work = info.rcWork;
|
|
|
|
if (Rainmeter::GetInstance().GetDebug())
|
|
{
|
|
LogDebugF(L"WorkArea@%i : L=%i, T=%i, R=%i, B=%i (W=%i, H=%i)",
|
|
i,
|
|
info.rcWork.left, info.rcWork.top, info.rcWork.right, info.rcWork.bottom,
|
|
info.rcWork.right - info.rcWork.left, info.rcWork.bottom - info.rcWork.top);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
** Finds the Default Shell's window.
|
|
**
|
|
*/
|
|
HWND System::GetDefaultShellWindow()
|
|
{
|
|
static HWND c_ShellW = nullptr; // cache
|
|
HWND ShellW = GetShellWindow();
|
|
|
|
if (ShellW)
|
|
{
|
|
if (ShellW == c_ShellW)
|
|
{
|
|
return ShellW;
|
|
}
|
|
else
|
|
{
|
|
const int classLen = _countof(L"Progman") + 1;
|
|
WCHAR className[classLen];
|
|
if (!(GetClassName(ShellW, className, classLen) > 0 &&
|
|
wcscmp(className, L"Progman") == 0))
|
|
{
|
|
ShellW = nullptr;
|
|
}
|
|
}
|
|
}
|
|
|
|
c_ShellW = ShellW;
|
|
return ShellW;
|
|
}
|
|
|
|
/*
|
|
** Finds the WorkerW window.
|
|
** If the WorkerW window is not active, returns nullptr.
|
|
**
|
|
*/
|
|
HWND System::GetWorkerW()
|
|
{
|
|
static HWND c_DefView = nullptr; // cache
|
|
HWND ShellW = GetDefaultShellWindow();
|
|
if (!ShellW) return nullptr; // Default Shell (Explorer) not running
|
|
|
|
if (c_DefView && IsWindow(c_DefView))
|
|
{
|
|
HWND parent = GetAncestor(c_DefView, GA_PARENT);
|
|
if (parent)
|
|
{
|
|
if (parent == ShellW)
|
|
{
|
|
return nullptr;
|
|
}
|
|
else
|
|
{
|
|
const int classLen = _countof(L"WorkerW") + 1;
|
|
WCHAR className[classLen];
|
|
if (GetClassName(parent, className, classLen) > 0 &&
|
|
wcscmp(className, L"WorkerW") == 0)
|
|
{
|
|
return parent;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
HWND WorkerW = nullptr, DefView = FindWindowEx(ShellW, nullptr, L"SHELLDLL_DefView", L"");
|
|
if (DefView == nullptr)
|
|
{
|
|
while (WorkerW = FindWindowEx(nullptr, WorkerW, L"WorkerW", L""))
|
|
{
|
|
if (IsWindowVisible(WorkerW) &&
|
|
BelongToSameProcess(ShellW, WorkerW) &&
|
|
(DefView = FindWindowEx(WorkerW, nullptr, L"SHELLDLL_DefView", L"")))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
c_DefView = DefView;
|
|
return WorkerW;
|
|
}
|
|
|
|
/*
|
|
** Returns the first window whose position is not ZPOSITION_ONDESKTOP,
|
|
** ZPOSITION_BOTTOM, or ZPOSITION_NORMAL.
|
|
**
|
|
*/
|
|
HWND System::GetBackmostTopWindow()
|
|
{
|
|
HWND winPos = c_HelperWindow;
|
|
|
|
// Skip all ZPOSITION_ONDESKTOP, ZPOSITION_BOTTOM, and ZPOSITION_NORMAL windows
|
|
while (winPos = ::GetNextWindow(winPos, GW_HWNDPREV))
|
|
{
|
|
MeterWindow* wnd = Rainmeter::GetInstance().GetMeterWindow(winPos);
|
|
if (!wnd ||
|
|
(wnd->GetWindowZPosition() != ZPOSITION_NORMAL &&
|
|
wnd->GetWindowZPosition() != ZPOSITION_ONDESKTOP &&
|
|
wnd->GetWindowZPosition() != ZPOSITION_ONBOTTOM))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
return winPos;
|
|
}
|
|
|
|
/*
|
|
** Checks whether the given windows belong to the same process.
|
|
**
|
|
*/
|
|
bool System::BelongToSameProcess(HWND hwndA, HWND hwndB)
|
|
{
|
|
DWORD procAId = 0, procBId = 0;
|
|
|
|
GetWindowThreadProcessId(hwndA, &procAId);
|
|
GetWindowThreadProcessId(hwndB, &procBId);
|
|
|
|
return (procAId == procBId);
|
|
}
|
|
|
|
/*
|
|
** Retrieves the Rainmeter's meter windows in Z-order.
|
|
**
|
|
*/
|
|
BOOL CALLBACK MyEnumWindowsProc(HWND hwnd, LPARAM lParam)
|
|
{
|
|
bool logging = Rainmeter::GetInstance().GetDebug() && DEBUG_VERBOSE;
|
|
const int classLen = _countof(METERWINDOW_CLASS_NAME) + (DEBUG_VERBOSE ? 32 : 1);
|
|
WCHAR className[classLen];
|
|
MeterWindow* Window;
|
|
WCHAR flag;
|
|
|
|
if (GetClassName(hwnd, className, classLen) > 0 &&
|
|
wcscmp(className, METERWINDOW_CLASS_NAME) == 0 &&
|
|
(Window = Rainmeter::GetInstance().GetMeterWindow(hwnd)))
|
|
{
|
|
ZPOSITION zPos = Window->GetWindowZPosition();
|
|
if (zPos == ZPOSITION_ONDESKTOP ||
|
|
(zPos == ZPOSITION_NORMAL && Rainmeter::GetInstance().IsNormalStayDesktop()) ||
|
|
zPos == ZPOSITION_ONBOTTOM)
|
|
{
|
|
if (lParam)
|
|
{
|
|
((std::vector<MeterWindow*>*)lParam)->push_back(Window);
|
|
}
|
|
|
|
if (logging) flag = L'+';
|
|
}
|
|
else
|
|
{
|
|
if (logging) flag = L'-';
|
|
}
|
|
|
|
if (logging)
|
|
{
|
|
LogDebugF(L"%c [%c] 0x%p : %s (Name: \"%s\", zPos=%i)",
|
|
flag, IsWindowVisible(hwnd) ? L'V' : L'H', hwnd, className, Window->GetFolderPath().c_str(), (int)zPos);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (logging)
|
|
{
|
|
flag = (hwnd == System::GetHelperWindow()) ? L'o' : ' ';
|
|
LogDebugF(L"%c [%c] 0x%p : %s", flag, IsWindowVisible(hwnd) ? L'V' : L'H', hwnd, className);
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
** Arranges the meter window in Z-order.
|
|
**
|
|
*/
|
|
void System::ChangeZPosInOrder()
|
|
{
|
|
bool logging = Rainmeter::GetInstance().GetDebug() && DEBUG_VERBOSE;
|
|
std::vector<MeterWindow*> windowsInZOrder;
|
|
|
|
if (logging) LogDebug(L"1: ----- BEFORE -----");
|
|
|
|
// Retrieve the Rainmeter's meter windows in Z-order
|
|
EnumWindows(MyEnumWindowsProc, (LPARAM)(&windowsInZOrder));
|
|
|
|
auto resetZPos = [&](ZPOSITION zpos)
|
|
{
|
|
// Reset ZPos in Z-order (Bottom)
|
|
std::vector<MeterWindow*>::const_iterator iter = windowsInZOrder.begin();
|
|
for ( ; iter != windowsInZOrder.end(); ++iter)
|
|
{
|
|
if ((*iter)->GetWindowZPosition() == zpos)
|
|
{
|
|
(*iter)->ChangeZPos(zpos); // reset
|
|
}
|
|
}
|
|
};
|
|
|
|
if (Rainmeter::GetInstance().IsNormalStayDesktop())
|
|
{
|
|
resetZPos(ZPOSITION_NORMAL);
|
|
}
|
|
|
|
if (!c_ShowDesktop)
|
|
{
|
|
resetZPos(ZPOSITION_ONBOTTOM);
|
|
}
|
|
|
|
resetZPos(ZPOSITION_ONDESKTOP);
|
|
|
|
if (logging)
|
|
{
|
|
LogDebug(L"2: ----- AFTER -----");
|
|
|
|
// Log all windows in Z-order
|
|
EnumWindows(MyEnumWindowsProc, (LPARAM)nullptr);
|
|
}
|
|
}
|
|
|
|
/*
|
|
** Moves the helper window to the reference position.
|
|
**
|
|
*/
|
|
void System::PrepareHelperWindow(HWND WorkerW)
|
|
{
|
|
bool logging = Rainmeter::GetInstance().GetDebug() && DEBUG_VERBOSE;
|
|
|
|
SetWindowPos(c_Window, HWND_BOTTOM, 0, 0, 0, 0, ZPOS_FLAGS); // always on bottom
|
|
|
|
if (c_ShowDesktop && WorkerW)
|
|
{
|
|
// Set WS_EX_TOPMOST flag
|
|
SetWindowPos(c_HelperWindow, HWND_TOPMOST, 0, 0, 0, 0, ZPOS_FLAGS);
|
|
|
|
// Find the "backmost" topmost window
|
|
HWND hwnd = WorkerW;
|
|
while (hwnd = ::GetNextWindow(hwnd, GW_HWNDPREV))
|
|
{
|
|
if (GetWindowLongPtr(hwnd, GWL_EXSTYLE) & WS_EX_TOPMOST)
|
|
{
|
|
WCHAR className[64], windowText[64];
|
|
|
|
if (logging)
|
|
{
|
|
GetClassName(hwnd, className, 64);
|
|
GetWindowText(hwnd, windowText, 64);
|
|
|
|
SetLastError(ERROR_SUCCESS);
|
|
}
|
|
|
|
// Insert the helper window after the found window
|
|
if (0 != SetWindowPos(c_HelperWindow, hwnd, 0, 0, 0, 0, ZPOS_FLAGS))
|
|
{
|
|
if (logging)
|
|
{
|
|
LogDebugF(L"System: HelperWindow: hwnd=0x%p (WorkerW=0x%p), hwndInsertAfter=0x%p (\"%s\" %s) - %s",
|
|
c_HelperWindow, WorkerW, hwnd, windowText, className, (GetWindowLongPtr(c_HelperWindow, GWL_EXSTYLE) & WS_EX_TOPMOST) ? L"TOPMOST" : L"NORMAL");
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (logging)
|
|
{
|
|
DWORD err = GetLastError();
|
|
LogDebugF(L"System: HelperWindow: hwnd=0x%p (WorkerW=0x%p), hwndInsertAfter=0x%p (\"%s\" %s) - FAILED (ErrorCode=0x%08X)",
|
|
c_HelperWindow, WorkerW, hwnd, windowText, className, err);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (logging)
|
|
{
|
|
LogDebugF(L"System: HelperWindow: hwnd=0x%p (WorkerW=0x%p), hwndInsertAfter=HWND_TOPMOST - %s",
|
|
c_HelperWindow, WorkerW, (GetWindowLongPtr(c_HelperWindow, GWL_EXSTYLE) & WS_EX_TOPMOST) ? L"TOPMOST" : L"NORMAL");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Insert the helper window to the bottom
|
|
SetWindowPos(c_HelperWindow, HWND_BOTTOM, 0, 0, 0, 0, ZPOS_FLAGS);
|
|
|
|
if (logging)
|
|
{
|
|
LogDebugF(L"System: HelperWindow: hwnd=0x%p (WorkerW=0x%p), hwndInsertAfter=HWND_BOTTOM - %s",
|
|
c_HelperWindow, WorkerW, (GetWindowLongPtr(c_HelperWindow, GWL_EXSTYLE) & WS_EX_TOPMOST) ? L"TOPMOST" : L"NORMAL");
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
** Changes the "Show Desktop" state.
|
|
**
|
|
*/
|
|
bool System::CheckDesktopState(HWND WorkerW)
|
|
{
|
|
HWND hwnd = nullptr;
|
|
|
|
if (WorkerW && IsWindowVisible(WorkerW))
|
|
{
|
|
hwnd = FindWindowEx(nullptr, WorkerW, L"RainmeterSystem", L"System");
|
|
}
|
|
|
|
bool stateChanged = (hwnd && !c_ShowDesktop) || (!hwnd && c_ShowDesktop);
|
|
|
|
if (stateChanged)
|
|
{
|
|
c_ShowDesktop = !c_ShowDesktop;
|
|
|
|
if (Rainmeter::GetInstance().GetDebug())
|
|
{
|
|
LogDebugF(L"System: \"Show %s\" has been detected.",
|
|
c_ShowDesktop ? L"desktop" : L"open windows");
|
|
}
|
|
|
|
PrepareHelperWindow(WorkerW);
|
|
|
|
ChangeZPosInOrder();
|
|
|
|
if (c_ShowDesktop)
|
|
{
|
|
SetTimer(c_Window, TIMER_SHOWDESKTOP, INTERVAL_RESTOREWINDOWS, nullptr);
|
|
}
|
|
else
|
|
{
|
|
SetTimer(c_Window, TIMER_SHOWDESKTOP, INTERVAL_SHOWDESKTOP, nullptr);
|
|
}
|
|
}
|
|
|
|
return stateChanged;
|
|
}
|
|
|
|
/*
|
|
** The event hook procedure
|
|
**
|
|
*/
|
|
void CALLBACK System::MyWinEventProc(HWINEVENTHOOK hWinEventHook, DWORD event, HWND hwnd, LONG idObject, LONG idChild, DWORD dwEventThread, DWORD dwmsEventTime)
|
|
{
|
|
if (event == EVENT_SYSTEM_FOREGROUND)
|
|
{
|
|
if (!c_ShowDesktop)
|
|
{
|
|
const int classLen = _countof(L"WorkerW") + 1;
|
|
WCHAR className[classLen];
|
|
if (GetClassName(hwnd, className, classLen) > 0 &&
|
|
wcscmp(className, L"WorkerW") == 0 &&
|
|
BelongToSameProcess(GetDefaultShellWindow(), hwnd))
|
|
{
|
|
const int max = 5;
|
|
int loop = 0;
|
|
while (loop < max && FindWindowEx(hwnd, nullptr, L"SHELLDLL_DefView", L"") == nullptr)
|
|
{
|
|
Sleep(2); // Wait for 2-16 ms before retrying
|
|
++loop;
|
|
}
|
|
|
|
if (loop < max)
|
|
{
|
|
loop = 0;
|
|
while (loop < max && !CheckDesktopState(hwnd))
|
|
{
|
|
Sleep(2); // Wait for 2-16 ms before retrying
|
|
++loop;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
** The window procedure
|
|
**
|
|
*/
|
|
LRESULT CALLBACK System::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
if (hWnd != c_Window)
|
|
{
|
|
if (uMsg == WM_WINDOWPOSCHANGING)
|
|
{
|
|
((LPWINDOWPOS)lParam)->flags |= SWP_NOZORDER;
|
|
return 0;
|
|
}
|
|
return DefWindowProc(hWnd, uMsg, wParam, lParam);
|
|
}
|
|
|
|
switch (uMsg)
|
|
{
|
|
case WM_WINDOWPOSCHANGING:
|
|
((LPWINDOWPOS)lParam)->flags |= SWP_NOZORDER;
|
|
break;
|
|
|
|
case WM_TIMER:
|
|
switch (wParam)
|
|
{
|
|
case TIMER_SHOWDESKTOP:
|
|
if (wParam == TIMER_SHOWDESKTOP)
|
|
{
|
|
CheckDesktopState(GetWorkerW());
|
|
}
|
|
break;
|
|
|
|
case TIMER_RESUME:
|
|
KillTimer(hWnd, TIMER_RESUME);
|
|
if (Rainmeter::GetInstance().IsRedrawable())
|
|
{
|
|
std::map<std::wstring, MeterWindow*>::const_iterator iter = Rainmeter::GetInstance().GetAllMeterWindows().begin();
|
|
for ( ; iter != Rainmeter::GetInstance().GetAllMeterWindows().end(); ++iter)
|
|
{
|
|
(*iter).second->RedrawWindow();
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case WM_DISPLAYCHANGE:
|
|
LogNotice(L"System: Display settings changed");
|
|
ClearMultiMonitorInfo();
|
|
ConfigParser::ClearMultiMonitorVariables();
|
|
case WM_SETTINGCHANGE:
|
|
if (uMsg == WM_DISPLAYCHANGE || (/*uMsg == WM_SETTINGCHANGE &&*/ wParam == SPI_SETWORKAREA))
|
|
{
|
|
if (uMsg == WM_SETTINGCHANGE) // SPI_SETWORKAREA
|
|
{
|
|
LogNotice(L"System: Work area changed");
|
|
UpdateWorkareaInfo();
|
|
ConfigParser::UpdateWorkareaVariables();
|
|
}
|
|
|
|
// Deliver WM_DISPLAYCHANGE / WM_SETTINGCHANGE message to all meter windows
|
|
std::map<std::wstring, MeterWindow*>::const_iterator iter = Rainmeter::GetInstance().GetAllMeterWindows().begin();
|
|
for ( ; iter != Rainmeter::GetInstance().GetAllMeterWindows().end(); ++iter)
|
|
{
|
|
PostMessage((*iter).second->GetWindow(), WM_METERWINDOW_DELAYED_MOVE, (WPARAM)uMsg, (LPARAM)0);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case WM_POWERBROADCAST:
|
|
if (wParam == PBT_APMRESUMESUSPEND)
|
|
{
|
|
// Deliver PBT_APMRESUMESUSPEND event to all meter windows
|
|
SetTimer(hWnd, TIMER_RESUME, INTERVAL_RESUME, nullptr);
|
|
}
|
|
return TRUE;
|
|
|
|
default:
|
|
return DefWindowProc(hWnd, uMsg, wParam, lParam);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
** Retrieves the number of milliseconds that have elapsed since the system was started.
|
|
** In XP, returns the predictive value due to the 32bit limitation.
|
|
**
|
|
*/
|
|
ULONGLONG System::GetTickCount64()
|
|
{
|
|
static auto s_GetTickCount64 =
|
|
(decltype(GetTickCount64)*)GetProcAddress(GetModuleHandle(L"kernel32"), "GetTickCount64");
|
|
|
|
if (s_GetTickCount64)
|
|
{
|
|
return s_GetTickCount64();
|
|
}
|
|
else
|
|
{
|
|
static ULONGLONG lastTicks = 0;
|
|
ULONGLONG ticks = GetTickCount();
|
|
while (ticks < lastTicks) ticks += 0x100000000;
|
|
lastTicks = ticks;
|
|
return ticks;
|
|
}
|
|
}
|
|
|
|
/*
|
|
** Gets the cursor position in last message retrieved by GetMessage().
|
|
**
|
|
*/
|
|
POINT System::GetCursorPosition()
|
|
{
|
|
DWORD pos = GetMessagePos();
|
|
POINT pt = { GET_X_LPARAM(pos), GET_Y_LPARAM(pos) };
|
|
return pt;
|
|
}
|
|
|
|
/*
|
|
** Checks if file is writable.
|
|
**
|
|
*/
|
|
bool System::IsFileWritable(LPCWSTR file)
|
|
{
|
|
HANDLE hFile = CreateFile(file, GENERIC_WRITE, FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, 0, nullptr);
|
|
if (hFile == INVALID_HANDLE_VALUE)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
CloseHandle(hFile);
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
** This function is a wrapper function for LoadLibrary().
|
|
**
|
|
** Avoids loading a DLL from current directory.
|
|
**
|
|
*/
|
|
HMODULE System::RmLoadLibrary(LPCWSTR lpLibFileName, DWORD* dwError)
|
|
{
|
|
// Remove current directory from DLL search path
|
|
SetDllDirectory(L"");
|
|
|
|
SetLastError(ERROR_SUCCESS);
|
|
HMODULE hLib = LoadLibrary(lpLibFileName);
|
|
|
|
if (dwError)
|
|
{
|
|
*dwError = GetLastError();
|
|
}
|
|
|
|
return hLib;
|
|
}
|
|
|
|
/*
|
|
** Resets working directory to default.
|
|
**
|
|
*/
|
|
void System::ResetWorkingDirectory()
|
|
{
|
|
WCHAR directory[MAX_PATH] = {0};
|
|
GetCurrentDirectory(MAX_PATH, directory);
|
|
|
|
const WCHAR* workDir = c_WorkingDirectory.c_str();
|
|
if (_wcsicmp(directory, workDir) != 0)
|
|
{
|
|
SetCurrentDirectory(workDir);
|
|
}
|
|
}
|
|
|
|
/*
|
|
** Initializes a critical section object by using InitializeCriticalSectionEx function with CRITICAL_SECTION_NO_DEBUG_INFO flag.
|
|
** For more details: http://stackoverflow.com/questions/804848/critical-sections-leaking-memory-on-vista-win2008/
|
|
**
|
|
*/
|
|
void System::InitializeCriticalSection(LPCRITICAL_SECTION lpCriticalSection)
|
|
{
|
|
static auto s_InitializeCriticalSectionEx = IsWindowsVistaOrGreater() ?
|
|
(decltype(InitializeCriticalSectionEx)*)GetProcAddress(GetModuleHandle(L"kernel32"), "InitializeCriticalSectionEx") : nullptr;
|
|
|
|
if (s_InitializeCriticalSectionEx &&
|
|
s_InitializeCriticalSectionEx(lpCriticalSection, 0, CRITICAL_SECTION_NO_DEBUG_INFO))
|
|
{
|
|
return;
|
|
}
|
|
|
|
InitializeCriticalSectionAndSpinCount(lpCriticalSection, 0);
|
|
}
|
|
|
|
/*
|
|
** Sets clipboard text to given string.
|
|
**
|
|
*/
|
|
void System::SetClipboardText(const std::wstring& text)
|
|
{
|
|
if (OpenClipboard(nullptr))
|
|
{
|
|
// Include terminating null char
|
|
size_t len = text.length() + 1;
|
|
|
|
HGLOBAL hMem = GlobalAlloc(GMEM_MOVEABLE, len * sizeof(WCHAR));
|
|
if (hMem)
|
|
{
|
|
LPVOID data = GlobalLock(hMem);
|
|
memcpy(data, text.c_str(), len * sizeof(WCHAR));
|
|
GlobalUnlock(hMem);
|
|
|
|
EmptyClipboard();
|
|
if (!SetClipboardData(CF_UNICODETEXT, hMem))
|
|
{
|
|
GlobalFree(hMem);
|
|
}
|
|
}
|
|
|
|
CloseClipboard();
|
|
}
|
|
}
|
|
|
|
/*
|
|
** Sets the system wallpapar.
|
|
**
|
|
*/
|
|
void System::SetWallpaper(const std::wstring& wallpaper, const std::wstring& style)
|
|
{
|
|
if (!wallpaper.empty())
|
|
{
|
|
if (_waccess(wallpaper.c_str(), 0) == -1)
|
|
{
|
|
LogErrorF(L"!SetWallpaper: Unable to read file: %s", wallpaper.c_str());
|
|
return;
|
|
}
|
|
|
|
Bitmap bitmap(wallpaper.c_str());
|
|
if (bitmap.GetLastStatus() == Ok)
|
|
{
|
|
std::wstring file = Rainmeter::GetInstance().GetSettingsPath() + L"Wallpaper.bmp";
|
|
|
|
const CLSID bmpClsid = { 0x557cf400, 0x1a04, 0x11d3, { 0x9a, 0x73, 0x0, 0x0, 0xf8, 0x1e, 0xf3, 0x2e } };
|
|
if (bitmap.Save(file.c_str(), &bmpClsid) == Ok)
|
|
{
|
|
if (!style.empty())
|
|
{
|
|
HKEY hKey;
|
|
if (RegOpenKeyEx(HKEY_CURRENT_USER, L"Control Panel\\Desktop", 0, KEY_SET_VALUE, &hKey) == ERROR_SUCCESS)
|
|
{
|
|
const WCHAR* wallStyle = nullptr;
|
|
const WCHAR* wallTile = L"0";
|
|
|
|
const WCHAR* option = style.c_str();
|
|
if (_wcsicmp(option, L"CENTER") == 0)
|
|
{
|
|
wallStyle = L"0";
|
|
}
|
|
else if (_wcsicmp(option, L"TILE") == 0)
|
|
{
|
|
wallStyle = L"0";
|
|
wallTile = L"1";
|
|
}
|
|
else if (_wcsicmp(option, L"STRETCH") == 0)
|
|
{
|
|
wallStyle = L"2";
|
|
}
|
|
else if (IsWindows7OrGreater())
|
|
{
|
|
if (_wcsicmp(option, L"FIT") == 0)
|
|
{
|
|
wallStyle = L"6";
|
|
}
|
|
else if (_wcsicmp(option, L"FILL") == 0)
|
|
{
|
|
wallStyle = L"10";
|
|
}
|
|
}
|
|
|
|
if (wallStyle)
|
|
{
|
|
RegSetValueEx(hKey, L"WallpaperStyle", 0, REG_SZ, (const BYTE*)wallStyle, sizeof(WCHAR) * 2);
|
|
RegSetValueEx(hKey, L"TileWallpaper", 0, REG_SZ, (const BYTE*)wallTile, sizeof(WCHAR) * 2);
|
|
}
|
|
else
|
|
{
|
|
LogError(L"!SetWallpaper: Invalid style");
|
|
}
|
|
|
|
RegCloseKey(hKey);
|
|
}
|
|
}
|
|
|
|
SystemParametersInfo(SPI_SETDESKWALLPAPER, 0, (void*)file.c_str(), SPIF_UPDATEINIFILE);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
** Copies files and folders from one location to another.
|
|
**
|
|
*/
|
|
bool System::CopyFiles(std::wstring from, std::wstring to, bool bMove)
|
|
{
|
|
// If given "from" path ends with path separator, remove it (Workaround for XP: error code 1026)
|
|
size_t len;
|
|
while (len = from.size(), len > 0 && PathUtil::IsSeparator(from[len - 1]))
|
|
{
|
|
from.resize(len - 1);
|
|
}
|
|
|
|
// The strings must end with double \0
|
|
from.append(1, L'\0');
|
|
to.append(1, L'\0');
|
|
|
|
SHFILEOPSTRUCT fo =
|
|
{
|
|
nullptr,
|
|
bMove ? FO_MOVE : FO_COPY,
|
|
from.c_str(),
|
|
to.c_str(),
|
|
FOF_NO_UI | FOF_NOCONFIRMATION | FOF_ALLOWUNDO
|
|
};
|
|
|
|
int result = SHFileOperation(&fo);
|
|
if (result != 0)
|
|
{
|
|
LogErrorF(L"Copy error: From %s to %s (%i)", from.c_str(), to.c_str(), result);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
** Removes a file even if a file is read-only.
|
|
**
|
|
*/
|
|
bool System::RemoveFile(const std::wstring& file)
|
|
{
|
|
DWORD attr = GetFileAttributes(file.c_str());
|
|
if (attr == -1 || (attr & FILE_ATTRIBUTE_READONLY))
|
|
{
|
|
// Unset read-only
|
|
SetFileAttributes(file.c_str(), (attr == -1) ? FILE_ATTRIBUTE_NORMAL : attr - FILE_ATTRIBUTE_READONLY);
|
|
}
|
|
|
|
return (DeleteFile(file.c_str()) != 0);
|
|
}
|
|
|
|
/*
|
|
** Recursively removes folder.
|
|
**
|
|
*/
|
|
bool System::RemoveFolder(std::wstring folder)
|
|
{
|
|
// The strings must end with double nul
|
|
folder.append(1, L'\0');
|
|
|
|
SHFILEOPSTRUCT fo =
|
|
{
|
|
nullptr,
|
|
FO_DELETE,
|
|
folder.c_str(),
|
|
nullptr,
|
|
FOF_NO_UI | FOF_NOCONFIRMATION | FOF_ALLOWUNDO
|
|
};
|
|
|
|
int result = SHFileOperation(&fo);
|
|
if (result != 0)
|
|
{
|
|
LogErrorF(L"Unable to delete folder %s (%i)", folder.c_str(), result);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
** Retrieves the "IniFileMapping" entries from Registry.
|
|
**
|
|
*/
|
|
void System::UpdateIniFileMappingList()
|
|
{
|
|
static ULONGLONG s_LastWriteTime = 0;
|
|
|
|
HKEY hKey;
|
|
LONG ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\IniFileMapping", 0, KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS, &hKey);
|
|
if (ret == ERROR_SUCCESS)
|
|
{
|
|
DWORD numSubKeys;
|
|
ULONGLONG ftLastWriteTime;
|
|
bool changed = false;
|
|
|
|
ret = RegQueryInfoKey(hKey, nullptr, nullptr, nullptr, &numSubKeys, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, (LPFILETIME)&ftLastWriteTime);
|
|
if (ret == ERROR_SUCCESS)
|
|
{
|
|
//LogDebugF(L"IniFileMapping: numSubKeys=%u, ftLastWriteTime=%llu", numSubKeys, ftLastWriteTime);
|
|
|
|
if (ftLastWriteTime != s_LastWriteTime ||
|
|
numSubKeys != c_IniFileMappings.size())
|
|
{
|
|
s_LastWriteTime = ftLastWriteTime;
|
|
if (numSubKeys > c_IniFileMappings.capacity())
|
|
{
|
|
c_IniFileMappings.reserve(numSubKeys);
|
|
}
|
|
changed = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
s_LastWriteTime = 0;
|
|
changed = true;
|
|
}
|
|
|
|
if (changed)
|
|
{
|
|
if (!c_IniFileMappings.empty())
|
|
{
|
|
c_IniFileMappings.clear();
|
|
}
|
|
|
|
WCHAR* buffer = new WCHAR[MAX_PATH];
|
|
DWORD index = 0, cch = MAX_PATH;
|
|
|
|
while ((ret = RegEnumKeyEx(hKey, index++, buffer, &cch, nullptr, nullptr, nullptr, nullptr)) != ERROR_NO_MORE_ITEMS)
|
|
{
|
|
if (ret == ERROR_SUCCESS)
|
|
{
|
|
c_IniFileMappings.push_back(buffer);
|
|
}
|
|
cch = MAX_PATH;
|
|
}
|
|
|
|
delete [] buffer;
|
|
}
|
|
|
|
RegCloseKey(hKey);
|
|
}
|
|
}
|
|
|
|
/*
|
|
** Prepares a temporary file if iniFile is included in the "IniFileMapping" entries.
|
|
** If iniFile is not included, returns a empty string. If error occurred, returns "?".
|
|
** Note that a temporary file must be deleted by caller.
|
|
**
|
|
*/
|
|
std::wstring System::GetTemporaryFile(const std::wstring& iniFile)
|
|
{
|
|
std::wstring temporary;
|
|
|
|
if (!c_IniFileMappings.empty())
|
|
{
|
|
std::wstring::size_type pos = iniFile.find_last_of(L"\\/");
|
|
const WCHAR* filename = iniFile.c_str() + ((pos != std::wstring::npos) ? pos + 1 : 0);
|
|
|
|
std::vector<std::wstring>::const_iterator iter = c_IniFileMappings.begin();
|
|
for ( ; iter != c_IniFileMappings.end(); ++iter)
|
|
{
|
|
if (_wcsicmp((*iter).c_str(), filename) == 0)
|
|
{
|
|
WCHAR* buffer = new WCHAR[MAX_PATH];
|
|
|
|
if (GetTempPath(MAX_PATH, buffer) != 0 &&
|
|
GetTempFileName(buffer, L"cfg", 0, buffer) != 0)
|
|
{
|
|
temporary = buffer;
|
|
|
|
std::wstring tmp = GetTemporaryFile(temporary);
|
|
if (!tmp.empty() || !CopyFiles(iniFile, temporary)) // temporary is reserved or failed
|
|
{
|
|
RemoveFile(temporary);
|
|
|
|
if (tmp.empty())
|
|
{
|
|
temporary = L"?";
|
|
}
|
|
else
|
|
{
|
|
temporary.swap(tmp);
|
|
}
|
|
}
|
|
}
|
|
else // failed
|
|
{
|
|
LogErrorF(L"Unable to create temporary file to: %s", temporary.c_str());
|
|
temporary = L"?";
|
|
}
|
|
|
|
delete [] buffer;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return temporary;
|
|
}
|