Merge branch 'master' of https://github.com/microsoft/PowerToys into dev/PowerLauncher

This commit is contained in:
Alekhya Reddy 2020-04-14 21:27:04 -07:00
commit 65276f45f1
39 changed files with 1637 additions and 913 deletions

100
NOTICE.md
View File

@ -18,11 +18,12 @@ Notwithstanding any other terms, you may reverse engineer this software to the
extent required to debug changes to any libraries licensed under the GNU Lesser
General Public License.
## ImageResizer
## PowerToy: ImageResizer
### Brice Lams's Image Resizer License
**Source**: https://github.com/bricelam/ImageResizer/
### License
The MIT License (MIT)
Copyright (c) Brice Lambson. All rights reserved.
@ -44,3 +45,98 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
## PowerToy: Launcher
### Wox License
**Source**: https://github.com/Wox-launcher/Wox
The MIT License (MIT)
Copyright (c) 2015 Wox
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
### Beta Tadele's Window Walker License
**Source**: https://github.com/betsegaw/windowwalker
The MIT License (MIT)
Copyright 2020 Betsegaw Tadele
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
### Squirrel.Windows License
**Source**: https://github.com/Squirrel/Squirrel.Windows/
The MIT License (MIT)
Copyright (c) 2012 GitHub, Inc.
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
## PowerToy: PowerRename
### Chris Davis's SmartRename License
**Source**: https://github.com/chrdavis/SmartRename
MIT License
Copyright (c) 2017 Chris Davis
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -17,11 +17,11 @@
using namespace std;
TRACELOGGING_DEFINE_PROVIDER(
g_hProvider,
"Microsoft.PowerToysInstaller",
// {e1d8165d-5cb6-5c74-3b51-bdfbfe4f7a3b}
(0xe1d8165d, 0x5cb6, 0x5c74, 0x3b, 0x51, 0xbd, 0xfb, 0xfe, 0x4f, 0x7a, 0x3b),
TraceLoggingOptionProjectTelemetry());
g_hProvider,
"Microsoft.PowerToysInstaller",
// {e1d8165d-5cb6-5c74-3b51-bdfbfe4f7a3b}
(0xe1d8165d, 0x5cb6, 0x5c74, 0x3b, 0x51, 0xbd, 0xfb, 0xfe, 0x4f, 0x7a, 0x3b),
TraceLoggingOptionProjectTelemetry());
const DWORD USERNAME_DOMAIN_LEN = DNLEN + UNLEN + 2; // Domain Name + '\' + User Name + '\0'
const DWORD USERNAME_LEN = UNLEN + 1; // User Name + '\0'
@ -30,502 +30,562 @@ const DWORD USERNAME_LEN = UNLEN + 1; // User Name + '\0'
// The path of the executable to run should be passed as the CustomActionData (Value).
// Based on the Task Scheduler Logon Trigger Example:
// https://docs.microsoft.com/en-us/windows/win32/taskschd/logon-trigger-example--c---/
UINT __stdcall CreateScheduledTaskCA(MSIHANDLE hInstall) {
HRESULT hr = S_OK;
UINT er = ERROR_SUCCESS;
UINT __stdcall CreateScheduledTaskCA(MSIHANDLE hInstall)
{
HRESULT hr = S_OK;
UINT er = ERROR_SUCCESS;
TCHAR username_domain[USERNAME_DOMAIN_LEN];
TCHAR username[USERNAME_LEN];
TCHAR username_domain[USERNAME_DOMAIN_LEN];
TCHAR username[USERNAME_LEN];
std::wstring wstrTaskName;
std::wstring wstrTaskName;
ITaskService *pService = NULL;
ITaskFolder *pTaskFolder = NULL;
ITaskDefinition *pTask = NULL;
IRegistrationInfo *pRegInfo = NULL;
ITaskSettings *pSettings = NULL;
ITriggerCollection *pTriggerCollection = NULL;
IRegisteredTask *pRegisteredTask = NULL;
ITaskService* pService = NULL;
ITaskFolder* pTaskFolder = NULL;
ITaskDefinition* pTask = NULL;
IRegistrationInfo* pRegInfo = NULL;
ITaskSettings* pSettings = NULL;
ITriggerCollection* pTriggerCollection = NULL;
IRegisteredTask* pRegisteredTask = NULL;
hr = WcaInitialize(hInstall, "CreateScheduledTaskCA");
ExitOnFailure(hr, "Failed to initialize");
hr = WcaInitialize(hInstall, "CreateScheduledTaskCA");
ExitOnFailure(hr, "Failed to initialize");
WcaLog(LOGMSG_STANDARD, "Initialized.");
WcaLog(LOGMSG_STANDARD, "Initialized.");
// ------------------------------------------------------
// Get the Domain/Username for the trigger.
//
// This action needs to run as the system to get elevated privileges from the installation,
// so GetUserNameEx can't be used to get the current user details.
// The USERNAME and USERDOMAIN environment variables are used instead.
if (!GetEnvironmentVariable(L"USERNAME", username, USERNAME_LEN)) {
ExitWithLastError(hr, "Getting username failed: %x", hr);
}
if (!GetEnvironmentVariable(L"USERDOMAIN", username_domain, USERNAME_DOMAIN_LEN)) {
ExitWithLastError(hr, "Getting the user's domain failed: %x", hr);
}
wcscat_s(username_domain, L"\\");
wcscat_s(username_domain, username);
WcaLog(LOGMSG_STANDARD, "Current user detected: %ls", username_domain);
// Task Name.
wstrTaskName = L"Autorun for ";
wstrTaskName += username;
// Get the executable path passed to the custom action.
LPWSTR wszExecutablePath = NULL;
hr = WcaGetProperty(L"CustomActionData", &wszExecutablePath);
ExitOnFailure(hr, "Failed to get the executable path from CustomActionData.");
// COM and Security Initialization is expected to have been done by the MSI.
// It couldn't be done in the DLL, anyway.
// ------------------------------------------------------
// Create an instance of the Task Service.
hr = CoCreateInstance(CLSID_TaskScheduler,
NULL,
CLSCTX_INPROC_SERVER,
IID_ITaskService,
(void**)&pService);
ExitOnFailure(hr, "Failed to create an instance of ITaskService: %x", hr);
// Connect to the task service.
hr = pService->Connect(_variant_t(), _variant_t(),
_variant_t(), _variant_t());
ExitOnFailure(hr, "ITaskService::Connect failed: %x", hr);
// ------------------------------------------------------
// Get the PowerToys task folder. Creates it if it doesn't exist.
hr = pService->GetFolder(_bstr_t(L"\\PowerToys"), &pTaskFolder);
if (FAILED(hr)) {
// Folder doesn't exist. Get the Root folder and create the PowerToys subfolder.
ITaskFolder *pRootFolder = NULL;
hr = pService->GetFolder(_bstr_t(L"\\"), &pRootFolder);
ExitOnFailure(hr, "Cannot get Root Folder pointer: %x", hr);
hr = pRootFolder->CreateFolder(_bstr_t(L"\\PowerToys"), _variant_t(L""), &pTaskFolder);
if (FAILED(hr)) {
pRootFolder->Release();
ExitOnFailure(hr, "Cannot create PowerToys task folder: %x", hr);
// ------------------------------------------------------
// Get the Domain/Username for the trigger.
//
// This action needs to run as the system to get elevated privileges from the installation,
// so GetUserNameEx can't be used to get the current user details.
// The USERNAME and USERDOMAIN environment variables are used instead.
if (!GetEnvironmentVariable(L"USERNAME", username, USERNAME_LEN))
{
ExitWithLastError(hr, "Getting username failed: %x", hr);
}
WcaLog(LOGMSG_STANDARD, "PowerToys task folder created.");
}
if (!GetEnvironmentVariable(L"USERDOMAIN", username_domain, USERNAME_DOMAIN_LEN))
{
ExitWithLastError(hr, "Getting the user's domain failed: %x", hr);
}
wcscat_s(username_domain, L"\\");
wcscat_s(username_domain, username);
// If the same task exists, remove it.
pTaskFolder->DeleteTask(_bstr_t(wstrTaskName.c_str()), 0);
WcaLog(LOGMSG_STANDARD, "Current user detected: %ls", username_domain);
// Create the task builder object to create the task.
hr = pService->NewTask(0, &pTask);
ExitOnFailure(hr, "Failed to create a task definition: %x", hr);
// Task Name.
wstrTaskName = L"Autorun for ";
wstrTaskName += username;
// ------------------------------------------------------
// Get the registration info for setting the identification.
hr = pTask->get_RegistrationInfo(&pRegInfo);
ExitOnFailure(hr, "Cannot get identification pointer: %x", hr);
hr = pRegInfo->put_Author(_bstr_t(username_domain));
ExitOnFailure(hr, "Cannot put identification info: %x", hr);
// Get the executable path passed to the custom action.
LPWSTR wszExecutablePath = NULL;
hr = WcaGetProperty(L"CustomActionData", &wszExecutablePath);
ExitOnFailure(hr, "Failed to get the executable path from CustomActionData.");
// ------------------------------------------------------
// Create the settings for the task
hr = pTask->get_Settings(&pSettings);
ExitOnFailure(hr, "Cannot get settings pointer: %x", hr);
// COM and Security Initialization is expected to have been done by the MSI.
// It couldn't be done in the DLL, anyway.
// ------------------------------------------------------
// Create an instance of the Task Service.
hr = CoCreateInstance(CLSID_TaskScheduler,
NULL,
CLSCTX_INPROC_SERVER,
IID_ITaskService,
(void**)&pService);
ExitOnFailure(hr, "Failed to create an instance of ITaskService: %x", hr);
hr = pSettings->put_StartWhenAvailable(VARIANT_FALSE);
ExitOnFailure(hr, "Cannot put_StartWhenAvailable setting info: %x", hr);
hr = pSettings->put_StopIfGoingOnBatteries(VARIANT_FALSE);
ExitOnFailure(hr, "Cannot put_StopIfGoingOnBatteries setting info: %x", hr);
hr = pSettings->put_ExecutionTimeLimit(_bstr_t(L"PT0S")); //Unlimited
ExitOnFailure(hr, "Cannot put_ExecutionTimeLimit setting info: %x", hr);
hr = pSettings->put_DisallowStartIfOnBatteries(VARIANT_FALSE);
ExitOnFailure(hr, "Cannot put_DisallowStartIfOnBatteries setting info: %x", hr);
// Connect to the task service.
hr = pService->Connect(_variant_t(), _variant_t(), _variant_t(), _variant_t());
ExitOnFailure(hr, "ITaskService::Connect failed: %x", hr);
// ------------------------------------------------------
// Get the trigger collection to insert the logon trigger.
hr = pTask->get_Triggers(&pTriggerCollection);
ExitOnFailure(hr, "Cannot get trigger collection: %x", hr);
// ------------------------------------------------------
// Get the PowerToys task folder. Creates it if it doesn't exist.
hr = pService->GetFolder(_bstr_t(L"\\PowerToys"), &pTaskFolder);
if (FAILED(hr))
{
// Folder doesn't exist. Get the Root folder and create the PowerToys subfolder.
ITaskFolder* pRootFolder = NULL;
hr = pService->GetFolder(_bstr_t(L"\\"), &pRootFolder);
ExitOnFailure(hr, "Cannot get Root Folder pointer: %x", hr);
hr = pRootFolder->CreateFolder(_bstr_t(L"\\PowerToys"), _variant_t(L""), &pTaskFolder);
if (FAILED(hr))
{
pRootFolder->Release();
ExitOnFailure(hr, "Cannot create PowerToys task folder: %x", hr);
}
WcaLog(LOGMSG_STANDARD, "PowerToys task folder created.");
}
// Add the logon trigger to the task.
ITrigger *pTrigger = NULL;
hr = pTriggerCollection->Create(TASK_TRIGGER_LOGON, &pTrigger);
ExitOnFailure(hr, "Cannot create the trigger: %x", hr);
// If the same task exists, remove it.
pTaskFolder->DeleteTask(_bstr_t(wstrTaskName.c_str()), 0);
ILogonTrigger *pLogonTrigger = NULL;
hr = pTrigger->QueryInterface(
IID_ILogonTrigger, (void**)&pLogonTrigger);
pTrigger->Release();
ExitOnFailure(hr, "QueryInterface call failed for ILogonTrigger: %x", hr);
// Create the task builder object to create the task.
hr = pService->NewTask(0, &pTask);
ExitOnFailure(hr, "Failed to create a task definition: %x", hr);
hr = pLogonTrigger->put_Id(_bstr_t(L"Trigger1"));
if (FAILED(hr)) {
WcaLogError(hr, "Cannot put the trigger ID: %x", hr);
}
// ------------------------------------------------------
// Get the registration info for setting the identification.
hr = pTask->get_RegistrationInfo(&pRegInfo);
ExitOnFailure(hr, "Cannot get identification pointer: %x", hr);
hr = pRegInfo->put_Author(_bstr_t(username_domain));
ExitOnFailure(hr, "Cannot put identification info: %x", hr);
// Timing issues may make explorer not be started when the task runs.
// Add a little delay to mitigate this.
hr = pLogonTrigger->put_Delay(_bstr_t(L"PT03S"));
if (FAILED(hr)) {
WcaLogError(hr, "Cannot put the trigger delay: %x", hr);
}
// ------------------------------------------------------
// Create the settings for the task
hr = pTask->get_Settings(&pSettings);
ExitOnFailure(hr, "Cannot get settings pointer: %x", hr);
// Define the user. The task will execute when the user logs on.
// The specified user must be a user on this computer.
hr = pLogonTrigger->put_UserId(_bstr_t(username_domain));
pLogonTrigger->Release();
ExitOnFailure(hr, "Cannot add user ID to logon trigger: %x", hr);
hr = pSettings->put_StartWhenAvailable(VARIANT_FALSE);
ExitOnFailure(hr, "Cannot put_StartWhenAvailable setting info: %x", hr);
hr = pSettings->put_StopIfGoingOnBatteries(VARIANT_FALSE);
ExitOnFailure(hr, "Cannot put_StopIfGoingOnBatteries setting info: %x", hr);
hr = pSettings->put_ExecutionTimeLimit(_bstr_t(L"PT0S")); //Unlimited
ExitOnFailure(hr, "Cannot put_ExecutionTimeLimit setting info: %x", hr);
hr = pSettings->put_DisallowStartIfOnBatteries(VARIANT_FALSE);
ExitOnFailure(hr, "Cannot put_DisallowStartIfOnBatteries setting info: %x", hr);
// ------------------------------------------------------
// Add an Action to the task. This task will execute the path passed to this custom action.
IActionCollection *pActionCollection = NULL;
// ------------------------------------------------------
// Get the trigger collection to insert the logon trigger.
hr = pTask->get_Triggers(&pTriggerCollection);
ExitOnFailure(hr, "Cannot get trigger collection: %x", hr);
// Get the task action collection pointer.
hr = pTask->get_Actions(&pActionCollection);
ExitOnFailure(hr, "Cannot get Task collection pointer: %x", hr);
// Add the logon trigger to the task.
ITrigger* pTrigger = NULL;
hr = pTriggerCollection->Create(TASK_TRIGGER_LOGON, &pTrigger);
ExitOnFailure(hr, "Cannot create the trigger: %x", hr);
// Create the action, specifying that it is an executable action.
IAction *pAction = NULL;
hr = pActionCollection->Create(TASK_ACTION_EXEC, &pAction);
pActionCollection->Release();
ExitOnFailure(hr, "Cannot create the action: %x", hr);
ILogonTrigger* pLogonTrigger = NULL;
hr = pTrigger->QueryInterface(
IID_ILogonTrigger, (void**)&pLogonTrigger);
pTrigger->Release();
ExitOnFailure(hr, "QueryInterface call failed for ILogonTrigger: %x", hr);
IExecAction *pExecAction = NULL;
// QI for the executable task pointer.
hr = pAction->QueryInterface(
IID_IExecAction, (void**)&pExecAction);
pAction->Release();
ExitOnFailure(hr, "QueryInterface call failed for IExecAction: %x", hr);
hr = pLogonTrigger->put_Id(_bstr_t(L"Trigger1"));
if (FAILED(hr))
{
WcaLogError(hr, "Cannot put the trigger ID: %x", hr);
}
// Set the path of the executable to PowerToys (passed as CustomActionData).
hr = pExecAction->put_Path(_bstr_t(wszExecutablePath));
pExecAction->Release();
ExitOnFailure(hr, "Cannot set path of executable: %x", hr);
// Timing issues may make explorer not be started when the task runs.
// Add a little delay to mitigate this.
hr = pLogonTrigger->put_Delay(_bstr_t(L"PT03S"));
if (FAILED(hr))
{
WcaLogError(hr, "Cannot put the trigger delay: %x", hr);
}
// ------------------------------------------------------
// Create the principal for the task
IPrincipal *pPrincipal = NULL;
hr = pTask->get_Principal(&pPrincipal);
ExitOnFailure(hr, "Cannot get principal pointer: %x", hr);
// Define the user. The task will execute when the user logs on.
// The specified user must be a user on this computer.
hr = pLogonTrigger->put_UserId(_bstr_t(username_domain));
pLogonTrigger->Release();
ExitOnFailure(hr, "Cannot add user ID to logon trigger: %x", hr);
// Set up principal information:
hr = pPrincipal->put_Id(_bstr_t(L"Principal1"));
if (FAILED(hr)) {
WcaLogError(hr, "Cannot put the principal ID: %x", hr);
}
// ------------------------------------------------------
// Add an Action to the task. This task will execute the path passed to this custom action.
IActionCollection* pActionCollection = NULL;
hr = pPrincipal->put_UserId(_bstr_t(username_domain));
if (FAILED(hr)) {
WcaLogError(hr, "Cannot put principal user Id: %x", hr);
}
// Get the task action collection pointer.
hr = pTask->get_Actions(&pActionCollection);
ExitOnFailure(hr, "Cannot get Task collection pointer: %x", hr);
hr = pPrincipal->put_LogonType(TASK_LOGON_INTERACTIVE_TOKEN);
if (FAILED(hr)) {
WcaLogError(hr, "Cannot put principal logon type: %x", hr);
}
// Create the action, specifying that it is an executable action.
IAction* pAction = NULL;
hr = pActionCollection->Create(TASK_ACTION_EXEC, &pAction);
pActionCollection->Release();
ExitOnFailure(hr, "Cannot create the action: %x", hr);
// Run the task with the highest available privileges.
hr = pPrincipal->put_RunLevel(TASK_RUNLEVEL_LUA);
pPrincipal->Release();
ExitOnFailure(hr, "Cannot put principal run level: %x", hr);
IExecAction* pExecAction = NULL;
// QI for the executable task pointer.
hr = pAction->QueryInterface(
IID_IExecAction, (void**)&pExecAction);
pAction->Release();
ExitOnFailure(hr, "QueryInterface call failed for IExecAction: %x", hr);
// ------------------------------------------------------
// Save the task in the PowerToys folder.
{
_variant_t SDDL_FULL_ACCESS_FOR_EVERYONE = L"D:(A;;FA;;;WD)";
hr = pTaskFolder->RegisterTaskDefinition(
_bstr_t(wstrTaskName.c_str()),
pTask,
TASK_CREATE_OR_UPDATE,
_variant_t(username_domain),
_variant_t(),
TASK_LOGON_INTERACTIVE_TOKEN,
SDDL_FULL_ACCESS_FOR_EVERYONE,
&pRegisteredTask);
ExitOnFailure(hr, "Error saving the Task : %x", hr);
}
// Set the path of the executable to PowerToys (passed as CustomActionData).
hr = pExecAction->put_Path(_bstr_t(wszExecutablePath));
pExecAction->Release();
ExitOnFailure(hr, "Cannot set path of executable: %x", hr);
WcaLog(LOGMSG_STANDARD, "Scheduled task created for the current user.");
// ------------------------------------------------------
// Create the principal for the task
IPrincipal* pPrincipal = NULL;
hr = pTask->get_Principal(&pPrincipal);
ExitOnFailure(hr, "Cannot get principal pointer: %x", hr);
// Set up principal information:
hr = pPrincipal->put_Id(_bstr_t(L"Principal1"));
if (FAILED(hr))
{
WcaLogError(hr, "Cannot put the principal ID: %x", hr);
}
hr = pPrincipal->put_UserId(_bstr_t(username_domain));
if (FAILED(hr))
{
WcaLogError(hr, "Cannot put principal user Id: %x", hr);
}
hr = pPrincipal->put_LogonType(TASK_LOGON_INTERACTIVE_TOKEN);
if (FAILED(hr))
{
WcaLogError(hr, "Cannot put principal logon type: %x", hr);
}
// Run the task with the highest available privileges.
hr = pPrincipal->put_RunLevel(TASK_RUNLEVEL_LUA);
pPrincipal->Release();
ExitOnFailure(hr, "Cannot put principal run level: %x", hr);
// ------------------------------------------------------
// Save the task in the PowerToys folder.
{
_variant_t SDDL_FULL_ACCESS_FOR_EVERYONE = L"D:(A;;FA;;;WD)";
hr = pTaskFolder->RegisterTaskDefinition(
_bstr_t(wstrTaskName.c_str()),
pTask,
TASK_CREATE_OR_UPDATE,
_variant_t(username_domain),
_variant_t(),
TASK_LOGON_INTERACTIVE_TOKEN,
SDDL_FULL_ACCESS_FOR_EVERYONE,
&pRegisteredTask);
ExitOnFailure(hr, "Error saving the Task : %x", hr);
}
WcaLog(LOGMSG_STANDARD, "Scheduled task created for the current user.");
LExit:
ReleaseStr(wszExecutablePath);
if (pService) pService->Release();
if (pTaskFolder) pTaskFolder->Release();
if (pTask) pTask->Release();
if (pRegInfo) pRegInfo->Release();
if (pSettings) pSettings->Release();
if (pTriggerCollection) pTriggerCollection->Release();
if (pRegisteredTask) pRegisteredTask->Release();
ReleaseStr(wszExecutablePath);
if (pService)
{
pService->Release();
}
if (pTaskFolder)
{
pTaskFolder->Release();
}
if (pTask)
{
pTask->Release();
}
if (pRegInfo)
{
pRegInfo->Release();
}
if (pSettings)
{
pSettings->Release();
}
if (pTriggerCollection)
{
pTriggerCollection->Release();
}
if (pRegisteredTask)
{
pRegisteredTask->Release();
}
if (!SUCCEEDED(hr)) {
PMSIHANDLE hRecord = MsiCreateRecord(0);
MsiRecordSetString(hRecord, 0, TEXT("Failed to create a scheduled task to start PowerToys at user login. You can re-try to create the scheduled task using the PowerToys settings."));
MsiProcessMessage(hInstall, INSTALLMESSAGE(INSTALLMESSAGE_WARNING + MB_OK), hRecord);
}
if (!SUCCEEDED(hr))
{
PMSIHANDLE hRecord = MsiCreateRecord(0);
MsiRecordSetString(hRecord, 0, TEXT("Failed to create a scheduled task to start PowerToys at user login. You can re-try to create the scheduled task using the PowerToys settings."));
MsiProcessMessage(hInstall, INSTALLMESSAGE(INSTALLMESSAGE_WARNING + MB_OK), hRecord);
}
er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
return WcaFinalize(er);
er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
return WcaFinalize(er);
}
// Removes all Scheduled Tasks in the PowerToys folder and deletes the folder afterwards.
// Based on the Task Scheduler Displaying Task Names and State example:
// https://docs.microsoft.com/en-us/windows/desktop/TaskSchd/displaying-task-names-and-state--c---/
UINT __stdcall RemoveScheduledTasksCA(MSIHANDLE hInstall) {
HRESULT hr = S_OK;
UINT er = ERROR_SUCCESS;
UINT __stdcall RemoveScheduledTasksCA(MSIHANDLE hInstall)
{
HRESULT hr = S_OK;
UINT er = ERROR_SUCCESS;
ITaskService *pService = NULL;
ITaskFolder *pTaskFolder = NULL;
IRegisteredTaskCollection* pTaskCollection = NULL;
ITaskService* pService = NULL;
ITaskFolder* pTaskFolder = NULL;
IRegisteredTaskCollection* pTaskCollection = NULL;
hr = WcaInitialize(hInstall, "RemoveScheduledTasksCA");
ExitOnFailure(hr, "Failed to initialize");
hr = WcaInitialize(hInstall, "RemoveScheduledTasksCA");
ExitOnFailure(hr, "Failed to initialize");
WcaLog(LOGMSG_STANDARD, "Initialized.");
WcaLog(LOGMSG_STANDARD, "Initialized.");
// COM and Security Initialization is expected to have been done by the MSI.
// It couldn't be done in the DLL, anyway.
// ------------------------------------------------------
// Create an instance of the Task Service.
hr = CoCreateInstance(CLSID_TaskScheduler,
NULL,
CLSCTX_INPROC_SERVER,
IID_ITaskService,
(void**)&pService);
ExitOnFailure(hr, "Failed to create an instance of ITaskService: %x", hr);
// COM and Security Initialization is expected to have been done by the MSI.
// It couldn't be done in the DLL, anyway.
// ------------------------------------------------------
// Create an instance of the Task Service.
hr = CoCreateInstance(CLSID_TaskScheduler,
NULL,
CLSCTX_INPROC_SERVER,
IID_ITaskService,
(void**)&pService);
ExitOnFailure(hr, "Failed to create an instance of ITaskService: %x", hr);
// Connect to the task service.
hr = pService->Connect(_variant_t(), _variant_t(),
_variant_t(), _variant_t());
ExitOnFailure(hr, "ITaskService::Connect failed: %x", hr);
// Connect to the task service.
hr = pService->Connect(_variant_t(), _variant_t(), _variant_t(), _variant_t());
ExitOnFailure(hr, "ITaskService::Connect failed: %x", hr);
// ------------------------------------------------------
// Get the PowerToys task folder.
hr = pService->GetFolder(_bstr_t(L"\\PowerToys"), &pTaskFolder);
if (FAILED(hr)) {
// Folder doesn't exist. No need to delete anything.
WcaLog(LOGMSG_STANDARD, "The PowerToys scheduled task folder wasn't found. Nothing to delete.");
hr = S_OK;
ExitFunction();
}
// -------------------------------------------------------
// Get the registered tasks in the folder.
hr = pTaskFolder->GetTasks(TASK_ENUM_HIDDEN, &pTaskCollection);
ExitOnFailure(hr, "Cannot get the registered tasks: %x", hr);
LONG numTasks = 0;
hr = pTaskCollection->get_Count(&numTasks);
for (LONG i = 0; i < numTasks; i++) {
// Delete all the tasks found.
// If some tasks can't be deleted, the folder won't be deleted later and the user will still be notified.
IRegisteredTask* pRegisteredTask = NULL;
hr = pTaskCollection->get_Item(_variant_t(i + 1), &pRegisteredTask);
if (SUCCEEDED(hr)) {
BSTR taskName = NULL;
hr = pRegisteredTask->get_Name(&taskName);
if (SUCCEEDED(hr)) {
hr = pTaskFolder->DeleteTask(taskName, NULL);
if (FAILED(hr)) {
WcaLogError(hr, "Cannot delete the '%S' task: %x", taskName, hr);
}
SysFreeString(taskName);
} else {
WcaLogError(hr, "Cannot get the registered task name: %x", hr);
}
pRegisteredTask->Release();
} else {
WcaLogError(hr, "Cannot get the registered task item at index=%d: %x", i + 1, hr);
// ------------------------------------------------------
// Get the PowerToys task folder.
hr = pService->GetFolder(_bstr_t(L"\\PowerToys"), &pTaskFolder);
if (FAILED(hr))
{
// Folder doesn't exist. No need to delete anything.
WcaLog(LOGMSG_STANDARD, "The PowerToys scheduled task folder wasn't found. Nothing to delete.");
hr = S_OK;
ExitFunction();
}
}
// ------------------------------------------------------
// Get the pointer to the root task folder and delete the PowerToys subfolder.
ITaskFolder *pRootFolder = NULL;
hr = pService->GetFolder(_bstr_t(L"\\"), &pRootFolder);
ExitOnFailure(hr, "Cannot get Root Folder pointer: %x", hr);
hr = pRootFolder->DeleteFolder(_bstr_t(L"PowerToys"), NULL);
pRootFolder->Release();
ExitOnFailure(hr, "Cannot delete the PowerToys folder: %x", hr);
// -------------------------------------------------------
// Get the registered tasks in the folder.
hr = pTaskFolder->GetTasks(TASK_ENUM_HIDDEN, &pTaskCollection);
ExitOnFailure(hr, "Cannot get the registered tasks: %x", hr);
WcaLog(LOGMSG_STANDARD, "Deleted the PowerToys Task Scheduler folder.");
LONG numTasks = 0;
hr = pTaskCollection->get_Count(&numTasks);
for (LONG i = 0; i < numTasks; i++)
{
// Delete all the tasks found.
// If some tasks can't be deleted, the folder won't be deleted later and the user will still be notified.
IRegisteredTask* pRegisteredTask = NULL;
hr = pTaskCollection->get_Item(_variant_t(i + 1), &pRegisteredTask);
if (SUCCEEDED(hr))
{
BSTR taskName = NULL;
hr = pRegisteredTask->get_Name(&taskName);
if (SUCCEEDED(hr))
{
hr = pTaskFolder->DeleteTask(taskName, NULL);
if (FAILED(hr))
{
WcaLogError(hr, "Cannot delete the '%S' task: %x", taskName, hr);
}
SysFreeString(taskName);
}
else
{
WcaLogError(hr, "Cannot get the registered task name: %x", hr);
}
pRegisteredTask->Release();
}
else
{
WcaLogError(hr, "Cannot get the registered task item at index=%d: %x", i + 1, hr);
}
}
// ------------------------------------------------------
// Get the pointer to the root task folder and delete the PowerToys subfolder.
ITaskFolder* pRootFolder = NULL;
hr = pService->GetFolder(_bstr_t(L"\\"), &pRootFolder);
ExitOnFailure(hr, "Cannot get Root Folder pointer: %x", hr);
hr = pRootFolder->DeleteFolder(_bstr_t(L"PowerToys"), NULL);
pRootFolder->Release();
ExitOnFailure(hr, "Cannot delete the PowerToys folder: %x", hr);
WcaLog(LOGMSG_STANDARD, "Deleted the PowerToys Task Scheduler folder.");
LExit:
if (pService) pService->Release();
if (pTaskFolder) pTaskFolder->Release();
if (pTaskCollection) pTaskCollection->Release();
if (pService)
{
pService->Release();
}
if (pTaskFolder)
{
pTaskFolder->Release();
}
if (pTaskCollection)
{
pTaskCollection->Release();
}
if (!SUCCEEDED(hr)) {
PMSIHANDLE hRecord = MsiCreateRecord(0);
MsiRecordSetString(hRecord, 0, TEXT("Failed to remove the PowerToys folder from the scheduled task. These can be removed manually later."));
MsiProcessMessage(hInstall, INSTALLMESSAGE(INSTALLMESSAGE_WARNING + MB_OK), hRecord);
}
if (!SUCCEEDED(hr))
{
PMSIHANDLE hRecord = MsiCreateRecord(0);
MsiRecordSetString(hRecord, 0, TEXT("Failed to remove the PowerToys folder from the scheduled task. These can be removed manually later."));
MsiProcessMessage(hInstall, INSTALLMESSAGE(INSTALLMESSAGE_WARNING + MB_OK), hRecord);
}
er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
return WcaFinalize(er);
er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
return WcaFinalize(er);
}
UINT __stdcall TelemetryLogInstallSuccessCA(MSIHANDLE hInstall) {
HRESULT hr = S_OK;
UINT er = ERROR_SUCCESS;
UINT __stdcall TelemetryLogInstallSuccessCA(MSIHANDLE hInstall)
{
HRESULT hr = S_OK;
UINT er = ERROR_SUCCESS;
hr = WcaInitialize(hInstall, "TelemetryLogInstallSuccessCA");
ExitOnFailure(hr, "Failed to initialize");
hr = WcaInitialize(hInstall, "TelemetryLogInstallSuccessCA");
ExitOnFailure(hr, "Failed to initialize");
TraceLoggingWrite(
g_hProvider,
"Install_Success",
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
TraceLoggingBoolean(TRUE, "UTCReplace_AppSessionGuid"),
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
TraceLoggingWrite(
g_hProvider,
"Install_Success",
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
TraceLoggingBoolean(TRUE, "UTCReplace_AppSessionGuid"),
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
LExit:
er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
return WcaFinalize(er);
er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
return WcaFinalize(er);
}
UINT __stdcall TelemetryLogInstallCancelCA(MSIHANDLE hInstall) {
HRESULT hr = S_OK;
UINT er = ERROR_SUCCESS;
UINT __stdcall TelemetryLogInstallCancelCA(MSIHANDLE hInstall)
{
HRESULT hr = S_OK;
UINT er = ERROR_SUCCESS;
hr = WcaInitialize(hInstall, "TelemetryLogInstallCancelCA");
ExitOnFailure(hr, "Failed to initialize");
hr = WcaInitialize(hInstall, "TelemetryLogInstallCancelCA");
ExitOnFailure(hr, "Failed to initialize");
TraceLoggingWrite(
g_hProvider,
"Install_Cancel",
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
TraceLoggingBoolean(TRUE, "UTCReplace_AppSessionGuid"),
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
TraceLoggingWrite(
g_hProvider,
"Install_Cancel",
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
TraceLoggingBoolean(TRUE, "UTCReplace_AppSessionGuid"),
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
LExit:
er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
return WcaFinalize(er);
er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
return WcaFinalize(er);
}
UINT __stdcall TelemetryLogInstallFailCA(MSIHANDLE hInstall) {
HRESULT hr = S_OK;
UINT er = ERROR_SUCCESS;
UINT __stdcall TelemetryLogInstallFailCA(MSIHANDLE hInstall)
{
HRESULT hr = S_OK;
UINT er = ERROR_SUCCESS;
hr = WcaInitialize(hInstall, "TelemetryLogInstallFailCA");
ExitOnFailure(hr, "Failed to initialize");
hr = WcaInitialize(hInstall, "TelemetryLogInstallFailCA");
ExitOnFailure(hr, "Failed to initialize");
TraceLoggingWrite(
g_hProvider,
"Install_Fail",
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
TraceLoggingBoolean(TRUE, "UTCReplace_AppSessionGuid"),
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
TraceLoggingWrite(
g_hProvider,
"Install_Fail",
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
TraceLoggingBoolean(TRUE, "UTCReplace_AppSessionGuid"),
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
LExit:
er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
return WcaFinalize(er);
er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
return WcaFinalize(er);
}
UINT __stdcall TelemetryLogUninstallSuccessCA(MSIHANDLE hInstall) {
HRESULT hr = S_OK;
UINT er = ERROR_SUCCESS;
UINT __stdcall TelemetryLogUninstallSuccessCA(MSIHANDLE hInstall)
{
HRESULT hr = S_OK;
UINT er = ERROR_SUCCESS;
hr = WcaInitialize(hInstall, "TelemetryLogUninstallSuccessCA");
ExitOnFailure(hr, "Failed to initialize");
hr = WcaInitialize(hInstall, "TelemetryLogUninstallSuccessCA");
ExitOnFailure(hr, "Failed to initialize");
TraceLoggingWrite(
g_hProvider,
"UnInstall_Success",
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
TraceLoggingBoolean(TRUE, "UTCReplace_AppSessionGuid"),
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
TraceLoggingWrite(
g_hProvider,
"UnInstall_Success",
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
TraceLoggingBoolean(TRUE, "UTCReplace_AppSessionGuid"),
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
LExit:
er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
return WcaFinalize(er);
er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
return WcaFinalize(er);
}
UINT __stdcall TelemetryLogUninstallCancelCA(MSIHANDLE hInstall) {
HRESULT hr = S_OK;
UINT er = ERROR_SUCCESS;
UINT __stdcall TelemetryLogUninstallCancelCA(MSIHANDLE hInstall)
{
HRESULT hr = S_OK;
UINT er = ERROR_SUCCESS;
hr = WcaInitialize(hInstall, "TelemetryLogUninstallCancelCA");
ExitOnFailure(hr, "Failed to initialize");
hr = WcaInitialize(hInstall, "TelemetryLogUninstallCancelCA");
ExitOnFailure(hr, "Failed to initialize");
TraceLoggingWrite(
g_hProvider,
"UnInstall_Cancel",
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
TraceLoggingBoolean(TRUE, "UTCReplace_AppSessionGuid"),
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
TraceLoggingWrite(
g_hProvider,
"UnInstall_Cancel",
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
TraceLoggingBoolean(TRUE, "UTCReplace_AppSessionGuid"),
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
LExit:
er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
return WcaFinalize(er);
er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
return WcaFinalize(er);
}
UINT __stdcall TelemetryLogUninstallFailCA(MSIHANDLE hInstall) {
HRESULT hr = S_OK;
UINT er = ERROR_SUCCESS;
UINT __stdcall TelemetryLogUninstallFailCA(MSIHANDLE hInstall)
{
HRESULT hr = S_OK;
UINT er = ERROR_SUCCESS;
hr = WcaInitialize(hInstall, "TelemetryLogUninstallFailCA");
ExitOnFailure(hr, "Failed to initialize");
hr = WcaInitialize(hInstall, "TelemetryLogUninstallFailCA");
ExitOnFailure(hr, "Failed to initialize");
TraceLoggingWrite(
g_hProvider,
"UnInstall_Fail",
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
TraceLoggingBoolean(TRUE, "UTCReplace_AppSessionGuid"),
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
TraceLoggingWrite(
g_hProvider,
"UnInstall_Fail",
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
TraceLoggingBoolean(TRUE, "UTCReplace_AppSessionGuid"),
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
LExit:
er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
return WcaFinalize(er);
er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
return WcaFinalize(er);
}
UINT __stdcall TelemetryLogRepairCancelCA(MSIHANDLE hInstall) {
HRESULT hr = S_OK;
UINT er = ERROR_SUCCESS;
UINT __stdcall TelemetryLogRepairCancelCA(MSIHANDLE hInstall)
{
HRESULT hr = S_OK;
UINT er = ERROR_SUCCESS;
hr = WcaInitialize(hInstall, "TelemetryLogRepairCancelCA");
ExitOnFailure(hr, "Failed to initialize");
hr = WcaInitialize(hInstall, "TelemetryLogRepairCancelCA");
ExitOnFailure(hr, "Failed to initialize");
TraceLoggingWrite(
g_hProvider,
"Repair_Cancel",
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
TraceLoggingBoolean(TRUE, "UTCReplace_AppSessionGuid"),
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
TraceLoggingWrite(
g_hProvider,
"Repair_Cancel",
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
TraceLoggingBoolean(TRUE, "UTCReplace_AppSessionGuid"),
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
LExit:
er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
return WcaFinalize(er);
er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
return WcaFinalize(er);
}
UINT __stdcall TelemetryLogRepairFailCA(MSIHANDLE hInstall) {
HRESULT hr = S_OK;
UINT er = ERROR_SUCCESS;
UINT __stdcall TelemetryLogRepairFailCA(MSIHANDLE hInstall)
{
HRESULT hr = S_OK;
UINT er = ERROR_SUCCESS;
hr = WcaInitialize(hInstall, "TelemetryLogRepairFailCA");
ExitOnFailure(hr, "Failed to initialize");
hr = WcaInitialize(hInstall, "TelemetryLogRepairFailCA");
ExitOnFailure(hr, "Failed to initialize");
TraceLoggingWrite(
g_hProvider,
"Repair_Fail",
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
TraceLoggingBoolean(TRUE, "UTCReplace_AppSessionGuid"),
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
TraceLoggingWrite(
g_hProvider,
"Repair_Fail",
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
TraceLoggingBoolean(TRUE, "UTCReplace_AppSessionGuid"),
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
LExit:
er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
return WcaFinalize(er);
er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
return WcaFinalize(er);
}
// DllMain - Initialize and cleanup WiX custom action utils.
extern "C" BOOL WINAPI DllMain(__in HINSTANCE hInst, __in ULONG ulReason, __in LPVOID) {
switch (ulReason) {
case DLL_PROCESS_ATTACH:
WcaGlobalInitialize(hInst);
TraceLoggingRegister(g_hProvider);
break;
extern "C" BOOL WINAPI DllMain(__in HINSTANCE hInst, __in ULONG ulReason, __in LPVOID)
{
switch (ulReason)
{
case DLL_PROCESS_ATTACH:
WcaGlobalInitialize(hInst);
TraceLoggingRegister(g_hProvider);
break;
case DLL_PROCESS_DETACH:
TraceLoggingUnregister(g_hProvider);
WcaGlobalFinalize();
break;
}
case DLL_PROCESS_DETACH:
TraceLoggingUnregister(g_hProvider);
WcaGlobalFinalize();
break;
}
return TRUE;
return TRUE;
}

View File

@ -364,27 +364,31 @@ WindowState get_window_state(HWND hwnd)
return RESTORED;
}
bool is_process_elevated()
bool is_process_elevated(const bool use_cached_value)
{
HANDLE token = nullptr;
bool elevated = false;
auto detection_func = []() {
HANDLE token = nullptr;
bool elevated = false;
if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token))
{
TOKEN_ELEVATION elevation;
DWORD size;
if (GetTokenInformation(token, TokenElevation, &elevation, sizeof(elevation), &size))
if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token))
{
elevated = (elevation.TokenIsElevated != 0);
TOKEN_ELEVATION elevation;
DWORD size;
if (GetTokenInformation(token, TokenElevation, &elevation, sizeof(elevation), &size))
{
elevated = (elevation.TokenIsElevated != 0);
}
}
}
if (token)
{
CloseHandle(token);
}
if (token)
{
CloseHandle(token);
}
return elevated;
return elevated;
};
static const bool cached_value = detection_func();
return use_cached_value ? cached_value : detection_func();
}
bool drop_elevated_privileges()

View File

@ -61,7 +61,7 @@ enum WindowState
WindowState get_window_state(HWND hwnd);
// Returns true if the current process is running with elevated privileges
bool is_process_elevated();
bool is_process_elevated(const bool use_cached_value = true);
// Drops the elevated privilages if present
bool drop_elevated_privileges();
@ -78,7 +78,7 @@ bool run_same_elevation(const std::wstring& file, const std::wstring& params);
// Returns true if the current process is running from administrator account
bool check_user_is_admin();
//Returns true when one or more strings from vector found in string
// Returns true when one or more strings from vector found in string
bool find_app_name_in_path(const std::wstring& where, const std::vector<std::wstring>& what);
// Get the executable path or module name for modern apps

View File

@ -84,6 +84,9 @@
<RunCodeAnalysis>false</RunCodeAnalysis>
<CodeAnalysisRuleSet>..\..\..\..\codeAnalysis\Rules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup>
<ApplicationIcon>images\FancyZonesEditor.ico</ApplicationIcon>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.ComponentModel.DataAnnotations" />
@ -256,5 +259,8 @@
<ItemGroup>
<Resource Include="images\Merge.png" />
</ItemGroup>
<ItemGroup>
<Resource Include="images\FancyZonesEditor.ico" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

View File

@ -101,7 +101,16 @@ namespace FancyZonesEditor
private int SplitterThickness
{
get { return Math.Max(((App)Application.Current).ZoneSettings.Spacing, 5); }
get
{
Settings settings = ((App)Application.Current).ZoneSettings;
if (!settings.ShowSpacing)
{
return 1;
}
return Math.Max(settings.Spacing, 1);
}
}
private void UpdateSplitter()

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

View File

@ -150,6 +150,7 @@ public:
void AddZoneWindow(HMONITOR monitor, PCWSTR deviceId) noexcept;
void MoveWindowIntoZoneByIndex(HWND window, HMONITOR monitor, int index) noexcept;
void MoveWindowIntoZoneByIndexSet(HWND window, HMONITOR monitor, const std::vector<int>& indexSet) noexcept;
protected:
static LRESULT CALLBACK s_WndProc(HWND, UINT, WPARAM, LPARAM) noexcept;
@ -680,7 +681,7 @@ void FancyZones::AddZoneWindow(HMONITOR monitor, PCWSTR deviceId) noexcept
//const bool flash = m_settings->GetSettings()->zoneSetChange_flashZones && newWorkArea;
const bool flash = false;
auto zoneWindow = MakeZoneWindow(this, m_hinstance, monitor, uniqueId, flash);
auto zoneWindow = MakeZoneWindow(this, m_hinstance, monitor, uniqueId, flash, newWorkArea);
if (zoneWindow)
{
m_zoneWindowMap[monitor] = std::move(zoneWindow);
@ -695,6 +696,12 @@ void FancyZones::AddZoneWindow(HMONITOR monitor, PCWSTR deviceId) noexcept
}
void FancyZones::MoveWindowIntoZoneByIndex(HWND window, HMONITOR monitor, int index) noexcept
{
std::shared_lock readLock(m_lock);
MoveWindowIntoZoneByIndexSet(window, monitor, { index });
}
void FancyZones::MoveWindowIntoZoneByIndexSet(HWND window, HMONITOR monitor, const std::vector<int>& indexSet) noexcept
{
std::shared_lock readLock(m_lock);
if (window != m_windowMoveSize)
@ -706,7 +713,7 @@ void FancyZones::MoveWindowIntoZoneByIndex(HWND window, HMONITOR monitor, int in
if (zoneWindow != m_zoneWindowMap.end())
{
const auto& zoneWindowPtr = zoneWindow->second;
zoneWindowPtr->MoveWindowIntoZoneByIndex(window, index);
zoneWindowPtr->MoveWindowIntoZoneByIndexSet(window, indexSet);
}
}
}
@ -860,10 +867,8 @@ void FancyZones::UpdateDragState(HWND window, require_write_lock) noexcept
m_dragEnabled = !(shift | mouse);
}
const bool windowElevated = IsProcessOfWindowElevated(window);
static const bool meElevated = is_process_elevated();
static bool warning_shown = false;
if (windowElevated && !meElevated)
if (!is_process_elevated() && IsProcessOfWindowElevated(window))
{
m_dragEnabled = false;
if (!warning_shown && !is_cant_drag_elevated_warning_disabled())

View File

@ -212,6 +212,11 @@ namespace JSONHelpers
customZoneSetsMap.clear();
activeDeviceId.clear();
}
inline void SetDeviceInfo(const std::wstring& deviceId, DeviceInfoData data)
{
deviceInfoMap[deviceId] = data;
}
#endif
inline void SetActiveDeviceId(const std::wstring& deviceId)

View File

@ -4,6 +4,7 @@
#include <common/monitors.h>
#include "Zone.h"
#include "Settings.h"
#include "util.h"
struct Zone : winrt::implements<Zone, IZone>
{
@ -20,6 +21,7 @@ public:
IFACEMETHODIMP_(void) RemoveWindowFromZone(HWND window, bool restoreSize) noexcept;
IFACEMETHODIMP_(void) SetId(size_t id) noexcept { m_id = id; }
IFACEMETHODIMP_(size_t) Id() noexcept { return m_id; }
IFACEMETHODIMP_(RECT) ComputeActualZoneRect(HWND window, HWND zoneWindow) noexcept;
private:
void SizeWindowToZone(HWND window, HWND zoneWindow) noexcept;
@ -61,12 +63,11 @@ IFACEMETHODIMP_(void) Zone::RemoveWindowFromZone(HWND window, bool restoreSize)
void Zone::SizeWindowToZone(HWND window, HWND zoneWindow) noexcept
{
// Skip invisible windows
if (!IsWindowVisible(window))
{
return;
}
SizeWindowToRect(window, ComputeActualZoneRect(window, zoneWindow));
}
RECT Zone::ComputeActualZoneRect(HWND window, HWND zoneWindow) noexcept
{
// Take care of 1px border
RECT newWindowRect = m_zoneRect;
@ -111,29 +112,7 @@ void Zone::SizeWindowToZone(HWND window, HWND zoneWindow) noexcept
newWindowRect.bottom = newWindowRect.top + (windowRect.bottom - windowRect.top);
}
WINDOWPLACEMENT placement{};
::GetWindowPlacement(window, &placement);
//wait if SW_SHOWMINIMIZED would be removed from window (Issue #1685)
for (int i = 0; i < 5 && (placement.showCmd & SW_SHOWMINIMIZED) != 0; i++)
{
std::this_thread::sleep_for(std::chrono::milliseconds(100));
::GetWindowPlacement(window, &placement);
}
// Do not restore minimized windows. We change their placement though so they restore to the correct zone.
if ((placement.showCmd & SW_SHOWMINIMIZED) == 0)
{
placement.showCmd = SW_RESTORE | SW_SHOWNA;
}
placement.rcNormalPosition = newWindowRect;
placement.flags |= WPF_ASYNCWINDOWPLACEMENT;
::SetWindowPlacement(window, &placement);
// Do it again, allowing Windows to resize the window and set correct scaling
// This fixes Issue #365
::SetWindowPlacement(window, &placement);
return newWindowRect;
}
void Zone::StampZone(HWND window, bool stamp) noexcept

View File

@ -42,9 +42,20 @@ interface __declspec(uuid("{8228E934-B6EF-402A-9892-15A1441BF8B0}")) IZone : pub
*/
IFACEMETHOD_(void, SetId)(size_t id) = 0;
/**
* @retirns Zone identifier.
* @returns Zone identifier.
*/
IFACEMETHOD_(size_t, Id)() = 0;
/**
* Compute the coordinates of the rectangle to which a window should be resized.
*
* @param window Handle of window which should be assigned to zone.
* @param zoneWindow The m_window of a ZoneWindow, it's a hidden window representing the
* current monitor desktop work area.
* @returns a RECT structure, describing global coordinates to which a window should be resized
*/
IFACEMETHOD_(RECT, ComputeActualZoneRect)(HWND window, HWND zoneWindow) = 0;
};
winrt::com_ptr<IZone> MakeZone(const RECT& zoneRect) noexcept;

View File

@ -122,14 +122,16 @@ public:
IFACEMETHODIMP_(JSONHelpers::ZoneSetLayoutType)
LayoutType() noexcept { return m_config.LayoutType; }
IFACEMETHODIMP AddZone(winrt::com_ptr<IZone> zone) noexcept;
IFACEMETHODIMP_(winrt::com_ptr<IZone>)
ZoneFromPoint(POINT pt) noexcept;
IFACEMETHODIMP_(std::vector<int>)
ZonesFromPoint(POINT pt) noexcept;
IFACEMETHODIMP_(int)
GetZoneIndexFromWindow(HWND window) noexcept;
IFACEMETHODIMP_(std::vector<winrt::com_ptr<IZone>>)
GetZones() noexcept { return m_zones; }
IFACEMETHODIMP_(void)
MoveWindowIntoZoneByIndex(HWND window, HWND zoneWindow, int index) noexcept;
MoveWindowIntoZoneByIndex(HWND window, HWND zoneWindow, int index, bool stampZone) noexcept;
IFACEMETHODIMP_(void)
MoveWindowIntoZoneByIndexSet(HWND window, HWND windowZone, const std::vector<int>& indexSet, bool stampZone) noexcept;
IFACEMETHODIMP_(bool)
MoveWindowIntoZoneByDirection(HWND window, HWND zoneWindow, DWORD vkCode, bool cycle) noexcept;
IFACEMETHODIMP_(void)
@ -162,41 +164,79 @@ IFACEMETHODIMP ZoneSet::AddZone(winrt::com_ptr<IZone> zone) noexcept
return S_OK;
}
IFACEMETHODIMP_(winrt::com_ptr<IZone>)
ZoneSet::ZoneFromPoint(POINT pt) noexcept
IFACEMETHODIMP_(std::vector<int>)
ZoneSet::ZonesFromPoint(POINT pt) noexcept
{
winrt::com_ptr<IZone> smallestKnownZone = nullptr;
// To reduce redundant calculations, we will store the last known zones area.
int smallestKnownZoneArea = INT32_MAX;
for (auto iter = m_zones.rbegin(); iter != m_zones.rend(); iter++)
const int SENSITIVITY_RADIUS = 20;
std::vector<int> capturedZones;
std::vector<int> strictlyCapturedZones;
for (size_t i = 0; i < m_zones.size(); i++)
{
if (winrt::com_ptr<IZone> zone = iter->try_as<IZone>())
auto zone = m_zones[i];
RECT newZoneRect = zone->GetZoneRect();
if (newZoneRect.left < newZoneRect.right && newZoneRect.top < newZoneRect.bottom) // proper zone
{
RECT* newZoneRect = &zone->GetZoneRect();
if (PtInRect(newZoneRect, pt))
if (newZoneRect.left - SENSITIVITY_RADIUS <= pt.x && pt.x <= newZoneRect.right + SENSITIVITY_RADIUS &&
newZoneRect.top - SENSITIVITY_RADIUS <= pt.y && pt.y <= newZoneRect.bottom + SENSITIVITY_RADIUS)
{
if (smallestKnownZone == nullptr)
{
smallestKnownZone = zone;
RECT* r = &smallestKnownZone->GetZoneRect();
smallestKnownZoneArea = (r->right - r->left) * (r->bottom - r->top);
}
else
{
int newZoneArea = (newZoneRect->right - newZoneRect->left) * (newZoneRect->bottom - newZoneRect->top);
if (newZoneArea < smallestKnownZoneArea)
{
smallestKnownZone = zone;
smallestKnownZoneArea = newZoneArea;
}
}
capturedZones.emplace_back(static_cast<int>(i));
}
if (newZoneRect.left <= pt.x && pt.x < newZoneRect.right &&
newZoneRect.top <= pt.y && pt.y < newZoneRect.bottom)
{
strictlyCapturedZones.emplace_back(static_cast<int>(i));
}
}
}
return smallestKnownZone;
// If only one zone is captured, but it's not strictly captured
// don't consider it as captured
if (capturedZones.size() == 1 && strictlyCapturedZones.size() == 0)
{
return {};
}
// If captured zones do not overlap, return all of them
// Otherwise, return the smallest one
bool overlap = false;
for (size_t i = 0; i < capturedZones.size(); ++i)
{
for (size_t j = i + 1; j < capturedZones.size(); ++j)
{
auto rectI = m_zones[capturedZones[i]]->GetZoneRect();
auto rectJ = m_zones[capturedZones[j]]->GetZoneRect();
if (max(rectI.top, rectJ.top) < min(rectI.bottom, rectJ.bottom) &&
max(rectI.left, rectJ.left) < min(rectI.right, rectJ.right))
{
overlap = true;
i = capturedZones.size() - 1;
break;
}
}
}
if (overlap)
{
size_t smallestIdx = 0;
for (size_t i = 1; i < capturedZones.size(); ++i)
{
auto rectS = m_zones[capturedZones[smallestIdx]]->GetZoneRect();
auto rectI = m_zones[capturedZones[i]]->GetZoneRect();
int smallestSize = (rectS.bottom - rectS.top) * (rectS.right - rectS.left);
int iSize = (rectI.bottom - rectI.top) * (rectI.right - rectI.left);
if (iSize <= smallestSize)
{
smallestIdx = i;
}
}
capturedZones = { capturedZones[smallestIdx] };
}
return capturedZones;
}
IFACEMETHODIMP_(int)
@ -217,7 +257,7 @@ ZoneSet::GetZoneIndexFromWindow(HWND window) noexcept
}
IFACEMETHODIMP_(void)
ZoneSet::MoveWindowIntoZoneByIndex(HWND window, HWND windowZone, int index) noexcept
ZoneSet::MoveWindowIntoZoneByIndex(HWND window, HWND windowZone, int index, bool stampZone) noexcept
{
if (m_zones.empty())
{
@ -236,7 +276,55 @@ ZoneSet::MoveWindowIntoZoneByIndex(HWND window, HWND windowZone, int index) noex
if (auto zone = m_zones.at(index))
{
zone->AddWindowToZone(window, windowZone, false);
zone->AddWindowToZone(window, windowZone, stampZone);
}
}
IFACEMETHODIMP_(void)
ZoneSet::MoveWindowIntoZoneByIndexSet(HWND window, HWND windowZone, const std::vector<int>& indexSet, bool stampZone) noexcept
{
if (m_zones.empty())
{
return;
}
while (auto zoneDrop = ZoneFromWindow(window))
{
zoneDrop->RemoveWindowFromZone(window, !IsZoomed(window));
}
if (indexSet.size() == 1)
{
MoveWindowIntoZoneByIndex(window, windowZone, indexSet[0], stampZone);
return;
}
RECT size;
bool sizeEmpty = true;
for (int index : indexSet)
{
if (index < static_cast<int>(m_zones.size()))
{
RECT newSize = m_zones.at(index)->ComputeActualZoneRect(window, windowZone);
if (!sizeEmpty)
{
size.left = min(size.left, newSize.left);
size.top = min(size.top, newSize.top);
size.right = max(size.right, newSize.right);
size.bottom = max(size.bottom, newSize.bottom);
}
else
{
size = newSize;
sizeEmpty = false;
}
}
}
if (!sizeEmpty)
{
SizeWindowToRect(window, size);
}
}
@ -306,10 +394,8 @@ ZoneSet::MoveWindowIntoZoneByPoint(HWND window, HWND zoneWindow, POINT ptClient)
zoneDrop->RemoveWindowFromZone(window, !IsZoomed(window));
}
if (auto zone = ZoneFromPoint(ptClient))
{
zone->AddWindowToZone(window, zoneWindow, true);
}
auto zones = ZonesFromPoint(ptClient);
MoveWindowIntoZoneByIndexSet(window, zoneWindow, zones, true);
}
IFACEMETHODIMP_(bool)

View File

@ -24,12 +24,12 @@ interface __declspec(uuid("{E4839EB7-669D-49CF-84A9-71A2DFD851A3}")) IZoneSet :
*/
IFACEMETHOD(AddZone)(winrt::com_ptr<IZone> zone) = 0;
/**
* Get zone from cursor coordinates.
* Get zones from cursor coordinates.
*
* @param pt Cursor coordinates.
* @returns Zone object (defining coordinates of the zone).
* @returns Vector of indices, corresponding to the current set of zones - the zones considered active.
*/
IFACEMETHOD_(winrt::com_ptr<IZone>, ZoneFromPoint)(POINT pt) = 0;
IFACEMETHOD_(std::vector<int>, ZonesFromPoint)(POINT pt) = 0;
/**
* Get index of the zone inside zone layout by window assigned to it.
*
@ -48,8 +48,20 @@ interface __declspec(uuid("{E4839EB7-669D-49CF-84A9-71A2DFD851A3}")) IZoneSet :
* @param zoneWindow The m_window of a ZoneWindow, it's a hidden window representing the
* current monitor desktop work area.
* @param index Zone index within zone layout.
* @param stampZone Whether the window being added to the zone should be stamped.
*/
IFACEMETHOD_(void, MoveWindowIntoZoneByIndex)(HWND window, HWND zoneWindow, int index) = 0;
IFACEMETHOD_(void, MoveWindowIntoZoneByIndex)(HWND window, HWND zoneWindow, int index, bool stampZone) = 0;
/**
* Assign window to the zones based on the set of zone indices inside zone layout.
*
* @param window Handle of window which should be assigned to zone.
* @param zoneWindow The m_window of a ZoneWindow, it's a hidden window representing the
* current monitor desktop work area.
* @param indexSet The set of zone indices within zone layout.
* @param stampZone Whether the window being added to the zone should be stamped,
in case a single window is to be added.
*/
IFACEMETHOD_(void, MoveWindowIntoZoneByIndexSet)(HWND window, HWND zoneWindow, const std::vector<int>& indexSet, bool stampZone) = 0;
/**
* Assign window to the zone based on direction (using WIN + LEFT/RIGHT arrow).
*

View File

@ -148,7 +148,7 @@ namespace ZoneWindowDrawUtils
COLORREF highlightColor,
int zoneOpacity,
const std::vector<winrt::com_ptr<IZone>>& zones,
const winrt::com_ptr<IZone>& highlightZone,
const std::vector<int>& highlightZones,
bool flashMode,
bool drawHints) noexcept
{
@ -158,15 +158,22 @@ namespace ZoneWindowDrawUtils
ColorSetting colorHighlight{ OpacitySettingToAlpha(zoneOpacity), 0, 255, 0, -2 };
ColorSetting const colorFlash{ OpacitySettingToAlpha(zoneOpacity), RGB(81, 92, 107), 200, RGB(104, 118, 138), -2 };
std::vector<bool> isHighlighted(zones.size(), false);
for (int x : highlightZones)
{
isHighlighted[x] = true;
}
for (auto iter = zones.begin(); iter != zones.end(); iter++)
{
int zoneId = static_cast<int>(iter - zones.begin());
winrt::com_ptr<IZone> zone = iter->try_as<IZone>();
if (!zone)
{
continue;
}
if (zone != highlightZone)
if (!isHighlighted[zoneId])
{
if (flashMode)
{
@ -182,13 +189,12 @@ namespace ZoneWindowDrawUtils
DrawZone(hdc, colorViewer, zone, zones, flashMode);
}
}
}
if (highlightZone)
{
colorHighlight.fill = highlightColor;
colorHighlight.border = zoneBorderColor;
DrawZone(hdc, colorHighlight, highlightZone, zones, flashMode);
else
{
colorHighlight.fill = highlightColor;
colorHighlight.border = zoneBorderColor;
DrawZone(hdc, colorHighlight, zone, zones, flashMode);
}
}
}
}
@ -199,7 +205,7 @@ public:
ZoneWindow(HINSTANCE hinstance);
~ZoneWindow();
bool Init(IZoneWindowHost* host, HINSTANCE hinstance, HMONITOR monitor, const std::wstring& uniqueId, bool flashZones);
bool Init(IZoneWindowHost* host, HINSTANCE hinstance, HMONITOR monitor, const std::wstring& uniqueId, bool flashZones, bool newWorkArea);
IFACEMETHODIMP MoveSizeEnter(HWND window, bool dragEnabled) noexcept;
IFACEMETHODIMP MoveSizeUpdate(POINT const& ptScreen, bool dragEnabled) noexcept;
@ -210,6 +216,8 @@ public:
IsDragEnabled() noexcept { return m_dragEnabled; }
IFACEMETHODIMP_(void)
MoveWindowIntoZoneByIndex(HWND window, int index) noexcept;
IFACEMETHODIMP_(void)
MoveWindowIntoZoneByIndexSet(HWND window, const std::vector<int>& indexSet) noexcept;
IFACEMETHODIMP_(bool)
MoveWindowIntoZoneByDirection(HWND window, DWORD vkCode, bool cycle) noexcept;
IFACEMETHODIMP_(void)
@ -232,13 +240,13 @@ protected:
private:
void LoadSettings() noexcept;
void InitializeZoneSets(MONITORINFO const& mi) noexcept;
void InitializeZoneSets(bool newWorkArea) noexcept;
void CalculateZoneSet() noexcept;
void UpdateActiveZoneSet(_In_opt_ IZoneSet* zoneSet) noexcept;
LRESULT WndProc(UINT message, WPARAM wparam, LPARAM lparam) noexcept;
void OnPaint(wil::unique_hdc& hdc) noexcept;
void OnKeyUp(WPARAM wparam) noexcept;
winrt::com_ptr<IZone> ZoneFromPoint(POINT pt) noexcept;
std::vector<int> ZonesFromPoint(POINT pt) noexcept;
void CycleActiveZoneSetInternal(DWORD wparam, Trace::ZoneWindow::InputMode mode) noexcept;
void FlashZones() noexcept;
@ -253,7 +261,7 @@ private:
bool m_dragEnabled{};
winrt::com_ptr<IZoneSet> m_activeZoneSet;
std::vector<winrt::com_ptr<IZoneSet>> m_zoneSets;
winrt::com_ptr<IZone> m_highlightZone;
std::vector<int> m_highlightZone;
WPARAM m_keyLast{};
size_t m_keyCycle{};
static const UINT m_showAnimationDuration = 200; // ms
@ -289,7 +297,7 @@ ZoneWindow::~ZoneWindow()
Gdiplus::GdiplusShutdown(gdiplusToken);
}
bool ZoneWindow::Init(IZoneWindowHost* host, HINSTANCE hinstance, HMONITOR monitor, const std::wstring& uniqueId, bool flashZones)
bool ZoneWindow::Init(IZoneWindowHost* host, HINSTANCE hinstance, HMONITOR monitor, const std::wstring& uniqueId, bool flashZones, bool newWorkArea)
{
m_host.copy_from(host);
@ -308,7 +316,7 @@ bool ZoneWindow::Init(IZoneWindowHost* host, HINSTANCE hinstance, HMONITOR monit
m_uniqueId = uniqueId;
LoadSettings();
InitializeZoneSets(mi);
InitializeZoneSets(newWorkArea);
m_window = wil::unique_hwnd{
CreateWindowExW(WS_EX_TOOLWINDOW, L"SuperFancyZones_ZoneWindow", L"", WS_POPUP, workAreaRect.left(), workAreaRect.top(), workAreaRect.width(), workAreaRect.height(), nullptr, nullptr, hinstance, this)
@ -363,7 +371,7 @@ IFACEMETHODIMP ZoneWindow::MoveSizeEnter(HWND window, bool dragEnabled) noexcept
m_dragEnabled = dragEnabled;
m_windowMoveSize = window;
m_drawHints = true;
m_highlightZone = nullptr;
m_highlightZone = {};
ShowZoneWindow();
return S_OK;
}
@ -378,13 +386,13 @@ IFACEMETHODIMP ZoneWindow::MoveSizeUpdate(POINT const& ptScreen, bool dragEnable
if (dragEnabled)
{
auto highlightZone = ZoneFromPoint(ptClient);
auto highlightZone = ZonesFromPoint(ptClient);
redraw = (highlightZone != m_highlightZone);
m_highlightZone = std::move(highlightZone);
}
else if (m_highlightZone)
else if (m_highlightZone.size())
{
m_highlightZone = nullptr;
m_highlightZone = {};
redraw = true;
}
@ -432,10 +440,16 @@ ZoneWindow::RestoreOrginalTransparency() noexcept
IFACEMETHODIMP_(void)
ZoneWindow::MoveWindowIntoZoneByIndex(HWND window, int index) noexcept
{
MoveWindowIntoZoneByIndexSet(window, { index });
}
IFACEMETHODIMP_(void)
ZoneWindow::MoveWindowIntoZoneByIndexSet(HWND window, const std::vector<int>& indexSet) noexcept
{
if (m_activeZoneSet)
{
m_activeZoneSet->MoveWindowIntoZoneByIndex(window, m_window.get(), index);
m_activeZoneSet->MoveWindowIntoZoneByIndexSet(window, m_window.get(), indexSet, false);
}
}
@ -518,7 +532,7 @@ ZoneWindow::HideZoneWindow() noexcept
m_keyLast = 0;
m_windowMoveSize = nullptr;
m_drawHints = false;
m_highlightZone = nullptr;
m_highlightZone = {};
}
}
@ -529,10 +543,10 @@ void ZoneWindow::LoadSettings() noexcept
JSONHelpers::FancyZonesDataInstance().AddDevice(m_uniqueId);
}
void ZoneWindow::InitializeZoneSets(MONITORINFO const& mi) noexcept
void ZoneWindow::InitializeZoneSets(bool newWorkArea) noexcept
{
auto parent = m_host->GetParentZoneWindow(m_monitor);
if (parent)
if (newWorkArea && parent)
{
// Update device info with device info from parent virtual desktop (if empty).
JSONHelpers::FancyZonesDataInstance().CloneDeviceInfo(parent->UniqueId(), m_uniqueId);
@ -685,13 +699,13 @@ void ZoneWindow::OnKeyUp(WPARAM wparam) noexcept
}
}
winrt::com_ptr<IZone> ZoneWindow::ZoneFromPoint(POINT pt) noexcept
std::vector<int> ZoneWindow::ZonesFromPoint(POINT pt) noexcept
{
if (m_activeZoneSet)
{
return m_activeZoneSet->ZoneFromPoint(pt);
return m_activeZoneSet->ZonesFromPoint(pt);
}
return nullptr;
return {};
}
void ZoneWindow::CycleActiveZoneSetInternal(DWORD wparam, Trace::ZoneWindow::InputMode mode) noexcept
@ -739,7 +753,7 @@ void ZoneWindow::CycleActiveZoneSetInternal(DWORD wparam, Trace::ZoneWindow::Inp
{
m_host->MoveWindowsOnActiveZoneSetChange();
}
m_highlightZone = nullptr;
m_highlightZone = {};
}
void ZoneWindow::FlashZones() noexcept
@ -773,10 +787,10 @@ LRESULT CALLBACK ZoneWindow::s_WndProc(HWND window, UINT message, WPARAM wparam,
DefWindowProc(window, message, wparam, lparam);
}
winrt::com_ptr<IZoneWindow> MakeZoneWindow(IZoneWindowHost* host, HINSTANCE hinstance, HMONITOR monitor, const std::wstring& uniqueId, bool flashZones) noexcept
winrt::com_ptr<IZoneWindow> MakeZoneWindow(IZoneWindowHost* host, HINSTANCE hinstance, HMONITOR monitor, const std::wstring& uniqueId, bool flashZones, bool newWorkArea) noexcept
{
auto self = winrt::make_self<ZoneWindow>(hinstance);
if (self->Init(host, hinstance, monitor, uniqueId, flashZones))
if (self->Init(host, hinstance, monitor, uniqueId, flashZones, newWorkArea))
{
return self;
}

View File

@ -53,6 +53,13 @@ interface __declspec(uuid("{7F017528-8110-4FB3-BE41-F472969C2560}")) IZoneWindow
* @param index Zone index within zone layout.
*/
IFACEMETHOD_(void, MoveWindowIntoZoneByIndex)(HWND window, int index) = 0;
/**
* Assign window to the zones based on the set of zone indices inside zone layout.
*
* @param window Handle of window which should be assigned to zone.
* @param indexSet The set of zone indices within zone layout.
*/
IFACEMETHOD_(void, MoveWindowIntoZoneByIndexSet)(HWND window, const std::vector<int>& indexSet) = 0;
/**
* Assign window to the zone based on direction (using WIN + LEFT/RIGHT arrow).
*
@ -98,4 +105,4 @@ interface __declspec(uuid("{7F017528-8110-4FB3-BE41-F472969C2560}")) IZoneWindow
};
winrt::com_ptr<IZoneWindow> MakeZoneWindow(IZoneWindowHost* host, HINSTANCE hinstance, HMONITOR monitor,
const std::wstring& uniqueId, bool flashZones) noexcept;
const std::wstring& uniqueId, bool flashZones, bool newWorkArea) noexcept;

View File

@ -108,3 +108,30 @@ void OrderMonitors(std::vector<std::pair<HMONITOR, RECT>>& monitorInfo)
monitorInfo = std::move(sortedMonitorInfo);
}
void SizeWindowToRect(HWND window, RECT rect) noexcept
{
WINDOWPLACEMENT placement{};
::GetWindowPlacement(window, &placement);
//wait if SW_SHOWMINIMIZED would be removed from window (Issue #1685)
for (int i = 0; i < 5 && (placement.showCmd & SW_SHOWMINIMIZED) != 0; i++)
{
std::this_thread::sleep_for(std::chrono::milliseconds(100));
::GetWindowPlacement(window, &placement);
}
// Do not restore minimized windows. We change their placement though so they restore to the correct zone.
if ((placement.showCmd & SW_SHOWMINIMIZED) == 0)
{
placement.showCmd = SW_RESTORE | SW_SHOWNA;
}
placement.rcNormalPosition = rect;
placement.flags |= WPF_ASYNCWINDOWPLACEMENT;
::SetWindowPlacement(window, &placement);
// Do it again, allowing Windows to resize the window and set correct scaling
// This fixes Issue #365
::SetWindowPlacement(window, &placement);
}

View File

@ -118,3 +118,4 @@ inline BYTE OpacitySettingToAlpha(int opacity)
UINT GetDpiForMonitor(HMONITOR monitor) noexcept;
void OrderMonitors(std::vector<std::pair<HMONITOR, RECT>>& monitorInfo);
void SizeWindowToRect(HWND window, RECT rect) noexcept;

View File

@ -132,8 +132,8 @@ namespace FancyZonesUnitTests
TEST_METHOD (ZoneFromPointEmpty)
{
auto actual = m_set->ZoneFromPoint(POINT{ 0, 0 });
Assert::IsTrue(nullptr == actual);
auto actual = m_set->ZonesFromPoint(POINT{ 0, 0 });
Assert::IsTrue(actual.size() == 0);
}
TEST_METHOD (ZoneFromPointInner)
@ -146,9 +146,9 @@ namespace FancyZonesUnitTests
{
for (int j = top + 1; j < bottom; j++)
{
auto actual = m_set->ZoneFromPoint(POINT{ i, j });
Assert::IsTrue(actual != nullptr);
compareZones(expected, actual);
auto actual = m_set->ZonesFromPoint(POINT{ i, j });
Assert::IsTrue(actual.size() == 1);
compareZones(expected, m_set->GetZones()[actual[0]]);
}
}
}
@ -161,29 +161,29 @@ namespace FancyZonesUnitTests
for (int i = left; i < right; i++)
{
auto actual = m_set->ZoneFromPoint(POINT{ i, top });
Assert::IsTrue(actual != nullptr);
compareZones(expected, actual);
auto actual = m_set->ZonesFromPoint(POINT{ i, top });
Assert::IsTrue(actual.size() == 1);
compareZones(expected, m_set->GetZones()[actual[0]]);
}
for (int i = top; i < bottom; i++)
{
auto actual = m_set->ZoneFromPoint(POINT{ left, i });
Assert::IsTrue(actual != nullptr);
compareZones(expected, actual);
auto actual = m_set->ZonesFromPoint(POINT{ left, i });
Assert::IsTrue(actual.size() == 1);
compareZones(expected, m_set->GetZones()[actual[0]]);
}
//bottom and right borders considered to be outside
for (int i = left; i < right; i++)
{
auto actual = m_set->ZoneFromPoint(POINT{ i, bottom });
Assert::IsTrue(nullptr == actual);
auto actual = m_set->ZonesFromPoint(POINT{ i, bottom });
Assert::IsTrue(actual.size() == 0);
}
for (int i = top; i < bottom; i++)
{
auto actual = m_set->ZoneFromPoint(POINT{ right, i });
Assert::IsTrue(nullptr == actual);
auto actual = m_set->ZonesFromPoint(POINT{ right, i });
Assert::IsTrue(actual.size() == 0);
}
}
@ -193,8 +193,8 @@ namespace FancyZonesUnitTests
winrt::com_ptr<IZone> zone = MakeZone({ left, top, right, bottom });
m_set->AddZone(zone);
auto actual = m_set->ZoneFromPoint(POINT{ 101, 101 });
Assert::IsTrue(actual == nullptr);
auto actual = m_set->ZonesFromPoint(POINT{ 200, 200 });
Assert::IsTrue(actual.size() == 0);
}
TEST_METHOD (ZoneFromPointOverlapping)
@ -208,9 +208,65 @@ namespace FancyZonesUnitTests
winrt::com_ptr<IZone> zone4 = MakeZone({ 10, 10, 50, 50 });
m_set->AddZone(zone4);
auto actual = m_set->ZoneFromPoint(POINT{ 50, 50 });
Assert::IsTrue(actual != nullptr);
compareZones(zone2, actual);
// zone4 is expected because it's the smallest one, and it's considered to be inside
// since Multizones support
auto actual = m_set->ZonesFromPoint(POINT{ 50, 50 });
Assert::IsTrue(actual.size() == 1);
compareZones(zone4, m_set->GetZones()[actual[0]]);
}
TEST_METHOD (ZoneFromPointMultizoneHorizontal)
{
winrt::com_ptr<IZone> zone1 = MakeZone({ 0, 0, 100, 100 });
m_set->AddZone(zone1);
winrt::com_ptr<IZone> zone2 = MakeZone({ 100, 0, 200, 100 });
m_set->AddZone(zone2);
winrt::com_ptr<IZone> zone3 = MakeZone({ 0, 100, 100, 200 });
m_set->AddZone(zone3);
winrt::com_ptr<IZone> zone4 = MakeZone({ 100, 100, 200, 200 });
m_set->AddZone(zone4);
auto actual = m_set->ZonesFromPoint(POINT{ 50, 100 });
Assert::IsTrue(actual.size() == 2);
compareZones(zone1, m_set->GetZones()[actual[0]]);
compareZones(zone3, m_set->GetZones()[actual[1]]);
}
TEST_METHOD (ZoneFromPointMultizoneVertical)
{
winrt::com_ptr<IZone> zone1 = MakeZone({ 0, 0, 100, 100 });
m_set->AddZone(zone1);
winrt::com_ptr<IZone> zone2 = MakeZone({ 100, 0, 200, 100 });
m_set->AddZone(zone2);
winrt::com_ptr<IZone> zone3 = MakeZone({ 0, 100, 100, 200 });
m_set->AddZone(zone3);
winrt::com_ptr<IZone> zone4 = MakeZone({ 100, 100, 200, 200 });
m_set->AddZone(zone4);
auto actual = m_set->ZonesFromPoint(POINT{ 100, 50 });
Assert::IsTrue(actual.size() == 2);
compareZones(zone1, m_set->GetZones()[actual[0]]);
compareZones(zone2, m_set->GetZones()[actual[1]]);
}
TEST_METHOD(ZoneFromPointMultizoneQuad)
{
winrt::com_ptr<IZone> zone1 = MakeZone({ 0, 0, 100, 100 });
m_set->AddZone(zone1);
winrt::com_ptr<IZone> zone2 = MakeZone({ 100, 0, 200, 100 });
m_set->AddZone(zone2);
winrt::com_ptr<IZone> zone3 = MakeZone({ 0, 100, 100, 200 });
m_set->AddZone(zone3);
winrt::com_ptr<IZone> zone4 = MakeZone({ 100, 100, 200, 200 });
m_set->AddZone(zone4);
auto actual = m_set->ZonesFromPoint(POINT{ 100, 100 });
Assert::IsTrue(actual.size() == 4);
compareZones(zone1, m_set->GetZones()[actual[0]]);
compareZones(zone2, m_set->GetZones()[actual[1]]);
compareZones(zone3, m_set->GetZones()[actual[2]]);
compareZones(zone4, m_set->GetZones()[actual[3]]);
}
TEST_METHOD (ZoneFromPointWithNotNormalizedRect)
@ -218,8 +274,8 @@ namespace FancyZonesUnitTests
winrt::com_ptr<IZone> zone = MakeZone({ 100, 100, 0, 0 });
m_set->AddZone(zone);
auto actual = m_set->ZoneFromPoint(POINT{ 50, 50 });
Assert::IsTrue(actual == nullptr);
auto actual = m_set->ZonesFromPoint(POINT{ 50, 50 });
Assert::IsTrue(actual.size() == 0);
}
TEST_METHOD (ZoneFromPointWithZeroRect)
@ -227,8 +283,8 @@ namespace FancyZonesUnitTests
winrt::com_ptr<IZone> zone = MakeZone({ 0, 0, 0, 0 });
m_set->AddZone(zone);
auto actual = m_set->ZoneFromPoint(POINT{ 0, 0 });
Assert::IsTrue(actual == nullptr);
auto actual = m_set->ZonesFromPoint(POINT{ 0, 0 });
Assert::IsTrue(actual.size() == 0);
}
TEST_METHOD (ZoneIndexFromWindow)
@ -316,7 +372,7 @@ namespace FancyZonesUnitTests
m_set->AddZone(zone3);
HWND window = Mocks::Window();
m_set->MoveWindowIntoZoneByIndex(window, Mocks::Window(), 1);
m_set->MoveWindowIntoZoneByIndex(window, Mocks::Window(), 1, false);
Assert::IsFalse(zone1->ContainsWindow(window));
Assert::IsTrue(zone2->ContainsWindow(window));
Assert::IsFalse(zone3->ContainsWindow(window));
@ -325,7 +381,7 @@ namespace FancyZonesUnitTests
TEST_METHOD (MoveWindowIntoZoneByIndexWithNoZones)
{
HWND window = Mocks::Window();
m_set->MoveWindowIntoZoneByIndex(window, Mocks::Window(), 0);
m_set->MoveWindowIntoZoneByIndex(window, Mocks::Window(), 0, false);
}
TEST_METHOD (MoveWindowIntoZoneByIndexWithInvalidIndex)
@ -338,7 +394,7 @@ namespace FancyZonesUnitTests
m_set->AddZone(zone3);
HWND window = Mocks::Window();
m_set->MoveWindowIntoZoneByIndex(window, Mocks::Window(), 100);
m_set->MoveWindowIntoZoneByIndex(window, Mocks::Window(), 100, false);
Assert::IsFalse(zone1->ContainsWindow(window));
Assert::IsFalse(zone2->ContainsWindow(window));
Assert::IsFalse(zone3->ContainsWindow(window));
@ -355,17 +411,17 @@ namespace FancyZonesUnitTests
m_set->AddZone(zone3);
HWND window = Mocks::Window();
m_set->MoveWindowIntoZoneByIndex(window, Mocks::Window(), 0);
m_set->MoveWindowIntoZoneByIndex(window, Mocks::Window(), 0, false);
Assert::IsTrue(zone1->ContainsWindow(window));
Assert::IsFalse(zone2->ContainsWindow(window));
Assert::IsFalse(zone3->ContainsWindow(window));
m_set->MoveWindowIntoZoneByIndex(window, Mocks::Window(), 1);
m_set->MoveWindowIntoZoneByIndex(window, Mocks::Window(), 1, false);
Assert::IsFalse(zone1->ContainsWindow(window));
Assert::IsTrue(zone2->ContainsWindow(window));
Assert::IsFalse(zone3->ContainsWindow(window));
m_set->MoveWindowIntoZoneByIndex(window, Mocks::Window(), 2);
m_set->MoveWindowIntoZoneByIndex(window, Mocks::Window(), 2, false);
Assert::IsFalse(zone1->ContainsWindow(window));
Assert::IsFalse(zone2->ContainsWindow(window));
Assert::IsTrue(zone3->ContainsWindow(window));
@ -382,9 +438,9 @@ namespace FancyZonesUnitTests
m_set->AddZone(zone3);
HWND window = Mocks::Window();
m_set->MoveWindowIntoZoneByIndex(window, Mocks::Window(), 0);
m_set->MoveWindowIntoZoneByIndex(window, Mocks::Window(), 0);
m_set->MoveWindowIntoZoneByIndex(window, Mocks::Window(), 0);
m_set->MoveWindowIntoZoneByIndex(window, Mocks::Window(), 0, false);
m_set->MoveWindowIntoZoneByIndex(window, Mocks::Window(), 0, false);
m_set->MoveWindowIntoZoneByIndex(window, Mocks::Window(), 0, false);
Assert::IsTrue(zone1->ContainsWindow(window));
Assert::IsFalse(zone2->ContainsWindow(window));
Assert::IsFalse(zone3->ContainsWindow(window));
@ -401,7 +457,7 @@ namespace FancyZonesUnitTests
m_set->AddZone(zone1);
auto window = Mocks::Window();
m_set->MoveWindowIntoZoneByPoint(window, Mocks::Window(), POINT{ 101, 101 });
m_set->MoveWindowIntoZoneByPoint(window, Mocks::Window(), POINT{ 200, 200 });
Assert::IsFalse(zone1->ContainsWindow(window));
}

View File

@ -55,6 +55,7 @@ namespace FancyZonesUnitTests
{
const std::wstring m_deviceId = L"\\\\?\\DISPLAY#DELA026#5&10a58c63&0&UID16777488#{e6f07b5f-ee97-4a90-b076-33f57bf4eaa7}";
const std::wstring m_virtualDesktopId = L"MyVirtualDesktopId";
std::wstringstream m_parentUniqueId;
std::wstringstream m_uniqueId;
HINSTANCE m_hInst{};
@ -75,6 +76,7 @@ namespace FancyZonesUnitTests
m_monitorInfo.cbSize = sizeof(m_monitorInfo);
Assert::AreNotEqual(0, GetMonitorInfoW(m_monitor, &m_monitorInfo));
m_parentUniqueId << L"DELA026#5&10a58c63&0&UID16777488_" << m_monitorInfo.rcMonitor.right << "_" << m_monitorInfo.rcMonitor.bottom << "_{61FA9FC0-26A6-4B37-A834-491C148DFC57}";
m_uniqueId << L"DELA026#5&10a58c63&0&UID16777488_" << m_monitorInfo.rcMonitor.right << "_" << m_monitorInfo.rcMonitor.bottom << "_{39B25DD2-130D-4B5D-8851-4791D66B1539}";
Assert::IsFalse(ZoneWindowUtils::GetActiveZoneSetTmpPath().empty());
@ -113,7 +115,7 @@ namespace FancyZonesUnitTests
m_fancyZonesData.ParseDeviceInfoFromTmpFile(activeZoneSetTempPath);
return MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false);
return MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false, false);
}
void testZoneWindow(winrt::com_ptr<IZoneWindow> zoneWindow)
@ -129,14 +131,14 @@ namespace FancyZonesUnitTests
public:
TEST_METHOD(CreateZoneWindow)
{
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false);
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false, false);
testZoneWindow(m_zoneWindow);
Assert::IsNull(m_zoneWindow->ActiveZoneSet());
}
TEST_METHOD(CreateZoneWindowNoHinst)
{
m_zoneWindow = MakeZoneWindow(m_hostPtr, {}, m_monitor, m_uniqueId.str(), false);
m_zoneWindow = MakeZoneWindow(m_hostPtr, {}, m_monitor, m_uniqueId.str(), false, false);
testZoneWindow(m_zoneWindow);
Assert::IsNull(m_zoneWindow->ActiveZoneSet());
@ -144,7 +146,7 @@ namespace FancyZonesUnitTests
TEST_METHOD(CreateZoneWindowNoHinstFlashZones)
{
m_zoneWindow = MakeZoneWindow(m_hostPtr, {}, m_monitor, m_uniqueId.str(), true);
m_zoneWindow = MakeZoneWindow(m_hostPtr, {}, m_monitor, m_uniqueId.str(), true, false);
testZoneWindow(m_zoneWindow);
Assert::IsNull(m_zoneWindow->ActiveZoneSet());
@ -152,7 +154,7 @@ namespace FancyZonesUnitTests
TEST_METHOD(CreateZoneWindowNoMonitor)
{
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, {}, m_uniqueId.str(), false);
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, {}, m_uniqueId.str(), false, false);
Assert::IsNull(m_zoneWindow.get());
Assert::IsNotNull(m_hostPtr);
@ -160,7 +162,7 @@ namespace FancyZonesUnitTests
TEST_METHOD(CreateZoneWindowNoMonitorFlashZones)
{
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, {}, m_uniqueId.str(), true);
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, {}, m_uniqueId.str(), true, false);
Assert::IsNull(m_zoneWindow.get());
Assert::IsNotNull(m_hostPtr);
@ -170,7 +172,7 @@ namespace FancyZonesUnitTests
{
// Generate unique id without device id
std::wstring uniqueId = ZoneWindowUtils::GenerateUniqueId(m_monitor, nullptr, m_virtualDesktopId.c_str());
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, uniqueId, false);
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, uniqueId, false, false);
const std::wstring expectedWorkArea = std::to_wstring(m_monitorInfo.rcMonitor.right) + L"_" + std::to_wstring(m_monitorInfo.rcMonitor.bottom);
const std::wstring expectedUniqueId = L"FallbackDevice_" + std::to_wstring(m_monitorInfo.rcMonitor.right) + L"_" + std::to_wstring(m_monitorInfo.rcMonitor.bottom) + L"_" + m_virtualDesktopId;
@ -186,7 +188,7 @@ namespace FancyZonesUnitTests
{
// Generate unique id without virtual desktop id
std::wstring uniqueId = ZoneWindowUtils::GenerateUniqueId(m_monitor, m_deviceId.c_str(), nullptr);
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, uniqueId, false);
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, uniqueId, false, false);
const std::wstring expectedWorkArea = std::to_wstring(m_monitorInfo.rcMonitor.right) + L"_" + std::to_wstring(m_monitorInfo.rcMonitor.bottom);
Assert::IsNotNull(m_zoneWindow.get());
@ -213,7 +215,7 @@ namespace FancyZonesUnitTests
m_fancyZonesData.ParseDeviceInfoFromTmpFile(activeZoneSetTempPath);
//temp file read on initialization
auto actual = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false);
auto actual = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false, false);
testZoneWindow(actual);
@ -237,7 +239,7 @@ namespace FancyZonesUnitTests
m_fancyZonesData.ParseDeviceInfoFromTmpFile(activeZoneSetTempPath);
//temp file read on initialization
auto actual = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false);
auto actual = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false, false);
testZoneWindow(actual);
@ -273,7 +275,7 @@ namespace FancyZonesUnitTests
m_fancyZonesData.ParseCustomZoneSetFromTmpFile(appliedZoneSetTempPath);
//temp file read on initialization
auto actual = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false);
auto actual = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false, false);
testZoneWindow(actual);
@ -320,7 +322,7 @@ namespace FancyZonesUnitTests
m_fancyZonesData.ParseCustomZoneSetFromTmpFile(appliedZoneSetTempPath);
//temp file read on initialization
auto actual = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false);
auto actual = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false, false);
testZoneWindow(actual);
@ -367,7 +369,7 @@ namespace FancyZonesUnitTests
m_fancyZonesData.ParseCustomZoneSetFromTmpFile(appliedZoneSetTempPath);
//temp file read on initialization
auto actual = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false);
auto actual = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false, false);
testZoneWindow(actual);
@ -376,9 +378,68 @@ namespace FancyZonesUnitTests
Assert::AreEqual((size_t)1, actualZoneSet.size());
}
TEST_METHOD (CreateZoneWindowClonedFromParent)
{
using namespace JSONHelpers;
const ZoneSetLayoutType type = ZoneSetLayoutType::PriorityGrid;
const int spacing = 10;
const int zoneCount = 5;
const auto customSetGuid = Helpers::CreateGuidString();
const auto parentZoneSet = ZoneSetData{ customSetGuid, type };
const auto parentDeviceInfo = DeviceInfoData{ parentZoneSet, true, spacing, zoneCount };
m_fancyZonesData.SetDeviceInfo(m_parentUniqueId.str(), parentDeviceInfo);
auto parentZoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_parentUniqueId.str(), false, false);
m_zoneWindowHost.m_zoneWindow = parentZoneWindow.get();
// newWorkArea = true - zoneWindow will be cloned from parent
auto actualZoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false, true);
Assert::IsNotNull(actualZoneWindow->ActiveZoneSet());
const auto actualZoneSet = actualZoneWindow->ActiveZoneSet()->GetZones();
Assert::AreEqual((size_t)zoneCount, actualZoneSet.size());
Assert::IsTrue(m_fancyZonesData.GetDeviceInfoMap().contains(m_uniqueId.str()));
auto currentDeviceInfo = m_fancyZonesData.GetDeviceInfoMap().at(m_uniqueId.str());
Assert::AreEqual(zoneCount, currentDeviceInfo.zoneCount);
Assert::AreEqual(spacing, currentDeviceInfo.spacing);
Assert::AreEqual(static_cast<int>(type), static_cast<int>(currentDeviceInfo.activeZoneSet.type));
}
TEST_METHOD (CreateZoneWindowNotClonedFromParent)
{
using namespace JSONHelpers;
const ZoneSetLayoutType type = ZoneSetLayoutType::PriorityGrid;
const int spacing = 10;
const int zoneCount = 5;
const auto customSetGuid = Helpers::CreateGuidString();
const auto parentZoneSet = ZoneSetData{ customSetGuid, type };
const auto parentDeviceInfo = DeviceInfoData{ parentZoneSet, true, spacing, zoneCount };
m_fancyZonesData.SetDeviceInfo(m_parentUniqueId.str(), parentDeviceInfo);
auto parentZoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_parentUniqueId.str(), false, false);
m_zoneWindowHost.m_zoneWindow = parentZoneWindow.get();
// newWorkArea = false - zoneWindow won't be cloned from parent
auto actualZoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false, false);
Assert::IsNull(actualZoneWindow->ActiveZoneSet());
Assert::IsTrue(m_fancyZonesData.GetDeviceInfoMap().contains(m_uniqueId.str()));
auto currentDeviceInfo = m_fancyZonesData.GetDeviceInfoMap().at(m_uniqueId.str());
// default values
Assert::AreEqual(false, currentDeviceInfo.showSpacing);
Assert::AreEqual(0, currentDeviceInfo.zoneCount);
Assert::AreEqual(0, currentDeviceInfo.spacing);
Assert::AreEqual(std::wstring{ L"null" }, currentDeviceInfo.activeZoneSet.uuid);
Assert::AreEqual(static_cast<int>(ZoneSetLayoutType::Blank), static_cast<int>(currentDeviceInfo.activeZoneSet.type));
}
TEST_METHOD(MoveSizeEnter)
{
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false);
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false, false);
const auto expected = S_OK;
const auto actual = m_zoneWindow->MoveSizeEnter(Mocks::Window(), true);
@ -389,7 +450,7 @@ namespace FancyZonesUnitTests
TEST_METHOD(MoveSizeEnterTwice)
{
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false);
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false, false);
const auto expected = E_INVALIDARG;
@ -402,7 +463,7 @@ namespace FancyZonesUnitTests
TEST_METHOD(MoveSizeUpdate)
{
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false);
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false, false);
const auto expected = S_OK;
const auto actual = m_zoneWindow->MoveSizeUpdate(POINT{ 0, 0 }, true);
@ -413,7 +474,7 @@ namespace FancyZonesUnitTests
TEST_METHOD(MoveSizeUpdatePointNegativeCoordinates)
{
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false);
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false, false);
const auto expected = S_OK;
const auto actual = m_zoneWindow->MoveSizeUpdate(POINT{ -10, -10 }, true);
@ -424,7 +485,7 @@ namespace FancyZonesUnitTests
TEST_METHOD(MoveSizeUpdatePointBigCoordinates)
{
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false);
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false, false);
const auto expected = S_OK;
const auto actual = m_zoneWindow->MoveSizeUpdate(POINT{ m_monitorInfo.rcMonitor.right + 1, m_monitorInfo.rcMonitor.bottom + 1 }, true);
@ -445,7 +506,7 @@ namespace FancyZonesUnitTests
Assert::AreEqual(expected, actual);
const auto zoneSet = zoneWindow->ActiveZoneSet();
zoneSet->MoveWindowIntoZoneByIndex(window, Mocks::Window(), false);
zoneSet->MoveWindowIntoZoneByIndex(window, Mocks::Window(), 0, false);
const auto actualZoneIndex = zoneSet->GetZoneIndexFromWindow(window);
Assert::AreNotEqual(-1, actualZoneIndex);
}
@ -458,7 +519,7 @@ namespace FancyZonesUnitTests
zoneWindow->MoveSizeEnter(window, true);
const auto expected = S_OK;
const auto actual = zoneWindow->MoveSizeEnd(window, POINT{ 0, 0 });
const auto actual = zoneWindow->MoveSizeEnd(window, POINT{ -100, -100 });
Assert::AreEqual(expected, actual);
const auto zoneSet = zoneWindow->ActiveZoneSet();
@ -468,7 +529,7 @@ namespace FancyZonesUnitTests
TEST_METHOD(MoveSizeEndDifferentWindows)
{
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false);
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false, false);
const auto window = Mocks::Window();
m_zoneWindow->MoveSizeEnter(window, true);
@ -481,7 +542,7 @@ namespace FancyZonesUnitTests
TEST_METHOD(MoveSizeEndWindowNotSet)
{
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false);
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false, false);
const auto expected = E_INVALIDARG;
const auto actual = m_zoneWindow->MoveSizeEnd(Mocks::Window(), POINT{ 0, 0 });
@ -501,14 +562,14 @@ namespace FancyZonesUnitTests
Assert::AreEqual(expected, actual);
const auto zoneSet = zoneWindow->ActiveZoneSet();
zoneSet->MoveWindowIntoZoneByIndex(window, Mocks::Window(), false);
zoneSet->MoveWindowIntoZoneByIndex(window, Mocks::Window(), 0, false);
const auto actualZoneIndex = zoneSet->GetZoneIndexFromWindow(window);
Assert::AreNotEqual(-1, actualZoneIndex); //with invalid point zone remains the same
}
TEST_METHOD(MoveWindowIntoZoneByIndexNoActiveZoneSet)
{
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false);
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false, false);
Assert::IsNull(m_zoneWindow->ActiveZoneSet());
m_zoneWindow->MoveWindowIntoZoneByIndex(Mocks::Window(), 0);
@ -526,7 +587,7 @@ namespace FancyZonesUnitTests
TEST_METHOD(MoveWindowIntoZoneByDirectionNoActiveZoneSet)
{
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false);
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false, false);
Assert::IsNull(m_zoneWindow->ActiveZoneSet());
m_zoneWindow->MoveWindowIntoZoneByIndex(Mocks::Window(), 0);
@ -564,7 +625,7 @@ namespace FancyZonesUnitTests
TEST_METHOD(SaveWindowProcessToZoneIndexNoActiveZoneSet)
{
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false);
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false, false);
Assert::IsNull(m_zoneWindow->ActiveZoneSet());
m_zoneWindow->SaveWindowProcessToZoneIndex(Mocks::Window());
@ -650,5 +711,29 @@ namespace FancyZonesUnitTests
const auto actual = m_fancyZonesData.GetAppZoneHistoryMap().at(processPath).zoneIndex;
Assert::AreEqual(expected, actual);
}
TEST_METHOD (WhenWindowIsNotResizablePlacingItIntoTheZoneShouldNotResizeIt)
{
m_zoneWindow = InitZoneWindowWithActiveZoneSet();
Assert::IsNotNull(m_zoneWindow->ActiveZoneSet());
auto window = Mocks::WindowCreate(m_hInst);
int orginalWidth = 450;
int orginalHeight = 550;
SetWindowPos(window, nullptr, 150, 150, orginalWidth, orginalHeight, SWP_SHOWWINDOW);
SetWindowLong(window, GWL_STYLE, GetWindowLong(window, GWL_STYLE) & ~WS_SIZEBOX);
auto zone = MakeZone(RECT{ 50, 50, 300, 300 });
m_zoneWindow->ActiveZoneSet()->AddZone(zone);
m_zoneWindow->MoveWindowIntoZoneByDirection(window, VK_LEFT, true);
RECT inZoneRect;
GetWindowRect(window, &inZoneRect);
Assert::AreEqual(orginalWidth, (int)inZoneRect.right - (int) inZoneRect.left);
Assert::AreEqual(orginalHeight, (int)inZoneRect.bottom - (int)inZoneRect.top);
}
};
}

View File

@ -162,7 +162,7 @@
<OptimizeReferences>true</OptimizeReferences>
<AdditionalDependencies>Pathcch.lib;comctl32.lib;$(SolutionDir)$(Platform)\$(Configuration)\obj\PowerRenameUI\PowerRenameUI.res;shcore.lib;%(AdditionalDependencies)</AdditionalDependencies>
<ModuleDefinitionFile>PowerRenameExt.def</ModuleDefinitionFile>
<DelayLoadDLLs>gdi32.dll;advapi32.dll;shell32.dll;ole32.dll;shlwapi.dll;oleaut32.dll;%(DelayLoadDLLs)</DelayLoadDLLs>
<DelayLoadDLLs>gdi32.dll;shell32.dll;ole32.dll;shlwapi.dll;oleaut32.dll;%(DelayLoadDLLs)</DelayLoadDLLs>
</Link>
</ItemDefinitionGroup>
<ItemGroup>

View File

@ -81,7 +81,7 @@
<IntDir>$(Platform)\$(Configuration)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>false</LinkIncremental>
<LinkIncremental>true</LinkIncremental>
<IncludePath>..\lib\;$(IncludePath)</IncludePath>
<OutDir>$(SolutionDir)$(Platform)\$(Configuration)\modules\</OutDir>
<IntDir>$(SolutionDir)$(Platform)\$(Configuration)\obj\$(ProjectName)\</IntDir>

View File

@ -87,7 +87,6 @@
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>true</LinkIncremental>
<OutDir>$(SolutionDir)$(Platform)\$(Configuration)\modules\</OutDir>
<IntDir>$(SolutionDir)$(Platform)\$(Configuration)\obj\$(ProjectName)\</IntDir>
</PropertyGroup>

View File

@ -1,4 +1,4 @@
// Copyright (c) Microsoft Corporation
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information. Code forked from Betsegaw Tadele's https://github.com/betsegaw/windowwalker/
@ -603,167 +603,200 @@ namespace WindowWalker.Components
/// The retrieved handle identifies the window of the same type that is highest in the Z order.
/// </summary>
GW_HWNDFIRST = 0,
/// <summary>
/// The retrieved handle identifies the window of the same type that is lowest in the Z order.
/// </summary>
GW_HWNDLAST = 1,
/// <summary>
/// The retrieved handle identifies the window below the specified window in the Z order.
/// </summary>
GW_HWNDNEXT = 2,
/// <summary>
/// The retrieved handle identifies the window above the specified window in the Z order.
/// </summary>
GW_HWNDPREV = 3,
/// <summary>
/// The retrieved handle identifies the specified window's owner window, if any.
/// </summary>
GW_OWNER = 4,
/// <summary>
/// The retrieved handle identifies the child window at the top of the Z order, if the specified window
/// is a parent window.
/// </summary>
GW_CHILD = 5,
/// <summary>
/// The retrieved handle identifies the enabled popup window owned by the specified window.
/// </summary>
GW_ENABLEDPOPUP = 6
GW_ENABLEDPOPUP = 6,
}
/// <summary>
/// GetWindowLong index to retrieves the extended window styles.
/// </summary>
#pragma warning disable SA1310 // Field names should not contain underscore
public const int GWL_EXSTYLE = -20;
#pragma warning restore SA1310 // Field names should not contain underscore
/// <summary>
/// The following are the extended window styles
/// </summary>
[Flags]
public enum ExtendedWindowStyles : UInt32
public enum ExtendedWindowStyles : uint
{
/// <summary>
/// The window has a double border; the window can, optionally, be created with a title bar by specifying
/// the WS_CAPTION style in the dwStyle parameter.
/// </summary>
WS_EX_DLGMODALFRAME = 0X0001,
/// <summary>
/// The child window created with this style does not send the WM_PARENTNOTIFY message to its parent window
/// when it is created or destroyed.
/// </summary>
WS_EX_NOPARENTNOTIFY = 0X0004,
/// <summary>
/// The window should be placed above all non-topmost windows and should stay above all non-topmost windows
/// and should stay above them, even when the window is deactivated.
/// </summary>
WS_EX_TOPMOST = 0X0008,
/// <summary>
/// The window accepts drag-drop files.
/// </summary>
WS_EX_ACCEPTFILES = 0x0010,
/// <summary>
/// The window should not be painted until siblings beneath the window (that were created by the same thread)
/// have been painted.
/// have been painted.
/// </summary>
WS_EX_TRANSPARENT = 0x0020,
/// <summary>
/// The window is a MDI child window.
/// </summary>
WS_EX_MDICHILD = 0x0040,
/// <summary>
/// The window is intended to be used as a floating toolbar. A tool window has a title bar that is shorter
/// than a normal title bar, and the window title is drawn using a smaller font. A tool window does not
/// appear in the taskbar or in the dialog that appears when the user presses ALT+TAB.
/// </summary>
WS_EX_TOOLWINDOW = 0x0080,
/// <summary>
/// The window has a border with a raised edge.
/// </summary>
WS_EX_WINDOWEDGE = 0x0100,
/// <summary>
/// The window has a border with a sunken edge.
/// </summary>
WS_EX_CLIENTEDGE = 0x0200,
/// <summary>
/// The title bar of the window includes a question mark.
/// </summary>
WS_EX_CONTEXTHELP = 0x0400,
/// <summary>
/// The window has generic "right-aligned" properties. This depends on the window class. This style has
/// an effect only if the shell language supports reading-order alignment, otherwise is ignored.
/// </summary>
WS_EX_RIGHT = 0x1000,
/// <summary>
/// The window has generic left-aligned properties. This is the default.
/// </summary>
WS_EX_LEFT = 0x0,
/// <summary>
/// If the shell language supports reading-order alignment, the window text is displayed using right-to-left
/// reading-order properties. For other languages, the styles is ignored.
/// </summary>
WS_EX_RTLREADING = 0x2000,
/// <summary>
/// The window text is displayed using left-to-right reading-order properties. This is the default.
/// </summary>
WS_EX_LTRREADING = 0x0,
/// <summary>
/// If the shell language supports reading order alignment, the vertical scroll bar (if present) is to
/// the left of the client area. For other languages, the style is ignored.
/// </summary>
WS_EX_LEFTSCROLLBAR = 0x4000,
/// <summary>
/// The vertical scroll bar (if present) is to the right of the client area. This is the default.
/// </summary>
WS_EX_RIGHTSCROLLBAR = 0x0,
/// <summary>
/// The window itself contains child windows that should take part in dialog box, navigation. If this
/// The window itself contains child windows that should take part in dialog box, navigation. If this
/// style is specified, the dialog manager recurses into children of this window when performing
/// navigation operations such as handling tha TAB key, an arrow key, or a keyboard mnemonic.
/// </summary>
WS_EX_CONTROLPARENT = 0x10000,
/// <summary>
/// The window has a three-dimensional border style intended to be used for items that do not accept
/// user input.
/// </summary>
WS_EX_STATICEDGE = 0x20000,
/// <summary>
/// Forces a top-level window onto the taskbar when the window is visible.
/// </summary>
WS_EX_APPWINDOW = 0x40000,
/// <summary>
/// The window is an overlapped window.
/// </summary>
WS_EX_OVERLAPPEDWINDOW = WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE,
/// <summary>
/// The window is palette window, which is a modeless dialog box that presents an array of commands.
/// </summary>
WS_EX_PALETTEWINDOW = WS_EX_WINDOWEDGE | WS_EX_TOOLWINDOW | WS_EX_TOPMOST,
/// <summary>
/// The window is a layered window. This style cannot be used if the window has a class style of either
/// CS_OWNDC or CS_CLASSDC. Only for top level window before Windows 8, and child windows from Windows 8.
/// </summary>
WS_EX_LAYERED = 0x80000,
/// <summary>
/// The window does not pass its window layout to its child windows.
/// </summary>
WS_EX_NOINHERITLAYOUT = 0x100000,
/// <summary>
/// If the shell language supports reading order alignment, the horizontal origin of the window is on the
/// right edge. Increasing horizontal values advance to the left.
/// </summary>
WS_EX_LAYOUTRTL = 0x400000,
/// <summary>
/// Paints all descendants of a window in bottom-to-top painting order using double-buffering.
/// Bottom-to-top painting order allows a descendent window to have translucency (alpha) and
/// Bottom-to-top painting order allows a descendent window to have translucency (alpha) and
/// transparency (color-key) effects, but only if the descendent window also has the WS_EX_TRANSPARENT
/// bit set. Double-buffering allows the window and its descendents to be painted without flicker.
/// </summary>
WS_EX_COMPOSITED = 0x2000000,
/// <summary>
/// A top-level window created with this style does not become the foreground window when the user
/// clicks it. The system does not bring this window to the foreground when the user minimizes or closes
/// the foreground window.
/// </summary>
WS_EX_NOACTIVATE = 0x8000000
WS_EX_NOACTIVATE = 0x8000000,
}
[DllImport("user32.dll", CharSet = CharSet.Unicode)]

View File

@ -4,7 +4,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace WindowWalker.Components
@ -19,11 +18,15 @@ namespace WindowWalker.Components
/// </summary>
public delegate void OpenWindowsUpdateHandler(object sender, SearchController.SearchResultUpdateEventArgs e);
#pragma warning disable 0067 // suppress false positive
/// <summary>
/// Event raised when there is an update to the list of open windows
/// </summary>
public event OpenWindowsUpdateHandler OnOpenWindowsUpdate;
#pragma warning restore 0067
/// <summary>
/// List of all the open windows
/// </summary>

View File

@ -112,7 +112,7 @@ namespace WindowWalker.Components
InteropAndHelpers.CallBackPtr callbackptr = new InteropAndHelpers.CallBackPtr((IntPtr hwnd, IntPtr lParam) =>
{
var childProcessId = GetProcessIDFromWindowHandle(hwnd);
if (childProcessId != this.ProcessID)
if (childProcessId != ProcessID)
{
_handlesToProcessCache[Hwnd] = GetProcessNameFromWindowHandle(hwnd);
return false;
@ -191,7 +191,7 @@ namespace WindowWalker.Components
}
/// <summary>
/// Determines whether the specified window handle identifies an existing window.
/// Gets a value indicating whether the specified window handle identifies an existing window.
/// </summary>
public bool IsWindow
{
@ -202,7 +202,7 @@ namespace WindowWalker.Components
}
/// <summary>
/// Get a value indicating whether is the window GWL_EX_STYLE is a toolwindow
/// Gets a value indicating whether a value is the window GWL_EX_STYLE is a toolwindow
/// </summary>
public bool IsToolWindow
{
@ -215,7 +215,7 @@ namespace WindowWalker.Components
}
/// <summary>
/// Get a value indicating whether the window GWL_EX_STYLE is an appwindow
/// Gets a value indicating whether the window GWL_EX_STYLE is an appwindow
/// </summary>
public bool IsAppWindow
{
@ -228,7 +228,7 @@ namespace WindowWalker.Components
}
/// <summary>
/// Get a value indicating whether the window has ITaskList_Deleted property
/// Gets a value indicating whether the window has ITaskList_Deleted property
/// </summary>
public bool TaskListDeleted
{
@ -239,18 +239,18 @@ namespace WindowWalker.Components
}
/// <summary>
/// Get a value indicating whether the app is a cloaked UWP app
/// Gets a value indicating whether the app is a cloaked UWP app
/// </summary>
public bool IsUWPCloaked
{
get
{
return (this.IsWindowCloaked() && this.ClassName == "ApplicationFrameWindow");
return IsWindowCloaked() && ClassName == "ApplicationFrameWindow";
}
}
/// <summary>
/// Determines whether the specified windows is the owner
/// Gets a value indicating whether the specified windows is the owner
/// </summary>
public bool IsOwner
{
@ -267,7 +267,7 @@ namespace WindowWalker.Components
{
int isCloaked = 0;
const int DWMWA_CLOAKED = 14;
InteropAndHelpers.DwmGetWindowAttribute(this.hwnd, DWMWA_CLOAKED, out isCloaked, sizeof(int));
InteropAndHelpers.DwmGetWindowAttribute(hwnd, DWMWA_CLOAKED, out isCloaked, sizeof(int));
return isCloaked != 0;
}

View File

@ -117,7 +117,7 @@ namespace WindowWalker
private void Window_GotFocus(object sender, RoutedEventArgs e)
{
this.searchBox.Focus();
searchBox.Focus();
}
}
}

View File

@ -6,7 +6,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Interop;
using Microsoft.Win32;
using WindowWalker.Components;
using WindowWalker.MVVMHelpers;
@ -18,7 +18,6 @@ namespace WindowWalker.ViewModels
private readonly List<string> _hints = new List<string>()
{
"search for running processes or windows...",
// "you can reinvoke this app using CTRL + WIN",
};
private string _searchText = string.Empty;

View File

@ -2437,6 +2437,16 @@
"integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==",
"dev": true
},
"bindings": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
"integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==",
"dev": true,
"optional": true,
"requires": {
"file-uri-to-path": "1.0.0"
}
},
"block-stream": {
"version": "0.0.9",
"resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz",
@ -2760,6 +2770,21 @@
"y18n": "^4.0.0"
},
"dependencies": {
"minimist": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
"dev": true
},
"mkdirp": {
"version": "0.5.5",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
"integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
"dev": true,
"requires": {
"minimist": "^1.2.5"
}
},
"rimraf": {
"version": "2.7.1",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
@ -3272,6 +3297,21 @@
"run-queue": "^1.0.0"
},
"dependencies": {
"minimist": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
"dev": true
},
"mkdirp": {
"version": "0.5.5",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
"integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
"dev": true,
"requires": {
"minimist": "^1.2.5"
}
},
"rimraf": {
"version": "2.7.1",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
@ -4477,6 +4517,15 @@
"ms": "2.0.0"
}
},
"mkdirp": {
"version": "0.5.5",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
"integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
"dev": true,
"requires": {
"minimist": "^1.2.5"
}
},
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
@ -4552,6 +4601,13 @@
"schema-utils": "^1.0.0"
}
},
"file-uri-to-path": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
"integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==",
"dev": true,
"optional": true
},
"filesize": {
"version": "3.6.1",
"resolved": "https://registry.npmjs.org/filesize/-/filesize-3.6.1.tgz",
@ -4810,14 +4866,15 @@
"dev": true
},
"fsevents": {
"version": "1.2.9",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.9.tgz",
"integrity": "sha512-oeyj2H3EjjonWcFjD5NvZNE9Rqe4UW+nQBU2HNeKw0koVLEFIhtyETyAakeAM3de7Z/SW5kcA+fZUait9EApnw==",
"version": "1.2.12",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.12.tgz",
"integrity": "sha512-Ggd/Ktt7E7I8pxZRbGIs7vwqAPscSESMrCSkx2FtWeqmheJgCo2R74fTsZFCifr0VTPwqRpPv17+6b8Zp7th0Q==",
"dev": true,
"optional": true,
"requires": {
"bindings": "^1.5.0",
"nan": "^2.12.1",
"node-pre-gyp": "^0.12.0"
"node-pre-gyp": "*"
},
"dependencies": {
"abbrev": {
@ -4865,7 +4922,7 @@
}
},
"chownr": {
"version": "1.1.1",
"version": "1.1.4",
"bundled": true,
"dev": true,
"optional": true
@ -4895,7 +4952,7 @@
"optional": true
},
"debug": {
"version": "4.1.1",
"version": "3.2.6",
"bundled": true,
"dev": true,
"optional": true,
@ -4922,12 +4979,12 @@
"optional": true
},
"fs-minipass": {
"version": "1.2.5",
"version": "1.2.7",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"minipass": "^2.2.1"
"minipass": "^2.6.0"
}
},
"fs.realpath": {
@ -4953,7 +5010,7 @@
}
},
"glob": {
"version": "7.1.3",
"version": "7.1.6",
"bundled": true,
"dev": true,
"optional": true,
@ -4982,7 +5039,7 @@
}
},
"ignore-walk": {
"version": "3.0.1",
"version": "3.0.3",
"bundled": true,
"dev": true,
"optional": true,
@ -5001,7 +5058,7 @@
}
},
"inherits": {
"version": "2.0.3",
"version": "2.0.4",
"bundled": true,
"dev": true,
"optional": true
@ -5037,13 +5094,11 @@
}
},
"minimist": {
"version": "0.0.8",
"bundled": true,
"dev": true,
"optional": true
"version": "1.2.5",
"bundled": true
},
"minipass": {
"version": "2.3.5",
"version": "2.9.0",
"bundled": true,
"dev": true,
"optional": true,
@ -5053,48 +5108,47 @@
}
},
"minizlib": {
"version": "1.2.1",
"version": "1.3.3",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"minipass": "^2.2.1"
"minipass": "^2.9.0"
}
},
"mkdirp": {
"version": "0.5.1",
"bundled": true,
"dev": true,
"optional": true,
"version": "0.5.5",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
"integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
"requires": {
"minimist": "0.0.8"
"minimist": "^1.2.5"
}
},
"ms": {
"version": "2.1.1",
"version": "2.1.2",
"bundled": true,
"dev": true,
"optional": true
},
"needle": {
"version": "2.3.0",
"version": "2.3.3",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"debug": "^4.1.0",
"debug": "^3.2.6",
"iconv-lite": "^0.4.4",
"sax": "^1.2.4"
}
},
"node-pre-gyp": {
"version": "0.12.0",
"version": "0.14.0",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"detect-libc": "^1.0.2",
"mkdirp": "^0.5.1",
"mkdirp": "0.5.5",
"needle": "^2.2.1",
"nopt": "^4.0.1",
"npm-packlist": "^1.1.6",
@ -5102,11 +5156,23 @@
"rc": "^1.2.7",
"rimraf": "^2.6.1",
"semver": "^5.3.0",
"tar": "^4"
"tar": "^4.4.2"
},
"dependencies": {
"mkdirp": {
"version": "0.5.5",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
"integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
"dev": true,
"optional": true,
"requires": {
"minimist": "^1.2.5"
}
}
}
},
"nopt": {
"version": "4.0.1",
"version": "4.0.3",
"bundled": true,
"dev": true,
"optional": true,
@ -5116,19 +5182,29 @@
}
},
"npm-bundled": {
"version": "1.0.6",
"version": "1.1.1",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"npm-normalize-package-bin": "^1.0.1"
}
},
"npm-normalize-package-bin": {
"version": "1.0.1",
"bundled": true,
"dev": true,
"optional": true
},
"npm-packlist": {
"version": "1.4.1",
"version": "1.4.8",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"ignore-walk": "^3.0.1",
"npm-bundled": "^1.0.1"
"npm-bundled": "^1.0.1",
"npm-normalize-package-bin": "^1.0.1"
}
},
"npmlog": {
@ -5193,7 +5269,7 @@
"optional": true
},
"process-nextick-args": {
"version": "2.0.0",
"version": "2.0.1",
"bundled": true,
"dev": true,
"optional": true
@ -5208,18 +5284,10 @@
"ini": "~1.3.0",
"minimist": "^1.2.0",
"strip-json-comments": "~2.0.1"
},
"dependencies": {
"minimist": {
"version": "1.2.0",
"bundled": true,
"dev": true,
"optional": true
}
}
},
"readable-stream": {
"version": "2.3.6",
"version": "2.3.7",
"bundled": true,
"dev": true,
"optional": true,
@ -5234,7 +5302,7 @@
}
},
"rimraf": {
"version": "2.6.3",
"version": "2.7.1",
"bundled": true,
"dev": true,
"optional": true,
@ -5261,7 +5329,7 @@
"optional": true
},
"semver": {
"version": "5.7.0",
"version": "5.7.1",
"bundled": true,
"dev": true,
"optional": true
@ -5314,18 +5382,30 @@
"optional": true
},
"tar": {
"version": "4.4.8",
"version": "4.4.13",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"chownr": "^1.1.1",
"fs-minipass": "^1.2.5",
"minipass": "^2.3.4",
"minizlib": "^1.1.1",
"mkdirp": "^0.5.0",
"minipass": "^2.8.6",
"minizlib": "^1.2.1",
"mkdirp": "0.5.5",
"safe-buffer": "^5.1.2",
"yallist": "^3.0.2"
"yallist": "^3.0.3"
},
"dependencies": {
"mkdirp": {
"version": "0.5.5",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
"integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
"dev": true,
"optional": true,
"requires": {
"minimist": "^1.2.5"
}
}
}
},
"util-deprecate": {
@ -5350,7 +5430,7 @@
"optional": true
},
"yallist": {
"version": "3.0.3",
"version": "3.1.1",
"bundled": true,
"dev": true,
"optional": true
@ -5369,6 +5449,21 @@
"rimraf": "2"
},
"dependencies": {
"minimist": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
"dev": true
},
"mkdirp": {
"version": "0.5.5",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
"integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
"dev": true,
"requires": {
"minimist": "^1.2.5"
}
},
"rimraf": {
"version": "2.7.1",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
@ -5609,15 +5704,16 @@
"dev": true
},
"handlebars": {
"version": "4.5.3",
"resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.5.3.tgz",
"integrity": "sha512-3yPecJoJHK/4c6aZhSvxOyG4vJKDshV36VHp0iVCDVh7o9w2vwi3NSnL2MMPj3YdduqaBcu7cGbggJQM0br9xA==",
"version": "4.7.6",
"resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.6.tgz",
"integrity": "sha512-1f2BACcBfiwAfStCKZNrUCgqNZkGsAT7UM3kkYtXuLo0KnaVfjKOyf7PRzB6++aK9STyT1Pd2ZCPe3EGOXleXA==",
"dev": true,
"requires": {
"minimist": "^1.2.5",
"neo-async": "^2.6.0",
"optimist": "^0.6.1",
"source-map": "^0.6.1",
"uglify-js": "^3.1.4"
"uglify-js": "^3.1.4",
"wordwrap": "^1.0.0"
}
},
"har-schema": {
@ -7098,6 +7194,21 @@
"semver": "^6.2.0"
},
"dependencies": {
"minimist": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
"dev": true
},
"mkdirp": {
"version": "0.5.5",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
"integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
"dev": true,
"requires": {
"minimist": "^1.2.5"
}
},
"semver": {
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
@ -7124,6 +7235,23 @@
"mkdirp": "^0.5.1",
"slash": "^2.0.0",
"source-map": "^0.6.0"
},
"dependencies": {
"minimist": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
"dev": true
},
"mkdirp": {
"version": "0.5.5",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
"integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
"dev": true,
"requires": {
"minimist": "^1.2.5"
}
}
}
},
"jest-validate": {
@ -8052,9 +8180,9 @@
}
},
"minimist": {
"version": "0.0.10",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz",
"integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8=",
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
"dev": true
},
"minipass": {
@ -8131,21 +8259,6 @@
}
}
},
"mkdirp": {
"version": "0.5.1",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
"integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
"requires": {
"minimist": "0.0.8"
},
"dependencies": {
"minimist": {
"version": "0.0.8",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
"integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0="
}
}
},
"move-concurrently": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz",
@ -8160,6 +8273,21 @@
"run-queue": "^1.0.3"
},
"dependencies": {
"minimist": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
"dev": true
},
"mkdirp": {
"version": "0.5.5",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
"integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
"dev": true,
"requires": {
"minimist": "^1.2.5"
}
},
"rimraf": {
"version": "2.7.1",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
@ -8312,6 +8440,21 @@
"which": "1"
},
"dependencies": {
"minimist": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
"dev": true
},
"mkdirp": {
"version": "0.5.5",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
"integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
"dev": true,
"requires": {
"minimist": "^1.2.5"
}
},
"rimraf": {
"version": "2.7.1",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
@ -8482,6 +8625,21 @@
"yallist": "^2.1.2"
}
},
"minimist": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
"dev": true
},
"mkdirp": {
"version": "0.5.5",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
"integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
"dev": true,
"requires": {
"minimist": "^1.2.5"
}
},
"strip-ansi": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
@ -8813,16 +8971,6 @@
"is-wsl": "^1.1.0"
}
},
"optimist": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz",
"integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=",
"dev": true,
"requires": {
"minimist": "~0.0.1",
"wordwrap": "~0.0.2"
}
},
"optionator": {
"version": "0.8.3",
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz",
@ -9184,6 +9332,21 @@
"ms": "2.0.0"
}
},
"minimist": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
"dev": true
},
"mkdirp": {
"version": "0.5.5",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
"integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
"dev": true,
"requires": {
"minimist": "^1.2.5"
}
},
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
@ -9782,6 +9945,28 @@
"integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==",
"dev": true
},
"minimist": {
"version": "0.0.8",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
"integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0="
},
"mkdirp": {
"version": "0.5.5",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
"integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
"dev": true,
"requires": {
"minimist": "^1.2.5"
},
"dependencies": {
"minimist": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
"dev": true
}
}
},
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
@ -11415,6 +11600,19 @@
"dom-serializer": "0",
"domelementtype": "1"
}
},
"minimist": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
},
"mkdirp": {
"version": "0.5.5",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
"integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
"requires": {
"minimist": "^1.2.5"
}
}
}
},
@ -11443,6 +11641,23 @@
"mkdirp": "^0.5.0",
"safe-buffer": "^5.1.2",
"yallist": "^3.0.3"
},
"dependencies": {
"minimist": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
"dev": true
},
"mkdirp": {
"version": "0.5.5",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
"integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
"dev": true,
"requires": {
"minimist": "^1.2.5"
}
}
}
},
"terser": {
@ -11494,8 +11709,25 @@
"ssri": "^6.0.1",
"unique-filename": "^1.1.1",
"y18n": "^4.0.0"
},
"dependencies": {
"mkdirp": {
"version": "0.5.5",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
"integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
"dev": true,
"requires": {
"minimist": "^1.2.5"
}
}
}
},
"minimist": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
"dev": true
},
"rimraf": {
"version": "2.7.1",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
@ -11727,6 +11959,21 @@
"integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=",
"dev": true
},
"minimist": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
"dev": true
},
"mkdirp": {
"version": "0.5.5",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
"integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
"dev": true,
"requires": {
"minimist": "^1.2.5"
}
},
"yargs-parser": {
"version": "10.1.0",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-10.1.0.tgz",
@ -11815,23 +12062,14 @@
"dev": true
},
"uglify-js": {
"version": "3.7.3",
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.7.3.tgz",
"integrity": "sha512-7tINm46/3puUA4hCkKYo4Xdts+JDaVC9ZPRcG8Xw9R4nhO/gZgUM3TENq8IF4Vatk8qCig4MzP/c8G4u2BkVQg==",
"version": "3.8.1",
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.8.1.tgz",
"integrity": "sha512-W7KxyzeaQmZvUFbGj4+YFshhVrMBGSg2IbcYAjGWGvx8DHvJMclbTDMpffdxFUGPBHjIytk7KJUR/KUXstUGDw==",
"dev": true,
"optional": true,
"requires": {
"commander": "~2.20.3",
"source-map": "~0.6.1"
},
"dependencies": {
"commander": {
"version": "2.20.3",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
"dev": true,
"optional": true
}
}
},
"undertaker": {
@ -12207,6 +12445,23 @@
"terser-webpack-plugin": "^1.1.0",
"watchpack": "^1.5.0",
"webpack-sources": "^1.3.0"
},
"dependencies": {
"minimist": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
"dev": true
},
"mkdirp": {
"version": "0.5.5",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
"integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
"dev": true,
"requires": {
"minimist": "^1.2.5"
}
}
}
},
"webpack-cli": {
@ -12537,9 +12792,9 @@
"dev": true
},
"wordwrap": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz",
"integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=",
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz",
"integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=",
"dev": true
},
"worker-farm": {

View File

@ -12,6 +12,7 @@
"typings": "lib/index.d.ts",
"license": "MIT",
"scripts": {
"preinstall": "npx npm-force-resolutions",
"just": "just-scripts",
"clean": "rimraf build lib lib-commonjs && just-scripts clean",
"build": "rimraf build && just-scripts build --min --production && copy *.html build && react-snap && xcopy build\\* ..\\settings\\settings-html /sy",
@ -46,5 +47,8 @@
},
"just": {
"stack": "just-stack-uifabric"
},
"resolutions": {
"mkdirp": "0.5.5"
}
}

View File

@ -567,7 +567,7 @@ int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _
Trace::RegisterProvider();
CoInitialize(nullptr);
const bool should_try_drop_privileges = !initialize_com_security_policy_for_webview() && is_process_elevated();
const bool should_try_drop_privileges = !initialize_com_security_policy_for_webview() && is_process_elevated(false);
if (should_try_drop_privileges)
{

View File

@ -26,13 +26,18 @@ namespace PowerToysTests
Assert.IsNotNull(topBorder);
Assert.IsNotNull(bottomBorder);
int height = bottomBorder.Rect.Y - topBorder.Rect.Y;
//up
new Actions(session).MoveToElement(topBorder).ClickAndHold().MoveByOffset(0, -5000).Release().Perform();
Assert.IsTrue(topBorder.Rect.Y >= 0);
Assert.IsTrue(height < bottomBorder.Rect.Y - topBorder.Rect.Y);
height = bottomBorder.Rect.Y - topBorder.Rect.Y;
//down
new Actions(session).MoveToElement(topBorder).ClickAndHold().MoveByOffset(0, 5000).Release().Perform();
Assert.IsTrue(topBorder.Rect.Y <= bottomBorder.Rect.Y);
Assert.IsTrue(height > bottomBorder.Rect.Y - topBorder.Rect.Y);
}
[TestMethod]
@ -43,13 +48,18 @@ namespace PowerToysTests
Assert.IsNotNull(topBorder);
Assert.IsNotNull(bottomBorder);
int height = bottomBorder.Rect.Y - topBorder.Rect.Y;
//up
new Actions(session).MoveToElement(bottomBorder).ClickAndHold().MoveByOffset(0, -5000).Release().Perform();
Assert.IsTrue(topBorder.Rect.Y <= bottomBorder.Rect.Y);
Assert.IsTrue(height > bottomBorder.Rect.Y - topBorder.Rect.Y);
height = bottomBorder.Rect.Y - topBorder.Rect.Y;
//down
new Actions(session).MoveToElement(bottomBorder).ClickAndHold().MoveByOffset(0, 5000).Release().Perform();
Assert.IsTrue(bottomBorder.Rect.Y <= Screen.PrimaryScreen.WorkingArea.Bottom);
Assert.IsTrue(height < bottomBorder.Rect.Y - topBorder.Rect.Y);
}
[TestMethod]
@ -60,13 +70,18 @@ namespace PowerToysTests
Assert.IsNotNull(leftBorder);
Assert.IsNotNull(rightBorder);
int width = rightBorder.Rect.X - leftBorder.Rect.X;
//to the left
new Actions(session).MoveToElement(leftBorder).ClickAndHold().MoveByOffset(-5000, 0).Release().Perform();
Assert.IsTrue(leftBorder.Rect.Y <= Screen.PrimaryScreen.WorkingArea.Bottom);
Assert.IsTrue(width < rightBorder.Rect.X - leftBorder.Rect.X);
width = rightBorder.Rect.X - leftBorder.Rect.X;
//to the right
new Actions(session).MoveToElement(leftBorder).ClickAndHold().MoveByOffset(5000, 0).Release().Perform();
Assert.IsTrue(leftBorder.Rect.X <= rightBorder.Rect.X);
Assert.IsTrue(width > rightBorder.Rect.X - leftBorder.Rect.X);
}
[TestMethod]
@ -77,13 +92,18 @@ namespace PowerToysTests
Assert.IsNotNull(leftBorder);
Assert.IsNotNull(rightBorder);
int width = rightBorder.Rect.X - leftBorder.Rect.X;
//to the left
new Actions(session).MoveToElement(rightBorder).ClickAndHold().MoveByOffset(-5000, 0).Release().Perform();
Assert.IsTrue(leftBorder.Rect.X <= rightBorder.Rect.X);
Assert.IsTrue(leftBorder.Rect.X <= rightBorder.Rect.X);
Assert.IsTrue(width > rightBorder.Rect.X - leftBorder.Rect.X);
width = rightBorder.Rect.X - leftBorder.Rect.X;
//to the right
new Actions(session).MoveToElement(rightBorder).ClickAndHold().MoveByOffset(5000, 0).Release().Perform();
Assert.IsTrue(leftBorder.Rect.X <= Screen.PrimaryScreen.WorkingArea.Right);
Assert.IsTrue(width < rightBorder.Rect.X - leftBorder.Rect.X);
}
[TestMethod]
@ -96,41 +116,32 @@ namespace PowerToysTests
Assert.IsNotNull(bottomBorder);
Assert.IsNotNull(rightBorder);
//up
MoveCorner(topLeftCorner, true, true, 0, -5000);
Assert.IsTrue(topLeftCorner.Rect.Y >= 0);
//down
MoveCorner(topLeftCorner, true, true, 0, 5000);
Assert.IsTrue(topLeftCorner.Rect.Y <= bottomBorder.Rect.Y);
int expectedWidth = rightBorder.Rect.X - topLeftCorner.Rect.X;
int expectedHeight = bottomBorder.Rect.Y - topLeftCorner.Rect.Y;
int actualWidth, actualHeight;
//up-left
MoveCorner(topLeftCorner, true, true, -5000, -5000);
actualHeight = bottomBorder.Rect.Y - topLeftCorner.Rect.Y;
actualWidth = rightBorder.Rect.X - topLeftCorner.Rect.X;
Assert.IsTrue(topLeftCorner.Rect.Y >= 0);
Assert.IsTrue(topLeftCorner.Rect.X >= 0);
Assert.IsTrue(actualHeight > expectedHeight);
Assert.IsTrue(actualWidth > expectedWidth);
//up-right
MoveCorner(topLeftCorner, true, true, 5000, -5000);
Assert.IsTrue(topLeftCorner.Rect.Y >= 0);
Assert.IsTrue(topLeftCorner.Rect.X <= rightBorder.Rect.X);
//to the left
MoveCorner(topLeftCorner, true, true, -5000, 0);
Assert.IsTrue(topLeftCorner.Rect.X >= 0);
//to the right
MoveCorner(topLeftCorner, true, true, 5000, 0);
Assert.IsTrue(topLeftCorner.Rect.X <= rightBorder.Rect.X);
//down-left
MoveCorner(topLeftCorner, true, true, -5000, 5000);
Assert.IsTrue(topLeftCorner.Rect.Y <= bottomBorder.Rect.Y);
Assert.IsTrue(topLeftCorner.Rect.X >= 0);
expectedHeight = actualHeight;
expectedWidth = actualWidth;
//down-right
MoveCorner(topLeftCorner, true, true, 5000, 5000);
actualHeight = bottomBorder.Rect.Y - topLeftCorner.Rect.Y;
actualWidth = rightBorder.Rect.X - topLeftCorner.Rect.X;
Assert.IsTrue(topLeftCorner.Rect.Y <= bottomBorder.Rect.Y);
Assert.IsTrue(topLeftCorner.Rect.X <= rightBorder.Rect.X);
Assert.IsTrue(actualHeight < expectedHeight);
Assert.IsTrue(actualWidth < expectedWidth);
}
[TestMethod]
@ -143,41 +154,32 @@ namespace PowerToysTests
Assert.IsNotNull(bottomBorder);
Assert.IsNotNull(leftBorder);
//up
MoveCorner(topRightCorner, false, true, 0, -5000);
Assert.IsTrue(topRightCorner.Rect.Y >= 0);
//down
MoveCorner(topRightCorner, false, true, 0, 5000);
Assert.IsTrue(topRightCorner.Rect.Y <= bottomBorder.Rect.Y);
//up-left
MoveCorner(topRightCorner, false, true, -5000, -5000);
Assert.IsTrue(topRightCorner.Rect.Y >= 0);
Assert.IsTrue(topRightCorner.Rect.X >= leftBorder.Rect.X);
int expectedWidth = topRightCorner.Rect.X - leftBorder.Rect.X;
int expectedHeight = bottomBorder.Rect.Y - topRightCorner.Rect.Y;
int actualWidth, actualHeight;
//up-right
MoveCorner(topRightCorner, false, true, 5000, -5000);
actualHeight = bottomBorder.Rect.Y - topRightCorner.Rect.Y;
actualWidth = topRightCorner.Rect.X - leftBorder.Rect.X;
Assert.IsTrue(topRightCorner.Rect.Y >= 0);
Assert.IsTrue(leftBorder.Rect.X <= Screen.PrimaryScreen.WorkingArea.Right);
Assert.IsTrue(actualHeight > expectedHeight);
Assert.IsTrue(actualWidth > expectedWidth);
//to the left
MoveCorner(topRightCorner, false, true, -5000, 0);
Assert.IsTrue(topRightCorner.Rect.X >= leftBorder.Rect.X);
//to the right
MoveCorner(topRightCorner, false, true, 5000, 0);
Assert.IsTrue(leftBorder.Rect.X <= Screen.PrimaryScreen.WorkingArea.Right);
//down-right
MoveCorner(topRightCorner, false, true, 5000, 5000);
Assert.IsTrue(topRightCorner.Rect.Y <= bottomBorder.Rect.Y);
Assert.IsTrue(leftBorder.Rect.X <= Screen.PrimaryScreen.WorkingArea.Right);
expectedHeight = actualHeight;
expectedWidth = actualWidth;
//down-left
MoveCorner(topRightCorner, false, true, -5000, 5000);
actualHeight = bottomBorder.Rect.Y - topRightCorner.Rect.Y;
actualWidth = topRightCorner.Rect.X - leftBorder.Rect.X;
Assert.IsTrue(topRightCorner.Rect.Y <= bottomBorder.Rect.Y);
Assert.IsTrue(topRightCorner.Rect.X >= leftBorder.Rect.X);
Assert.IsTrue(actualHeight < expectedHeight);
Assert.IsTrue(actualWidth < expectedWidth);
}
[TestMethod]
@ -190,41 +192,32 @@ namespace PowerToysTests
Assert.IsNotNull(topBorder);
Assert.IsNotNull(rightBorder);
//down
MoveCorner(bottomLeftCorner, true, false, 0, 5000);
Assert.IsTrue(bottomLeftCorner.Rect.Y <= Screen.PrimaryScreen.WorkingArea.Bottom);
//up
MoveCorner(bottomLeftCorner, true, false, 0, -5000);
Assert.IsTrue(bottomLeftCorner.Rect.Y >= topBorder.Rect.Y);
//down-right
MoveCorner(bottomLeftCorner, true, false, 5000, 5000);
Assert.IsTrue(bottomLeftCorner.Rect.Y <= Screen.PrimaryScreen.WorkingArea.Bottom);
Assert.IsTrue(bottomLeftCorner.Rect.X <= rightBorder.Rect.X);
//down-left
MoveCorner(bottomLeftCorner, true, false, -5000, 5000);
Assert.IsTrue(bottomLeftCorner.Rect.Y <= Screen.PrimaryScreen.WorkingArea.Bottom);
Assert.IsTrue(bottomLeftCorner.Rect.X >= 0);
//to the right
MoveCorner(bottomLeftCorner, true, false, 5000, 0);
Assert.IsTrue(bottomLeftCorner.Rect.X <= rightBorder.Rect.X);
//to the left
MoveCorner(bottomLeftCorner, true, false, -5000, 0);
Assert.IsTrue(bottomLeftCorner.Rect.X >= 0);
int expectedWidth = rightBorder.Rect.X - bottomLeftCorner.Rect.X;
int expectedHeight = bottomLeftCorner.Rect.Y - topBorder.Rect.Y;
int actualWidth, actualHeight;
//up-left
MoveCorner(bottomLeftCorner, true, false, -5000, -5000);
Assert.IsTrue(bottomLeftCorner.Rect.Y >= topBorder.Rect.Y);
Assert.IsTrue(bottomLeftCorner.Rect.X >= 0);
//up-right
MoveCorner(bottomLeftCorner, true, false, 5000, -5000);
actualHeight = bottomLeftCorner.Rect.Y - topBorder.Rect.Y;
actualWidth = rightBorder.Rect.X - bottomLeftCorner.Rect.X;
Assert.IsTrue(bottomLeftCorner.Rect.Y >= topBorder.Rect.Y);
Assert.IsTrue(bottomLeftCorner.Rect.X <= rightBorder.Rect.X);
Assert.IsTrue(actualHeight < expectedHeight);
Assert.IsTrue(actualWidth < expectedWidth);
expectedHeight = actualHeight;
expectedWidth = actualWidth;
//down-right
MoveCorner(bottomLeftCorner, true, false, -5000, 5000);
actualHeight = bottomLeftCorner.Rect.Y - topBorder.Rect.Y;
actualWidth = rightBorder.Rect.X - bottomLeftCorner.Rect.X;
Assert.IsTrue(bottomLeftCorner.Rect.Y <= Screen.PrimaryScreen.WorkingArea.Bottom);
Assert.IsTrue(bottomLeftCorner.Rect.X >= 0);
Assert.IsTrue(actualHeight > expectedHeight);
Assert.IsTrue(actualWidth > expectedWidth);
}
[TestMethod]
@ -237,41 +230,31 @@ namespace PowerToysTests
Assert.IsNotNull(topBorder);
Assert.IsNotNull(leftBorder);
//to the right
MoveCorner(bottomRightCorner, false, false, 5000, 0);
Assert.IsTrue(bottomRightCorner.Rect.X <= Screen.PrimaryScreen.WorkingArea.Right);
//to the left
MoveCorner(bottomRightCorner, false, false, -5000, 0);
Assert.IsTrue(bottomRightCorner.Rect.X >= leftBorder.Rect.X);
//down
MoveCorner(bottomRightCorner, false, false, 0, 5000);
Assert.IsTrue(bottomRightCorner.Rect.Y <= Screen.PrimaryScreen.WorkingArea.Bottom);
//up
MoveCorner(bottomRightCorner, false, false, 0, -5000);
Assert.IsTrue(bottomRightCorner.Rect.Y >= topBorder.Rect.Y);
int expectedWidth = bottomRightCorner.Rect.X - leftBorder.Rect.X;
int expectedHeight = bottomRightCorner.Rect.Y - topBorder.Rect.Y;
int actualWidth, actualHeight;
//up-left
MoveCorner(bottomRightCorner, false, false, -5000, -5000);
actualHeight = bottomRightCorner.Rect.Y - topBorder.Rect.Y;
actualWidth = bottomRightCorner.Rect.X - leftBorder.Rect.X;
Assert.IsTrue(bottomRightCorner.Rect.Y >= topBorder.Rect.Y);
Assert.IsTrue(bottomRightCorner.Rect.X >= leftBorder.Rect.X);
Assert.IsTrue(actualHeight < expectedHeight);
Assert.IsTrue(actualWidth < expectedWidth);
//up-right
MoveCorner(bottomRightCorner, false, false, 5000, -5000);
Assert.IsTrue(bottomRightCorner.Rect.Y >= topBorder.Rect.Y);
Assert.IsTrue(bottomRightCorner.Rect.X <= Screen.PrimaryScreen.WorkingArea.Right);
expectedHeight = actualHeight;
expectedWidth = actualWidth;
//down-right
MoveCorner(bottomRightCorner, false, false, 5000, 5000);
actualHeight = bottomRightCorner.Rect.Y - topBorder.Rect.Y;
actualWidth = bottomRightCorner.Rect.X - leftBorder.Rect.X;
Assert.IsTrue(bottomRightCorner.Rect.Y <= Screen.PrimaryScreen.WorkingArea.Bottom);
Assert.IsTrue(bottomRightCorner.Rect.X <= Screen.PrimaryScreen.WorkingArea.Right);
//down-left
MoveCorner(bottomRightCorner, false, false, -5000, 5000);
Assert.IsTrue(bottomRightCorner.Rect.Y <= Screen.PrimaryScreen.WorkingArea.Bottom);
Assert.IsTrue(bottomRightCorner.Rect.X >= leftBorder.Rect.X);
Assert.IsTrue(actualHeight > expectedHeight);
}
[ClassInitialize]
@ -285,17 +268,12 @@ namespace PowerToysTests
LaunchPowerToys();
}
OpenEditor();
OpenCustomLayouts();
//create canvas zone
OpenCreatorWindow("Create new custom", "Custom layout creator");
session.FindElementByAccessibilityId("newZoneButton").Click();
OpenCustomLayouts();
}
[ClassCleanup]
public static void ClassCleanup()
{
new Actions(session).MoveToElement(session.FindElementByXPath("//Button[@Name=\"Cancel\"]")).Click().Perform();
CloseEditor();
TearDown();
}
@ -303,13 +281,15 @@ namespace PowerToysTests
[TestInitialize]
public void TestInitialize()
{
//create canvas zone
OpenCreatorWindow("Create new custom", "Custom layout creator");
session.FindElementByAccessibilityId("newZoneButton").Click();
}
[TestCleanup]
public void TestCleanup()
{
new Actions(session).MoveToElement(session.FindElementByXPath("//Button[@Name=\"Cancel\"]")).Click().Perform();
}
}
}

View File

@ -23,7 +23,7 @@ namespace PowerToysTests
{
WindowsElement cancelButton = session.FindElementByXPath("//Window[@Name=\"FancyZones Editor\"]/Window/Button[@Name=\"Cancel\"]");
new Actions(session).MoveToElement(cancelButton).Click().Perform();
ShortWait();
WaitSeconds(1);
Assert.AreEqual(_initialZoneSettings, File.ReadAllText(_zoneSettingsPath), "Settings were changed");
}
@ -31,7 +31,7 @@ namespace PowerToysTests
private void SaveTest(string type, string name, int zoneCount)
{
new Actions(session).MoveToElement(session.FindElementByName("Save and apply")).Click().Perform();
ShortWait();
WaitSeconds(1);
JObject settings = JObject.Parse(File.ReadAllText(_zoneSettingsPath));
Assert.AreEqual(name, settings["custom-zone-sets"][0]["name"]);
@ -149,7 +149,7 @@ namespace PowerToysTests
string name = "My custom zone layout name";
SetLayoutName(name);
SaveTest("canvas", name, 0);
ShortWait();
WaitSeconds(1);
//rename layout
OpenEditor();
@ -168,7 +168,7 @@ namespace PowerToysTests
string name = "Name";
SetLayoutName(name);
SaveTest("canvas", name, 0);
ShortWait();
WaitSeconds(1);
//save layout id
JObject settings = JObject.Parse(File.ReadAllText(_zoneSettingsPath));
@ -183,7 +183,7 @@ namespace PowerToysTests
//settings are saved on window closing
new Actions(session).MoveToElement(session.FindElementByAccessibilityId("PART_Close")).Click().Perform();
ShortWait();
WaitSeconds(1);
//check settings
settings = JObject.Parse(File.ReadAllText(_zoneSettingsPath));
@ -206,7 +206,7 @@ namespace PowerToysTests
SetLayoutName(name);
new Actions(session).MoveToElement(session.FindElementByName("Save and apply")).Click().Perform();
ShortWait();
WaitSeconds(1);
//remove layout
OpenEditor();
@ -217,7 +217,7 @@ namespace PowerToysTests
//settings are saved on window closing
new Actions(session).MoveToElement(session.FindElementByAccessibilityId("PART_Close")).Click().Perform();
ShortWait();
WaitSeconds(1);
//check settings
JObject settings = JObject.Parse(File.ReadAllText(_zoneSettingsPath));
@ -236,8 +236,7 @@ namespace PowerToysTests
SetLayoutName(name);
new Actions(session).MoveToElement(session.FindElementByName("Save and apply")).Click().Perform();
ShortWait();
//remove layout
OpenEditor();
OpenCustomLayouts();
@ -247,7 +246,7 @@ namespace PowerToysTests
//settings are saved on window closing
new Actions(session).MoveToElement(session.FindElementByAccessibilityId("PART_Close")).Click().Perform();
ShortWait();
WaitSeconds(1);
//check settings
JObject settings = JObject.Parse(File.ReadAllText(_zoneSettingsPath));
@ -263,7 +262,7 @@ namespace PowerToysTests
OpenCreatorWindow("Create new custom", "Custom layout creator");
SetLayoutName(name);
new Actions(session).MoveToElement(session.FindElementByName("Save and apply")).Click().Perform();
ShortWait();
WaitSeconds(1);
//save layout id
JObject settings = JObject.Parse(File.ReadAllText(_zoneSettingsPath));
@ -278,7 +277,7 @@ namespace PowerToysTests
//apply
new Actions(session).MoveToElement(session.FindElementByName("Apply")).Click().Perform();
ShortWait();
WaitSeconds(1);
//check settings
settings = JObject.Parse(File.ReadAllText(_zoneSettingsPath));

View File

@ -59,11 +59,10 @@ namespace PowerToysTests
WindowsElement errorMessage = null;
try
{
errorMessage = session.FindElementByName("FancyZones Editor Exception Handler");
errorMessage = WaitElementByName("FancyZones Editor Exception Handler");
if (errorMessage != null)
{
errorMessage.FindElementByName("OK").Click();
ShortWait();
}
}
catch (OpenQA.Selenium.WebDriverException)
@ -92,16 +91,12 @@ namespace PowerToysTests
Assert.IsNotNull(editorButton);
editorButton.Click();
ShortWait();
TestEditorOpened();
}
void OpenEditorByHotkey()
{
new Actions(session).KeyDown(OpenQA.Selenium.Keys.Command).SendKeys("`").KeyUp(OpenQA.Selenium.Keys.Command).Perform();
ShortWait();
TestEditorOpened();
}

View File

@ -37,7 +37,7 @@ namespace PowerToysTests
{
session.FindElementByAccessibilityId("ApplyTemplateButton").Click();
ShortWait();
WaitSeconds(1);
Assert.AreEqual(editorZoneCountValue, GetEditZonesSetting<int>(editorZoneCount));
OpenEditor();
@ -57,7 +57,7 @@ namespace PowerToysTests
}
session.FindElementByAccessibilityId("ApplyTemplateButton").Click();
ShortWait();
WaitSeconds(1);
Assert.AreEqual(editorZoneCountValue, GetEditZonesSetting<int>(editorZoneCount));
}
@ -74,7 +74,7 @@ namespace PowerToysTests
session.FindElementByAccessibilityId("ApplyTemplateButton").Click();
ShortWait();
WaitSeconds(1);
Assert.AreNotEqual(spaceAroundSettingValue, GetEditZonesSetting<bool>(editorShowSpacing));
}
@ -89,7 +89,7 @@ namespace PowerToysTests
bool editorShowSpacingValue = spaceAroundSetting.Selected;
session.FindElementByAccessibilityId("ApplyTemplateButton").Click();
ShortWait();
WaitSeconds(1);
string[] validValues = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" };
@ -102,7 +102,7 @@ namespace PowerToysTests
paddingValue.SendKeys(editorSpacingValue);
session.FindElementByAccessibilityId("ApplyTemplateButton").Click();
ShortWait();
WaitSeconds(1);
Assert.AreEqual(editorShowSpacingValue, GetEditZonesSetting<bool>(editorShowSpacing));
Assert.AreEqual(editorSpacingValue, GetEditZonesSetting<string>(editorSpacing));
@ -118,7 +118,7 @@ namespace PowerToysTests
bool editorShowSpacingValue = spaceAroundSetting.Selected;
session.FindElementByAccessibilityId("ApplyTemplateButton").Click();
ShortWait();
WaitSeconds(1);
string[] invalidValues = { "!", "/", "<", "?", "D", "Z", "]", "m", "}", "1.5", "2,5" };
@ -133,7 +133,7 @@ namespace PowerToysTests
paddingValue.SendKeys(value);
session.FindElementByAccessibilityId("ApplyTemplateButton").Click();
ShortWait();
WaitSeconds(1);
Assert.AreEqual(editorShowSpacingValue, GetEditZonesSetting<bool>(editorShowSpacing));
Assert.AreEqual(editorSpacingValue, GetEditZonesSetting<string>(editorSpacing));

View File

@ -18,7 +18,7 @@ namespace PowerToysTests
{
WindowsElement cancelButton = session.FindElementByXPath("//Window[@Name=\"FancyZones Editor\"]/Window/Button[@Name=\"Cancel\"]");
new Actions(session).MoveToElement(cancelButton).Click().Perform();
ShortWait();
WaitSeconds(1);
Assert.AreEqual(_defaultZoneSettings, File.ReadAllText(_zoneSettingsPath), "Settings were changed");
}
@ -26,7 +26,7 @@ namespace PowerToysTests
private void SaveTest()
{
new Actions(session).MoveToElement(session.FindElementByName("Save and apply")).Click().Perform();
ShortWait();
WaitSeconds(1);
JObject settings = JObject.Parse(File.ReadAllText(_zoneSettingsPath));
Assert.AreEqual("Custom Layout 1", settings["custom-zone-sets"][0]["name"]);
@ -188,7 +188,6 @@ namespace PowerToysTests
if (editorWindow != null)
{
editorWindow.SendKeys(OpenQA.Selenium.Keys.Alt + OpenQA.Selenium.Keys.F4);
ShortWait();
}
}
catch(OpenQA.Selenium.WebDriverException)

View File

@ -1,4 +1,4 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using OpenQA.Selenium.Appium.Windows;
using OpenQA.Selenium.Interactions;
@ -17,8 +17,10 @@ namespace PowerToysTests
protected static void OpenEditor()
{
new Actions(session).KeyDown(OpenQA.Selenium.Keys.Command).SendKeys("`").KeyUp(OpenQA.Selenium.Keys.Command).Perform();
ShortWait();
editorWindow = session.FindElementByXPath("//Window[@Name=\"FancyZones Editor\"]");
//editorWindow = WaitElementByXPath("//Window[@Name=\"FancyZones Editor\"]");
//may not find editor by name in 0.16.1
editorWindow = WaitElementByAccessibilityId("MainWindow1");
Assert.IsNotNull(editorWindow, "Couldn't find editor window");
}
protected static void CloseEditor()
@ -28,7 +30,6 @@ namespace PowerToysTests
if (editorWindow != null)
{
editorWindow.SendKeys(OpenQA.Selenium.Keys.Alt + OpenQA.Selenium.Keys.F4);
ShortWait();
}
}
catch (OpenQA.Selenium.WebDriverException)

View File

@ -25,8 +25,6 @@ namespace PowerToysTests
private static void Init()
{
OpenSettings();
ShortWait();
OpenFancyZonesSettings();
_saveButton = session.FindElementByName("Save");
@ -94,7 +92,7 @@ namespace PowerToysTests
Assert.AreEqual(expected.ToString() + "\r\n", editor.Text);
SaveChanges();
ShortWait();
WaitSeconds(1);
int value = GetPropertyValue<int>("fancyzones_highlight_opacity");
Assert.AreEqual(expected, value);
@ -215,7 +213,7 @@ namespace PowerToysTests
action.Perform();
SaveChanges();
ShortWait();
WaitSeconds(1);
//Assert.AreEqual(expectedText, input.Text);
@ -235,7 +233,7 @@ namespace PowerToysTests
//black on the bottom
new Actions(session).MoveToElement(saturationAndBrightness).ClickAndHold().MoveByOffset(0, satRect.Height).Release().Perform();
ShortWait();
WaitSeconds(1);
Assert.AreEqual("0\r\n", red.Text);
Assert.AreEqual("0\r\n", green.Text);
@ -243,7 +241,7 @@ namespace PowerToysTests
Assert.AreEqual("000000\r\n", hex.Text);
SaveChanges();
ShortWait();
WaitSeconds(1);
Assert.AreEqual("#000000", GetPropertyValue<string>(propertyName));
//white in left corner
@ -254,7 +252,7 @@ namespace PowerToysTests
Assert.AreEqual("ffffff\r\n", hex.Text);
SaveChanges();
ShortWait();
WaitSeconds(1);
Assert.AreEqual("#ffffff", GetPropertyValue<string>(propertyName));
//color in right corner
@ -266,7 +264,7 @@ namespace PowerToysTests
Assert.AreEqual("ff0000\r\n", hex.Text);
SaveChanges();
ShortWait();
WaitSeconds(1);
Assert.AreEqual("#ff0000", GetPropertyValue<string>(propertyName));
}
@ -299,9 +297,10 @@ namespace PowerToysTests
toggle.Click();
SaveChanges();
ShortWait();
}
WaitSeconds(1);
//check saved settings
JObject savedProps = GetProperties();
Assert.AreNotEqual(toggleValues[0], GetPropertyValue<bool>(savedProps, "fancyzones_shiftDrag"));
@ -339,7 +338,7 @@ namespace PowerToysTests
}
SaveChanges();
ShortWait();
WaitSeconds(1);
JObject savedProps = GetProperties();
Assert.AreEqual(toggleValues[0], GetPropertyValue<bool>(savedProps, "fancyzones_shiftDrag"));
@ -396,7 +395,7 @@ namespace PowerToysTests
Actions action = new Actions(session);
action.MoveToElement(editor).MoveByOffset(editorRect.Width / 2 + 10, -editorRect.Height / 4).Perform();
ShortWait();
WaitSeconds(1);
action.Click().Perform();
Assert.AreEqual("100\r\n", editor.Text);
@ -421,7 +420,7 @@ namespace PowerToysTests
Actions action = new Actions(session);
action.MoveToElement(editor).MoveByOffset(editorRect.Width / 2 + 10, editorRect.Height / 4).Perform();
ShortWait();
WaitSeconds(1);
action.Click().Perform();
Assert.AreEqual("0\r\n", editor.Text);
@ -494,7 +493,7 @@ namespace PowerToysTests
Assert.AreEqual("152", hue.Text);
SaveChanges();
ShortWait();
WaitSeconds(1);
Assert.AreEqual("#63c99a", GetPropertyValue<string>("fancyzones_zoneHighlightColor"));
}
@ -565,7 +564,7 @@ namespace PowerToysTests
input.SendKeys(inputValue);
SaveChanges();
ClearInput(input);
ShortWait();
WaitSeconds(1);
Assert.AreEqual(inputValue, GetPropertyValue<string>("fancyzones_excluded_apps"));
//invalid
@ -573,28 +572,28 @@ namespace PowerToysTests
input.SendKeys(inputValue);
SaveChanges();
ClearInput(input);
ShortWait();
WaitSeconds(1);
Assert.AreEqual(inputValue, GetPropertyValue<string>("fancyzones_excluded_apps"));
inputValue = "Notepad,Chrome";
input.SendKeys(inputValue);
SaveChanges();
ClearInput(input);
ShortWait();
WaitSeconds(1);
Assert.AreEqual(inputValue, GetPropertyValue<string>("fancyzones_excluded_apps"));
inputValue = "Note*";
input.SendKeys(inputValue);
SaveChanges();
ClearInput(input);
ShortWait();
WaitSeconds(1);
Assert.AreEqual(inputValue, GetPropertyValue<string>("fancyzones_excluded_apps"));
inputValue = "Кириллица";
input.SendKeys(inputValue);
SaveChanges();
ClearInput(input);
ShortWait();
WaitSeconds(1);
Assert.AreEqual(inputValue, GetPropertyValue<string>("fancyzones_excluded_apps"));
}

View File

@ -13,6 +13,8 @@ namespace PowerToysTests
public class PowerToysSession
{
protected const string WindowsApplicationDriverUrl = "http://127.0.0.1:4723";
protected const string AppPath = "C:\\Program Files\\PowerToys\\PowerToys.exe";
protected static WindowsDriver<WindowsElement> session;
protected static bool isPowerToysLaunched = false;
protected static WindowsElement trayButton;
@ -66,14 +68,27 @@ namespace PowerToysTests
{
Thread.Sleep(TimeSpan.FromSeconds(seconds));
}
public static void ShortWait()
{
Thread.Sleep(TimeSpan.FromSeconds(0.5));
//Trying to find element by XPath
protected static WindowsElement WaitElementByName(string name, double maxTime = 10)
{
WindowsElement result = null;
Stopwatch timer = new Stopwatch();
timer.Start();
while (timer.Elapsed < TimeSpan.FromSeconds(maxTime))
{
try
{
result = session.FindElementByName(name);
}
catch { }
return result;
}
return null;
}
//Trying to find element by XPath
protected WindowsElement WaitElementByXPath(string xPath, double maxTime = 10)
protected static WindowsElement WaitElementByXPath(string xPath, double maxTime = 10)
{
WindowsElement result = null;
Stopwatch timer = new Stopwatch();
@ -85,17 +100,13 @@ namespace PowerToysTests
result = session.FindElementByXPath(xPath);
}
catch { }
if (result != null)
{
return result;
}
return result;
}
Assert.IsNotNull(result);
return null;
}
//Trying to find element by AccessibilityId
protected WindowsElement WaitElementByAccessibilityId(string accessibilityId, double maxTime = 10)
protected static WindowsElement WaitElementByAccessibilityId(string accessibilityId, double maxTime = 10)
{
WindowsElement result = null;
Stopwatch timer = new Stopwatch();
@ -107,12 +118,8 @@ namespace PowerToysTests
result = session.FindElementByAccessibilityId(accessibilityId);
}
catch { }
if (result != null)
{
return result;
}
return result;
}
Assert.IsNotNull(result);
return null;
}
@ -125,13 +132,11 @@ namespace PowerToysTests
public static void OpenFancyZonesSettings()
{
WindowsElement fzNavigationButton = session.FindElementByXPath("//Button[@Name=\"FancyZones\"]");
WindowsElement fzNavigationButton = WaitElementByXPath("//Button[@Name=\"FancyZones\"]");
Assert.IsNotNull(fzNavigationButton);
fzNavigationButton.Click();
fzNavigationButton.Click();
ShortWait();
}
public static void CloseSettings()
@ -157,7 +162,7 @@ namespace PowerToysTests
try
{
WindowsElement pt = session.FindElementByXPath("//Button[@Name=\"PowerToys\"]");
WindowsElement pt = WaitElementByXPath("//Button[@Name=\"PowerToys\"]");
isLaunched = (pt != null);
}
catch(OpenQA.Selenium.WebDriverException)
@ -174,11 +179,9 @@ namespace PowerToysTests
try
{
AppiumOptions opts = new AppiumOptions();
opts.PlatformName = "Windows";
opts.AddAdditionalCapability("platformVersion", "10");
opts.AddAdditionalCapability("deviceName", "WindowsPC");
opts.AddAdditionalCapability("app", "C:/Program Files/PowerToys/PowerToys.exe");
opts.PlatformName = "Windows";
opts.AddAdditionalCapability("app", AppPath);
WindowsDriver<WindowsElement> driver = new WindowsDriver<WindowsElement>(new Uri(WindowsApplicationDriverUrl), opts);
Assert.IsNotNull(driver);
driver.LaunchApp();
@ -195,13 +198,12 @@ namespace PowerToysTests
public static void ExitPowerToys()
{
trayButton.Click();
ShortWait();
WindowsElement pt = session.FindElementByXPath("//Button[@Name=\"PowerToys\"]");
WindowsElement pt = WaitElementByXPath("//Button[@Name=\"PowerToys\"]");
Assert.IsNotNull(pt, "Couldn't find \'PowerToys\' button");
new Actions(session).MoveToElement(pt).ContextClick().Perform();
ShortWait();
session.FindElementByXPath("//MenuItem[@Name=\"Exit\"]").Click();
WaitElementByXPath("//MenuItem[@Name=\"Exit\"]").Click();
trayButton.Click(); //close tray
isPowerToysLaunched = false;
}

View File

@ -15,10 +15,9 @@ namespace PowerToysTests
public void SettingsOpen()
{
OpenSettings();
ShortWait();
//check settings window opened
WindowsElement settingsWindow = session.FindElementByName("PowerToys Settings");
WindowsElement settingsWindow = WaitElementByName("PowerToys Settings");
Assert.IsNotNull(settingsWindow);
isSettingsOpened = true;
@ -36,14 +35,12 @@ namespace PowerToysTests
Assert.IsNotNull(pt);
new Actions(session).MoveToElement(pt).ContextClick().Perform();
ShortWait();
//open settings
session.FindElementByXPath("//MenuItem[@Name=\"Settings\"]").Click();
ShortWait();
WaitElementByXPath("//MenuItem[@Name=\"Settings\"]").Click();
//check settings window opened
WindowsElement settingsWindow = session.FindElementByName("PowerToys Settings");
WindowsElement settingsWindow = WaitElementByName("PowerToys Settings");
Assert.IsNotNull(settingsWindow);
isSettingsOpened = true;
@ -62,12 +59,10 @@ namespace PowerToysTests
Assert.IsNotNull(powerToys);
new Actions(session).MoveToElement(powerToys).ContextClick().Perform();
ShortWait();
//exit
session.FindElementByXPath("//MenuItem[@Name=\"Exit\"]").Click();
ShortWait();
WaitElementByXPath("//MenuItem[@Name=\"Exit\"]").Click();
//check PowerToys exited
powerToys = null;
try
@ -82,8 +77,6 @@ namespace PowerToysTests
}
LaunchPowerToys();
ShortWait();
Assert.IsNull(powerToys);
}