[New Module] Workspaces (#34324)

* spell checker

* Adding OOBE Projects page

* changed the default hotkey

* module interface

* rename projects editor

* bug report tool

* installer

* gpo

* exit event constant

* extend search for projects by search over the containing apps' names

* [Projects] fix grammatical issue #43 (1 app - many apps)

* [Projects] Editor: Main page: fix layout if there are many apps, launch button not disappearing on the right side

* dsc

* github

* pipeline

* guid prefix

* [Projects] fixing general settings gpo handling in runner + minor changes

* arm build fix

* Do not allow saving project if name or applist is empty. Also minor UI changes

* version

* editor version

* spellcheck

* editor dll signing

* update projects names to filter them out

* shortcut saving fix

* [Projects] Editor: brining the highlighted app's icon into the foreground. + minor UI fixes

* spell checker

* spellcheck

* [Projects] re-implementing icon size calculation to have similar sized icons for every app.

* [projects] Adding info message for cases: there are no projects or no results for the search

* [Projects] Adding Edit button to the popup. + minor changes

* [Projects] Making popup having rounded corners

* changed "no projects" text color and position

* remove opening the first proj

* fix placing windows of the same app in the project

* [Projects] bringing back the breadcrumb on the editor page. Make it clickable.

* [Projects] optimizing click handlers

* [Projects] Removing not selected apps on save

* moved on thread executor to common

* moved display utils

* added convert rect

* unsigned monitor number

* set awareness

* app placement

* [Projects] Re-implementing preview drawing - one common image

* [Projects] fix boundary calculation, use DPI aware values

* fix launching with command line args

* Fix ARM64 CI build

* launch packaged apps using names when possible

* spell-check

* update packaged apps path

* projects editor single instance

* [Projects] Add Select all checkbox, Delete selected button

* Add Checkbox for per monitor selection

* modifying highlight in preview

* spell checker

* logs

* exclude help windows

https://github.com/JaneaSystems/PowerToys-DevProjects/issues/49

* Add intermediate step to project creation

* minor bugfix

* mutex fix

* modifying highlight for minimized apps

* Fixing bug: re-draw the preview on app deletion in the editor

* Adding helper class for getting the right bounds for screens

* spell checker

* spell checker

* Minor fixes in the capture dialog

* get dpi unaware screen bounds

* refactoring: added utils

* changed window filter

https://github.com/JaneaSystems/PowerToys-DevProjects/issues/2

* clean up

* refactoring

* projects common lib

* localizable default project prefix

* launcher resources

* clean up

* change snapshot project saving

https://github.com/JaneaSystems/PowerToys-DevProjects/issues/14

* changed project data

https://github.com/JaneaSystems/PowerToys-DevProjects/issues/14

* changed project creation save-cancel handles

https://github.com/JaneaSystems/PowerToys-DevProjects/issues/14

* spell-check

* Remove checkboxes, delete feature

* remove unused from the project

* get command line args in the snapshot

* minimized settings snap fix

* set window property after launching

* FZ: ignore projects launched windows

* Implementing major new features: remove button, position manipulation, arguments, admin, minimized, maximized

* modifying colors

* launcher project filters

* clean up

* Hide Admin checkbox

* hide WIP

* spell-check

* Revert "Hide Admin checkbox"

This reverts commit 3036df9d7f.

* get app elevated property

* Implementing Launch and Edit feature

* fixing: update of listed projects on the main page after hitting save in editor

* Fix for packaged app's icons

* fixing scroll speed issue

* change scroll speed to 15

* launch elevated apps

* minor fixes

* minor fix

* enhancing shortcut handling

* can-launch-elevated check

* projects module interface telemetry

* Implementing store of setting "order by".

* minor string correction

* moved projects data parsing

* telemetry

* add move apps checkbox

* notification about elevated apps

* restart unelevated

* move existing windows

* keep opened windows at the same positions

* handle powertoys settings

* use common theme

* fix corrupted data: project id and monitor id

* project launch on "launch and edit"

* clean up

* show screen numbers instead of monitor names

* launcher error messages

* fix default shortcut

* Adding launch button to projects settings, dashboard and flyout

* Adding new app which is launched when launching a project. It shows the status of the launch process

* spell checker

* Renaming Projects to App Layouts. Replacing only string values, not the variable names

* Re-ordering modules after Renaming Projects + spell checker

* setting window size according to the screen (making it bigger)

* commenting out feature "move apps if exist"

* spell checker

* Add ProjectsLauncherUI to signing

* opening apps in minimized state which are placed on a monitor, which is not found at the moment of launching

* consistent file name

* removed unused sln

* telemetry: create event

* WindowPosition comparison

* telemetry: edit event

* fix muted Launch as admin checkbox

* telemetry: delete event

* updated Edit telemetry event

* added invoke point to launcher args

* added utils

* parse invoke point

* replaced tuple with struct

* telemetry: launch event

* MonitorRect comparison

* resources

* rename: folders

* remove outdated

* rename: window property

* rename: files and folders

* rename: common data structures

* rename: telemetry namespace

* rename: workspaces data

* rename ProjectsLib -> WorkspacesLib

* rename: gpo

* rename: settings

* rename: launcher UI

* rename: other

* rename: pt run

* rename: fz

* rename: module interface

* rename: icon

* rename: snapshot tool

* rename: editor

* rename: common files

* rename: launcher

* rename: editor resources

* fix empty file crash

* rename: json

* rename: module interface

* fix custom actions build

* added launch editor event constant

* xaml formatting

* Add missing method defition to interop::Constants idl
Remove Any CPU config

* more .sln cleanup

* [Run][PowerToys] Fix Workspaces utility (#34336)

polished workspaces utility

* build fix - align CppWinRT version

* address PR comment: fix isdigit

* indentation

* address PR comment: rename function

* address PR comment: changed version for workspaces and revision

* added supported version definition

* addressPR comment: use BringToForeground

* address PR comments: updated projects

* address PR comment: uncomment gpo in settings

* address PR comment: rename oobe view

* update OOBE image with current module name

* moved AppUtils

* launching with AppUserModel.ID

* fixed module order in settings

* fix xaml formatting

* [Workspaces] Close launcher if there are failed launches. Plus adding new spinner gif

* fix topmost LauncherUI

* clean up

* UI closing

* BugReportTool - omit cmd arg data

* Delete icon on workspace removal

* Adding cancellation to launcher UI.

* reordered launching

* fix terminating UI

* Removing old shortcut on workspace renaming

* Sentence case labels

* get process path without waiting

* comment out unused

* remove unused argument

* logs

* New icon

* fix launch and edit for the new project

* fix launch and edit: save new project

* Update exe icons

---------

Co-authored-by: donlaci <laszlo@janeasystems.com>
Co-authored-by: Jaime Bernardo <jaime@janeasystems.com>
Co-authored-by: Stefan Markovic <stefan@janeasystems.com>
Co-authored-by: Davide Giacometti <davide.giacometti@outlook.it>
Co-authored-by: Niels Laute <niels.laute@live.nl>
This commit is contained in:
Seraphima Zykova
2024-08-23 09:28:13 +02:00
committed by GitHub
parent 2a8e211cfd
commit 579619952d
221 changed files with 12805 additions and 12 deletions

View File

@@ -0,0 +1,57 @@
#pragma once
#include <vector>
#include <WorkspacesLib/WorkspacesData.h>
#include <common/logger/logger.h>
namespace WorkspacesJsonUtils
{
inline std::vector<WorkspacesData::WorkspacesProject> Read(const std::wstring& fileName)
{
std::vector<WorkspacesData::WorkspacesProject> projects{};
try
{
auto savedProjectsJson = json::from_file(fileName);
if (savedProjectsJson.has_value())
{
auto savedProjects = WorkspacesData::WorkspacesListJSON::FromJson(savedProjectsJson.value());
if (savedProjects.has_value())
{
projects = savedProjects.value();
}
}
}
catch (std::exception ex)
{
Logger::error("Error reading workspaces file. {}", ex.what());
}
return projects;
}
inline void Write(const std::wstring& fileName, const std::vector<WorkspacesData::WorkspacesProject>& projects)
{
try
{
json::to_file(fileName, WorkspacesData::WorkspacesListJSON::ToJson(projects));
}
catch (std::exception ex)
{
Logger::error("Error writing workspaces file. {}", ex.what());
}
}
inline void Write(const std::wstring& fileName, const WorkspacesData::WorkspacesProject& project)
{
try
{
json::to_file(fileName, WorkspacesData::WorkspacesProjectJSON::ToJson(project));
}
catch (std::exception ex)
{
Logger::error("Error writing workspaces file. {}", ex.what());
}
}
}

View File

@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ImportGroup Label="PropertySheets" />
<PropertyGroup Label="UserMacros" />
<!--
To customize common C++/WinRT project properties:
* right-click the project node
* expand the Common Properties item
* select the C++/WinRT property page
For more advanced scenarios, and complete documentation, please see:
https://github.com/Microsoft/cppwinrt/tree/master/nuget
-->
<PropertyGroup />
<ItemDefinitionGroup />
</Project>

View File

@@ -0,0 +1,135 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="DefaultProjectNamePrefix" xml:space="preserve">
<value>Workspace</value>
</data>
<data name="Projects" xml:space="preserve">
<value>Workspaces</value>
</data>
<data name="System_Foreground_Elevated" xml:space="preserve">
<value>We've detected an application running with administrator privileges. This will prevent certain interactions with these applications.</value>
</data>
<data name="System_Foreground_Elevated_Dialog_Dont_Show_Again" xml:space="preserve">
<value>Don't show again</value>
</data>
<data name="System_Foreground_Elevated_Learn_More" xml:space="preserve">
<value>Learn more</value>
</data>
</root>

View File

@@ -0,0 +1,277 @@
#include "pch.h"
#include "SnapshotUtils.h"
#include <comdef.h>
#include <Wbemidl.h>
#include <common/utils/elevation.h>
#include <common/utils/process_path.h>
#include <common/notifications/NotificationUtil.h>
#include <workspaces-common/WindowEnumerator.h>
#include <workspaces-common/WindowFilter.h>
#include <WorkspacesLib/AppUtils.h>
namespace SnapshotUtils
{
namespace NonLocalizable
{
const std::wstring ApplicationFrameHost = L"ApplicationFrameHost.exe";
}
class WbemHelper
{
public:
WbemHelper() = default;
~WbemHelper()
{
if (m_services)
{
m_services->Release();
}
if (m_locator)
{
m_locator->Release();
}
}
bool Initialize()
{
// Obtain the initial locator to WMI.
HRESULT hres = CoCreateInstance(CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER, IID_IWbemLocator, reinterpret_cast<LPVOID*>(&m_locator));
if (FAILED(hres))
{
Logger::error(L"Failed to create IWbemLocator object. Error: {}", get_last_error_or_default(hres));
return false;
}
// Connect to WMI through the IWbemLocator::ConnectServer method.
hres = m_locator->ConnectServer(_bstr_t(L"ROOT\\CIMV2"), NULL, NULL, 0, NULL, 0, 0, &m_services);
if (FAILED(hres))
{
Logger::error(L"Could not connect to WMI. Error: {}", get_last_error_or_default(hres));
return false;
}
// Set security levels on the proxy.
hres = CoSetProxyBlanket(m_services, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, NULL, RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE);
if (FAILED(hres))
{
Logger::error(L"Could not set proxy blanket. Error: {}", get_last_error_or_default(hres));
return false;
}
return true;
}
std::wstring GetCommandLineArgs(DWORD processID) const
{
static std::wstring property = L"CommandLine";
std::wstring query = L"SELECT " + property + L" FROM Win32_Process WHERE ProcessId = " + std::to_wstring(processID);
return Query(query, property);
}
std::wstring GetExecutablePath(DWORD processID) const
{
static std::wstring property = L"ExecutablePath";
std::wstring query = L"SELECT " + property + L" FROM Win32_Process WHERE ProcessId = " + std::to_wstring(processID);
return Query(query, property);
}
private:
std::wstring Query(const std::wstring& query, const std::wstring& propertyName) const
{
if (!m_locator || !m_services)
{
return L"";
}
IEnumWbemClassObject* pEnumerator = NULL;
HRESULT hres = m_services->ExecQuery(bstr_t("WQL"), bstr_t(query.c_str()), WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, NULL, &pEnumerator);
if (FAILED(hres))
{
Logger::error(L"Query for process failed. Error: {}", get_last_error_or_default(hres));
return L"";
}
IWbemClassObject* pClassObject = NULL;
ULONG uReturn = 0;
std::wstring result = L"";
while (pEnumerator)
{
HRESULT hr = pEnumerator->Next(WBEM_INFINITE, 1, &pClassObject, &uReturn);
if (uReturn == 0)
{
break;
}
VARIANT vtProp;
hr = pClassObject->Get(propertyName.c_str(), 0, &vtProp, 0, 0);
if (SUCCEEDED(hr) && vtProp.vt == VT_BSTR)
{
result = vtProp.bstrVal;
}
VariantClear(&vtProp);
pClassObject->Release();
}
pEnumerator->Release();
return result;
}
IWbemLocator* m_locator = NULL;
IWbemServices* m_services = NULL;
};
std::wstring GetCommandLineArgs(DWORD processID, const WbemHelper& wbemHelper)
{
std::wstring executablePath = wbemHelper.GetExecutablePath(processID);
std::wstring commandLineArgs = wbemHelper.GetCommandLineArgs(processID);
if (!commandLineArgs.empty())
{
auto pos = commandLineArgs.find(executablePath);
if (pos != std::wstring::npos)
{
commandLineArgs = commandLineArgs.substr(pos + executablePath.size());
auto spacePos = commandLineArgs.find_first_of(' ');
if (spacePos != std::wstring::npos)
{
commandLineArgs = commandLineArgs.substr(spacePos + 1);
}
}
}
return commandLineArgs;
}
bool IsProcessElevated(DWORD processID)
{
wil::unique_handle hProcess{ OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, processID) };
wil::unique_handle token;
if (OpenProcessToken(hProcess.get(), TOKEN_QUERY, &token))
{
TOKEN_ELEVATION elevation;
DWORD size;
if (GetTokenInformation(token.get(), TokenElevation, &elevation, sizeof(elevation), &size))
{
return elevation.TokenIsElevated != 0;
}
}
return false;
}
std::vector<WorkspacesData::WorkspacesProject::Application> GetApps(const std::function<unsigned int(HWND)> getMonitorNumberFromWindowHandle)
{
std::vector<WorkspacesData::WorkspacesProject::Application> apps{};
auto installedApps = Utils::Apps::GetAppsList();
auto windows = WindowEnumerator::Enumerate(WindowFilter::Filter);
// for command line args detection
// WbemHelper wbemHelper;
// wbemHelper.Initialize();
for (const auto window : windows)
{
// filter by window rect size
RECT rect = WindowUtils::GetWindowRect(window);
if (rect.right - rect.left <= 0 || rect.bottom - rect.top <= 0)
{
continue;
}
// filter by window title
std::wstring title = WindowUtils::GetWindowTitle(window);
if (title.empty())
{
continue;
}
DWORD pid{};
GetWindowThreadProcessId(window, &pid);
// filter by app path
std::wstring processPath = get_process_path(window);
if (processPath.empty())
{
// When PT runs not as admin, it can't get the process path of the window of the elevated process.
// Notify the user that running as admin is required to process elevated windows.
if (!is_process_elevated() && IsProcessElevated(pid))
{
notifications::WarnIfElevationIsRequired(GET_RESOURCE_STRING(IDS_PROJECTS),
GET_RESOURCE_STRING(IDS_SYSTEM_FOREGROUND_ELEVATED),
GET_RESOURCE_STRING(IDS_SYSTEM_FOREGROUND_ELEVATED_LEARN_MORE),
GET_RESOURCE_STRING(IDS_SYSTEM_FOREGROUND_ELEVATED_DIALOG_DONT_SHOW_AGAIN));
}
continue;
}
if (WindowUtils::IsExcludedByDefault(window, processPath))
{
continue;
}
// fix for the packaged apps that are not caught when minimized, e.g., Settings.
if (processPath.ends_with(NonLocalizable::ApplicationFrameHost))
{
for (auto otherWindow : windows)
{
DWORD otherPid{};
GetWindowThreadProcessId(otherWindow, &otherPid);
// searching for the window with the same title but different PID
if (pid != otherPid && title == WindowUtils::GetWindowTitle(otherWindow))
{
processPath = get_process_path(otherPid);
break;
}
}
}
if (WindowFilter::FilterPopup(window))
{
continue;
}
auto data = Utils::Apps::GetApp(processPath, installedApps);
if (!data.has_value() || data->name.empty())
{
Logger::debug(L"Installed app not found: {}, {}", title, processPath);
continue;
}
WorkspacesData::WorkspacesProject::Application app{
.name = data.value().name,
.title = title,
.path = processPath,
.packageFullName = data.value().packageFullName,
.appUserModelId = data.value().appUserModelId,
.commandLineArgs = L"", // GetCommandLineArgs(pid, wbemHelper),
.isElevated = IsProcessElevated(pid),
.canLaunchElevated = data.value().canLaunchElevated,
.isMinimized = WindowUtils::IsMinimized(window),
.isMaximized = WindowUtils::IsMaximized(window),
.position = WorkspacesData::WorkspacesProject::Application::Position{
.x = rect.left,
.y = rect.top,
.width = rect.right - rect.left,
.height = rect.bottom - rect.top,
},
.monitor = getMonitorNumberFromWindowHandle(window),
};
apps.push_back(app);
}
return apps;
}
}

View File

@@ -0,0 +1,8 @@
#pragma once
#include <WorkspacesLib/WorkspacesData.h>
namespace SnapshotUtils
{
std::vector<WorkspacesData::WorkspacesProject::Application> GetApps(const std::function<unsigned int(HWND)> getMonitorNumberFromWindowHandle);
};

View File

@@ -0,0 +1,181 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!-- Project configurations -->
<!-- Props that should be disabled while building on CI server -->
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props')" />
<Target Name="GenerateResourceFiles" BeforeTargets="PrepareForBuild">
<Exec Command="powershell -NonInteractive -executionpolicy Unrestricted $(SolutionDir)tools\build\convert-resx-to-rc.ps1 $(MSBuildThisFileDirectory) resource.base.h resource.h WorkspacesSnapshotToolResources.base.rc WorkspacesSnapshotToolResources.rc" />
</Target>
<!-- C++ source compile-specific things for all configurations -->
<ItemDefinitionGroup>
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<ConformanceMode>false</ConformanceMode>
<TreatWarningAsError>true</TreatWarningAsError>
<LanguageStandard>stdcpplatest</LanguageStandard>
<AdditionalOptions>/await %(AdditionalOptions)</AdditionalOptions>
<PreprocessorDefinitions>_UNICODE;UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
</Link>
<Lib>
<TreatLibWarningAsErrors>true</TreatLibWarningAsErrors>
</Lib>
</ItemDefinitionGroup>
<!-- C++ source compile-specific things for Debug/Release configurations -->
<ItemDefinitionGroup Condition="'$(Configuration)'=='Debug'">
<ClCompile>
<PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<Optimization>Disabled</Optimization>
<SDLCheck>true</SDLCheck>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)'=='Release'">
<ClCompile>
<PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<Optimization>MaxSpeed</Optimization>
<SDLCheck>false</SDLCheck>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<!-- Global props -->
<PropertyGroup Label="Globals">
<VCProjectVersion>16.0</VCProjectVersion>
<Keyword>Win32Proj</Keyword>
<ProjectGuid>{3d63307b-9d27-44fd-b033-b26f39245b85}</ProjectGuid>
<RootNamespace>WorkspacesSnapshotTool</RootNamespace>
</PropertyGroup>
<!-- Props that are constant for both Debug and Release configurations -->
<PropertyGroup Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
<SpectreMitigation>Spectre</SpectreMitigation>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
<UseDebugLibraries>true</UseDebugLibraries>
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup>
<TargetName>PowerToys.$(MSBuildProjectName)</TargetName>
<OutDir>..\..\..\..\$(Platform)\$(Configuration)\</OutDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Debug'">
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Release'">
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)'=='Debug'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<AdditionalIncludeDirectories>./../;$(SolutionDir)src\common\Telemetry;$(SolutionDir)src\common;$(SolutionDir)src\;./;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>shcore.lib;Shell32.lib;propsys.lib;DbgHelp.lib;wbemuuid.lib</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)'=='Release'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<AdditionalIncludeDirectories>./../;$(SolutionDir)src\common\Telemetry;$(SolutionDir)src\common;$(SolutionDir)src\;./;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>shcore.lib;Shell32.lib;propsys.lib;DbgHelp.lib;wbemuuid.lib</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="main.cpp" />
<ClCompile Include="pch.cpp">
<PrecompiledHeader Condition="'$(UsePrecompiledHeaders)' != 'false'">Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include="SnapshotUtils.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="JsonUtils.h" />
<ClInclude Include="pch.h" />
<ClInclude Include="resource.base.h" />
<ClInclude Include="SnapshotUtils.h" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\common\Display\Display.vcxproj">
<Project>{caba8dfb-823b-4bf2-93ac-3f31984150d9}</Project>
</ProjectReference>
<ProjectReference Include="..\..\..\common\notifications\notifications.vcxproj">
<Project>{1d5be09d-78c0-4fd7-af00-ae7c1af7c525}</Project>
</ProjectReference>
<ProjectReference Include="..\..\..\common\SettingsAPI\SettingsAPI.vcxproj">
<Project>{6955446d-23f7-4023-9bb3-8657f904af99}</Project>
</ProjectReference>
<ProjectReference Include="..\WorkspacesLib\WorkspacesLib.vcxproj">
<Project>{b31fcc55-b5a4-4ea7-b414-2dceae6af332}</Project>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="Generated Files/WorkspacesSnapshotToolResources.rc" />
<None Include="WorkspacesSnapshotToolResources.base.rc" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Resource.resx">
<SubType>Designer</SubType>
</EmbeddedResource>
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<Import Project="..\..\..\..\deps\spdlog.props" />
<ImportGroup Label="ExtensionTargets">
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" />
<Import Project="..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" />
</ImportGroup>
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props'))" />
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets'))" />
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets'))" />
</Target>
</Project>

View File

@@ -0,0 +1,61 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClInclude Include="pch.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="JsonUtils.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="SnapshotUtils.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="resource.base.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="pch.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="main.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="SnapshotUtils.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
<None Include="WorkspacesSnapshotToolResources.base.rc">
<Filter>Resource Files</Filter>
</None>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Resource.resx">
<Filter>Resource Files</Filter>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="Generated Files/WorkspacesSnapshotToolResources.rc">
<Filter>Resource Files</Filter>
</ResourceCompile>
</ItemGroup>
<ItemGroup>
<Natvis Include="$(MSBuildThisFileDirectory)..\..\natvis\wil.natvis" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,83 @@
#include "pch.h"
#include <chrono>
#include <workspaces-common/GuidUtils.h>
#include <workspaces-common/MonitorUtils.h>
#include <WorkspacesLib/WorkspacesData.h>
#include <JsonUtils.h>
#include <SnapshotUtils.h>
#include <common/utils/gpo.h>
#include <common/utils/logger_helper.h>
#include <common/utils/UnhandledExceptionHandler.h>
const std::wstring moduleName = L"Workspaces\\ProjectsSnapshotTool";
const std::wstring internalPath = L"";
int APIENTRY WinMain(HINSTANCE hInst, HINSTANCE hInstPrev, LPSTR cmdLine, int cmdShow)
{
LoggerHelpers::init_logger(moduleName, internalPath, LogSettings::workspacesLauncherLoggerName);
InitUnhandledExceptionHandler();
if (powertoys_gpo::getConfiguredWorkspacesEnabledValue() == powertoys_gpo::gpo_rule_configured_disabled)
{
Logger::warn(L"Tried to start with a GPO policy setting the utility to always be disabled. Please contact your systems administrator.");
return 0;
}
SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
HRESULT comInitHres = CoInitializeEx(0, COINIT_MULTITHREADED);
if (FAILED(comInitHres))
{
Logger::error(L"Failed to initialize COM library. {}", comInitHres);
return -1;
}
// Set general COM security levels.
comInitHres = CoInitializeSecurity(NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_DEFAULT, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE, NULL);
if (FAILED(comInitHres))
{
Logger::error(L"Failed to initialize security. Error code: {}", get_last_error_or_default(comInitHres));
CoUninitialize();
return -1;
}
std::wstring fileName = WorkspacesData::WorkspacesFile();
std::string cmdLineStr(cmdLine);
if (!cmdLineStr.empty())
{
std::wstring fileNameParam(cmdLineStr.begin(), cmdLineStr.end());
fileName = fileNameParam;
}
// create new project
time_t creationTime = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
WorkspacesData::WorkspacesProject project{ .id = CreateGuidString(), .creationTime = creationTime };
Logger::trace(L"Creating workspace {}:{}", project.name, project.id);
project.monitors = MonitorUtils::IdentifyMonitors();
project.apps = SnapshotUtils::GetApps([&](HWND window) -> unsigned int {
auto windowMonitor = MonitorFromWindow(window, MONITOR_DEFAULTTOPRIMARY);
unsigned int monitorNumber = 0;
for (const auto& monitor : project.monitors)
{
if (monitor.monitor == windowMonitor)
{
monitorNumber = monitor.number;
break;
}
}
return monitorNumber;
});
WorkspacesJsonUtils::Write(WorkspacesData::TempWorkspacesFile(), project);
Logger::trace(L"WorkspacesProject {}:{} created", project.name, project.id);
CoUninitialize();
return 0;
}

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.Windows.CppWinRT" version="2.0.240111.5" targetFramework="native" />
<package id="Microsoft.Windows.ImplementationLibrary" version="1.0.231216.1" targetFramework="native" />
</packages>

View File

@@ -0,0 +1 @@
#include "pch.h"

View File

@@ -0,0 +1,6 @@
#pragma once
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <common/logger/logger.h>
#include <winrt/Windows.Foundation.h>
#include <winrt/Windows.Foundation.Collections.h>

View File

@@ -0,0 +1,13 @@
//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// Used by WorkspacesSnapshotTool.rc
//////////////////////////////
// Non-localizable
#define FILE_DESCRIPTION "PowerToys Workspaces Snapshot Tool"
#define INTERNAL_NAME "PowerToys.WorkspacesSnapshotTool"
#define ORIGINAL_FILENAME "PowerToys.WorkspacesSnapshotTool.exe"
// Non-localizable
//////////////////////////////