mirror of
https://github.com/microsoft/PowerToys.git
synced 2025-12-15 19:27:56 +01:00
Merge branch 'dev/feature/projects' of https://github.com/microsoft/PowerToys into dev/feature/projects
This commit is contained in:
2
.github/actions/spell-check/expect.txt
vendored
2
.github/actions/spell-check/expect.txt
vendored
@@ -1210,6 +1210,8 @@ PROJECTSSNAPSHOTTOOL
|
||||
PROPBAG
|
||||
PROPERTYKEY
|
||||
propkey
|
||||
propsys
|
||||
PROPVARIANT
|
||||
propvarutil
|
||||
prvpane
|
||||
psapi
|
||||
|
||||
@@ -31,12 +31,14 @@ namespace ProjectsEditor.Data
|
||||
public int Height { get; set; }
|
||||
}
|
||||
|
||||
public long Hwnd { get; set; }
|
||||
|
||||
public string Application { get; set; }
|
||||
|
||||
public string ApplicationPath { get; set; }
|
||||
|
||||
public string Title { get; set; }
|
||||
|
||||
public string PackageFullName { get; set; }
|
||||
|
||||
public string CommandLineArguments { get; set; }
|
||||
|
||||
public bool Minimized { get; set; }
|
||||
|
||||
@@ -34,12 +34,14 @@ namespace ProjectsEditor.Models
|
||||
public int Height { get; set; }
|
||||
}
|
||||
|
||||
public IntPtr Hwnd { get; set; }
|
||||
public string AppName { get; set; }
|
||||
|
||||
public string AppPath { get; set; }
|
||||
|
||||
public string AppTitle { get; set; }
|
||||
|
||||
public string PackageFullName { get; set; }
|
||||
|
||||
public string CommandLineArguments { get; set; }
|
||||
|
||||
public bool Minimized { get; set; }
|
||||
@@ -134,20 +136,6 @@ namespace ProjectsEditor.Models
|
||||
}
|
||||
}
|
||||
|
||||
[JsonIgnore]
|
||||
public string AppName
|
||||
{
|
||||
get
|
||||
{
|
||||
if (File.Exists(AppPath))
|
||||
{
|
||||
return Path.GetFileNameWithoutExtension(AppPath);
|
||||
}
|
||||
|
||||
return AppPath.Split('\\').LastOrDefault();
|
||||
}
|
||||
}
|
||||
|
||||
public WindowPosition Position { get; set; }
|
||||
|
||||
private WindowPosition? _scaledPosition;
|
||||
@@ -194,18 +182,6 @@ namespace ProjectsEditor.Models
|
||||
PropertyChanged?.Invoke(this, e);
|
||||
}
|
||||
|
||||
internal bool IsMyAppPath(string path)
|
||||
{
|
||||
if (!IsPackagedApp)
|
||||
{
|
||||
return path.Equals(AppPath, StringComparison.Ordinal);
|
||||
}
|
||||
else
|
||||
{
|
||||
return path.Contains(PackagedName + "_", StringComparison.InvariantCultureIgnoreCase) && path.Contains(PackagedPublisherID, StringComparison.InvariantCultureIgnoreCase);
|
||||
}
|
||||
}
|
||||
|
||||
private bool? _isPackagedApp;
|
||||
|
||||
public string PackagedId { get; set; }
|
||||
|
||||
@@ -204,10 +204,11 @@ namespace ProjectsEditor.Models
|
||||
{
|
||||
Applications.Add(new Application()
|
||||
{
|
||||
Hwnd = item.Hwnd,
|
||||
AppName = item.AppName,
|
||||
AppPath = item.AppPath,
|
||||
AppTitle = item.AppTitle,
|
||||
CommandLineArguments = item.CommandLineArguments,
|
||||
PackageFullName = item.PackageFullName,
|
||||
Minimized = item.Minimized,
|
||||
Maximized = item.Maximized,
|
||||
IsSelected = item.IsSelected,
|
||||
|
||||
@@ -109,9 +109,10 @@ namespace ProjectsEditor.Utils
|
||||
{
|
||||
newProject.Applications.Add(new Models.Application()
|
||||
{
|
||||
Hwnd = new IntPtr(app.Hwnd),
|
||||
AppPath = app.Application,
|
||||
AppName = app.Application,
|
||||
AppPath = app.ApplicationPath,
|
||||
AppTitle = app.Title,
|
||||
PackageFullName = app.PackageFullName,
|
||||
Parent = newProject,
|
||||
CommandLineArguments = app.CommandLineArguments,
|
||||
Maximized = app.Maximized,
|
||||
@@ -163,9 +164,10 @@ namespace ProjectsEditor.Utils
|
||||
{
|
||||
wrapper.Applications.Add(new ProjectsData.ApplicationWrapper
|
||||
{
|
||||
Hwnd = app.Hwnd,
|
||||
Application = app.AppPath,
|
||||
Application = app.AppName,
|
||||
ApplicationPath = app.AppPath,
|
||||
Title = app.AppTitle,
|
||||
PackageFullName = app.PackageFullName,
|
||||
CommandLineArguments = app.CommandLineArguments,
|
||||
Maximized = app.Maximized,
|
||||
Minimized = app.Minimized,
|
||||
|
||||
@@ -9,14 +9,11 @@ using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Timers;
|
||||
using ManagedCommon;
|
||||
using ProjectsEditor.Models;
|
||||
using ProjectsEditor.Utils;
|
||||
using Windows.ApplicationModel.Core;
|
||||
using Windows.Management.Deployment;
|
||||
using static ProjectsEditor.Data.ProjectsData;
|
||||
|
||||
namespace ProjectsEditor.ViewModels
|
||||
@@ -234,215 +231,11 @@ namespace ProjectsEditor.ViewModels
|
||||
|
||||
public async void LaunchProject(Project project, bool exitAfterLaunch = false)
|
||||
{
|
||||
Project actualSetup = await GetActualSetup();
|
||||
|
||||
// Launch apps
|
||||
// TODO: move launching to ProjectsLauncher
|
||||
List<Application> newlyStartedApps = new List<Application>();
|
||||
foreach (Application app in project.Applications.Where(x => x.IsSelected))
|
||||
{
|
||||
string launchParam = app.AppPath;
|
||||
if (string.IsNullOrEmpty(launchParam))
|
||||
{
|
||||
Logger.LogWarning($"Project launch. App path is empty. App name: {app.AppName}");
|
||||
continue;
|
||||
}
|
||||
|
||||
// todo: app path might be different for packaged apps.
|
||||
if (actualSetup.Applications.Exists(x => x.AppPath.Equals(app.AppPath, StringComparison.Ordinal)))
|
||||
{
|
||||
// just move the existing window to the desired position
|
||||
Logger.LogInfo($"Project launch. App exists. Moving the first app with same app path to the desired position. App name: {app.AppName}");
|
||||
Application existingApp = actualSetup.Applications.Where(x => x.AppPath.Equals(app.AppPath, StringComparison.Ordinal)).First();
|
||||
NativeMethods.ShowWindow(existingApp.Hwnd, app.Minimized ? NativeMethods.SW_MINIMIZE : NativeMethods.SW_NORMAL);
|
||||
if (!app.Minimized)
|
||||
{
|
||||
MoveWindowWithScaleAdjustment(project, existingApp.Hwnd, app, true);
|
||||
NativeMethods.SetForegroundWindow(existingApp.Hwnd);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (launchParam.EndsWith("systemsettings.exe", StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
try
|
||||
{
|
||||
string args = string.IsNullOrWhiteSpace(app.CommandLineArguments) ? "home" : app.CommandLineArguments;
|
||||
bool result = await Windows.System.Launcher.LaunchUriAsync(new Uri($"ms-settings:{args}"));
|
||||
newlyStartedApps.Add(app);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
// todo exception handling
|
||||
Logger.LogError($"Exception while launching the project {project.Id}. Tried to launch the settings app via LaunchUriAsync. Exception message: {e.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
// todo: check the user's packaged apps folder
|
||||
else if (app.IsPackagedApp)
|
||||
{
|
||||
bool started = false;
|
||||
int retryCountLaunch = 50;
|
||||
|
||||
while (!started && retryCountLaunch > 0)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (string.IsNullOrEmpty(app.CommandLineArguments))
|
||||
{
|
||||
// the official app launching method.No parameters are expected
|
||||
var packApp = await GetAppByPackageFamilyNameAsync(app.PackagedId);
|
||||
if (packApp != null)
|
||||
{
|
||||
await packApp.LaunchAsync();
|
||||
}
|
||||
|
||||
newlyStartedApps.Add(app);
|
||||
started = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
await Task.Run(() =>
|
||||
{
|
||||
Process p = new Process();
|
||||
p.StartInfo = new ProcessStartInfo("cmd.exe");
|
||||
p.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
|
||||
p.StartInfo.Arguments = $"cmd /c start shell:appsfolder\\{app.Aumid} {app.CommandLineArguments}";
|
||||
p.EnableRaisingEvents = true;
|
||||
p.ErrorDataReceived += (sender, e) =>
|
||||
{
|
||||
started = false;
|
||||
};
|
||||
p.Start();
|
||||
p.WaitForExit();
|
||||
});
|
||||
newlyStartedApps.Add(app);
|
||||
started = true;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
// todo exception handling
|
||||
Logger.LogError($"Exception while launching the project {project.Id}. Tried to launch a packaged app {app.AppName}. Exception message{e.Message}");
|
||||
Thread.Sleep(100);
|
||||
}
|
||||
|
||||
retryCountLaunch--;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
await Task.Run(() =>
|
||||
{
|
||||
Process p = new Process();
|
||||
p.StartInfo = new ProcessStartInfo(launchParam);
|
||||
p.StartInfo.Arguments = app.CommandLineArguments;
|
||||
p.Start();
|
||||
});
|
||||
newlyStartedApps.Add(app);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// try again as admin
|
||||
try
|
||||
{
|
||||
await Task.Run(() =>
|
||||
{
|
||||
Process p = new Process();
|
||||
p.StartInfo = new ProcessStartInfo(launchParam);
|
||||
p.StartInfo.Arguments = app.CommandLineArguments;
|
||||
p.StartInfo.UseShellExecute = true;
|
||||
p.StartInfo.Verb = "runas"; // administrator privileges, some apps start only that way
|
||||
p.Start();
|
||||
});
|
||||
newlyStartedApps.Add(app);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
// todo exception handling
|
||||
Logger.LogError($"Exception while launching the project {project.Id}. Tried to launch an exe via Process.Start: {app.AppName}. Exception message{e.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// the official launching method needs this task.
|
||||
static async Task<AppListEntry> GetAppByPackageFamilyNameAsync(string packageFamilyName)
|
||||
{
|
||||
var pkgManager = new PackageManager();
|
||||
var pkg = pkgManager.FindPackagesForUser(string.Empty, packageFamilyName).FirstOrDefault();
|
||||
|
||||
if (pkg == null)
|
||||
{
|
||||
Logger.LogWarning($"Could not load any app for {packageFamilyName}.");
|
||||
return null;
|
||||
}
|
||||
|
||||
var apps = await pkg.GetAppListEntriesAsync();
|
||||
var firstApp = apps.Any() ? apps[0] : null;
|
||||
return firstApp;
|
||||
}
|
||||
|
||||
// next step: move newly created apps to their desired position
|
||||
// the OS needs some time to show the apps, so do it in multiple steps until all windows all in place
|
||||
// retry every 100ms for 5 sec totally
|
||||
int retryCount = 50;
|
||||
while (newlyStartedApps.Count > 0 && retryCount > 0)
|
||||
{
|
||||
List<Application> finishedApps = new List<Application>();
|
||||
actualSetup = await GetActualSetup();
|
||||
foreach (Application app in newlyStartedApps)
|
||||
{
|
||||
IEnumerable<Application> candidates = actualSetup.Applications.Where(x => app.IsMyAppPath(x.AppPath));
|
||||
if (candidates.Any())
|
||||
{
|
||||
if (app.AppPath.EndsWith("PowerToys.Settings.exe", StringComparison.Ordinal))
|
||||
{
|
||||
// give it time to not get confused (the app tries to position itself)
|
||||
Thread.Sleep(1000);
|
||||
}
|
||||
else
|
||||
{
|
||||
// the other apps worked fine and reacted correctly to the MoveWindow event, but to be safe, give them also a little time
|
||||
Thread.Sleep(100);
|
||||
}
|
||||
|
||||
// just move the existing window to the desired position
|
||||
Application existingApp = candidates.First();
|
||||
NativeMethods.ShowWindow(existingApp.Hwnd, app.Minimized ? NativeMethods.SW_MINIMIZE : NativeMethods.SW_NORMAL);
|
||||
if (!app.Minimized)
|
||||
{
|
||||
MoveWindowWithScaleAdjustment(project, existingApp.Hwnd, app, true);
|
||||
NativeMethods.SetForegroundWindow(existingApp.Hwnd);
|
||||
}
|
||||
|
||||
finishedApps.Add(app);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (Application app in finishedApps)
|
||||
{
|
||||
newlyStartedApps.Remove(app);
|
||||
}
|
||||
|
||||
Thread.Sleep(100);
|
||||
retryCount--;
|
||||
}
|
||||
|
||||
// update last launched time
|
||||
var ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
|
||||
project.LastLaunchedTime = (long)ts.TotalSeconds;
|
||||
_projectsEditorIO.SerializeProjects(Projects.ToList());
|
||||
OnPropertyChanged(new PropertyChangedEventArgs(nameof(ProjectsView)));
|
||||
|
||||
/*await Task.Run(() => RunLauncher(project.Id));
|
||||
await Task.Run(() => RunLauncher(project.Id));
|
||||
if (_projectsEditorIO.ParseProjects(this).Result == true)
|
||||
{
|
||||
OnPropertyChanged(new PropertyChangedEventArgs("ProjectsView"));
|
||||
}*/
|
||||
OnPropertyChanged(new PropertyChangedEventArgs(nameof(ProjectsView)));
|
||||
}
|
||||
|
||||
if (exitAfterLaunch)
|
||||
{
|
||||
@@ -464,32 +257,6 @@ namespace ProjectsEditor.ViewModels
|
||||
}
|
||||
}
|
||||
|
||||
private void MoveWindowWithScaleAdjustment(Project project, nint hwnd, Application app, bool repaint)
|
||||
{
|
||||
NativeMethods.SetForegroundWindow(hwnd);
|
||||
|
||||
NativeMethods.MoveWindow(hwnd, app.ScaledPosition.X, app.ScaledPosition.Y, app.ScaledPosition.Width, app.ScaledPosition.Height, repaint);
|
||||
}
|
||||
|
||||
private async Task<Project> GetActualSetup()
|
||||
{
|
||||
string filename = Path.GetTempFileName();
|
||||
if (File.Exists(filename))
|
||||
{
|
||||
File.Delete(filename);
|
||||
}
|
||||
|
||||
await Task.Run(() => RunSnapshotTool(filename));
|
||||
Project actualProject;
|
||||
_projectsEditorIO.ParseProject(filename, out actualProject);
|
||||
if (File.Exists(filename))
|
||||
{
|
||||
File.Delete(filename);
|
||||
}
|
||||
|
||||
return actualProject;
|
||||
}
|
||||
|
||||
private void RunSnapshotTool(string filename = null)
|
||||
{
|
||||
Process p = new Process();
|
||||
|
||||
@@ -1,20 +1,19 @@
|
||||
#include "pch.h"
|
||||
#include "AppLauncher.h"
|
||||
|
||||
#include <TlHelp32.h>
|
||||
|
||||
#include <future>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <winrt/Windows.Management.Deployment.h>
|
||||
#include <winrt/Windows.ApplicationModel.Core.h>
|
||||
|
||||
#include <ShellScalingApi.h>
|
||||
#include <shellapi.h>
|
||||
|
||||
#include <winrt/Windows.UI.Notifications.h>
|
||||
#include <winrt/Windows.Data.Xml.Dom.h>
|
||||
#include <iostream>
|
||||
|
||||
#include "../projects-common/MonitorEnumerator.h"
|
||||
#include "../projects-common/WindowEnumerator.h"
|
||||
|
||||
using namespace winrt;
|
||||
using namespace Windows::Foundation;
|
||||
using namespace Windows::Management::Deployment;
|
||||
|
||||
namespace FancyZones
|
||||
{
|
||||
@@ -326,328 +325,95 @@ namespace FancyZones
|
||||
}
|
||||
}
|
||||
|
||||
namespace KBM
|
||||
bool LaunchApp(const std::wstring& appPath, const std::wstring& commandLineArgs)
|
||||
{
|
||||
using namespace winrt;
|
||||
using namespace Windows::UI::Notifications;
|
||||
using namespace Windows::Data::Xml::Dom;
|
||||
SHELLEXECUTEINFO shExecInfo;
|
||||
shExecInfo.cbSize = sizeof(SHELLEXECUTEINFO);
|
||||
shExecInfo.fMask = NULL;
|
||||
shExecInfo.hwnd = NULL;
|
||||
shExecInfo.lpVerb = NULL;
|
||||
shExecInfo.lpFile = appPath.c_str();
|
||||
shExecInfo.lpParameters = commandLineArgs.c_str();
|
||||
shExecInfo.lpDirectory = NULL;
|
||||
shExecInfo.nShow = SW_MAXIMIZE;
|
||||
shExecInfo.hInstApp = NULL;
|
||||
|
||||
// Use to find a process by its name
|
||||
std::wstring GetFileNameFromPath(const std::wstring& fullPath)
|
||||
BOOL result = ShellExecuteEx(&shExecInfo);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool LaunchPackagedApp(const std::wstring& packageFullName)
|
||||
{
|
||||
try
|
||||
{
|
||||
size_t found = fullPath.find_last_of(L"\\");
|
||||
if (found != std::wstring::npos)
|
||||
// Create a PackageManager object to get the package information.
|
||||
PackageManager packageManager;
|
||||
|
||||
// Find the package by its full name.
|
||||
for (const auto& package : packageManager.FindPackagesForUser({}))
|
||||
{
|
||||
return fullPath.substr(found + 1);
|
||||
}
|
||||
return fullPath;
|
||||
}
|
||||
|
||||
DWORD GetProcessIdByName(const std::wstring& processName)
|
||||
{
|
||||
DWORD pid = 0;
|
||||
HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
|
||||
|
||||
if (snapshot != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
PROCESSENTRY32 processEntry;
|
||||
processEntry.dwSize = sizeof(PROCESSENTRY32);
|
||||
|
||||
if (Process32First(snapshot, &processEntry))
|
||||
if (package.Id().FullName() == packageFullName)
|
||||
{
|
||||
do
|
||||
// Get the AppListEntry for the package.
|
||||
auto getAppListEntriesOperation = package.GetAppListEntriesAsync();
|
||||
auto appEntries = getAppListEntriesOperation.get();
|
||||
|
||||
// Launch the first app in the list.
|
||||
if (appEntries.Size() > 0)
|
||||
{
|
||||
if (_wcsicmp(processEntry.szExeFile, processName.c_str()) == 0)
|
||||
{
|
||||
pid = processEntry.th32ProcessID;
|
||||
break;
|
||||
}
|
||||
} while (Process32Next(snapshot, &processEntry));
|
||||
}
|
||||
|
||||
CloseHandle(snapshot);
|
||||
}
|
||||
|
||||
return pid;
|
||||
}
|
||||
|
||||
std::vector<DWORD> GetProcessesIdByName(const std::wstring& processName)
|
||||
{
|
||||
std::vector<DWORD> processIds;
|
||||
HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
|
||||
|
||||
if (snapshot != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
PROCESSENTRY32 processEntry;
|
||||
processEntry.dwSize = sizeof(PROCESSENTRY32);
|
||||
|
||||
if (Process32First(snapshot, &processEntry))
|
||||
{
|
||||
do
|
||||
{
|
||||
if (_wcsicmp(processEntry.szExeFile, processName.c_str()) == 0)
|
||||
{
|
||||
processIds.push_back(processEntry.th32ProcessID);
|
||||
}
|
||||
} while (Process32Next(snapshot, &processEntry));
|
||||
}
|
||||
|
||||
CloseHandle(snapshot);
|
||||
}
|
||||
|
||||
return processIds;
|
||||
}
|
||||
|
||||
struct handle_data
|
||||
{
|
||||
unsigned long process_id;
|
||||
HWND window_handle;
|
||||
};
|
||||
|
||||
// used by FindMainWindow
|
||||
BOOL CALLBACK EnumWindowsCallbackAllowNonVisible(HWND handle, LPARAM lParam)
|
||||
{
|
||||
handle_data& data = *reinterpret_cast<handle_data*>(lParam);
|
||||
unsigned long process_id = 0;
|
||||
GetWindowThreadProcessId(handle, &process_id);
|
||||
|
||||
if (data.process_id == process_id)
|
||||
{
|
||||
data.window_handle = handle;
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// used by FindMainWindow
|
||||
BOOL CALLBACK EnumWindowsCallback(HWND handle, LPARAM lParam)
|
||||
{
|
||||
handle_data& data = *reinterpret_cast<handle_data*>(lParam);
|
||||
unsigned long process_id = 0;
|
||||
GetWindowThreadProcessId(handle, &process_id);
|
||||
|
||||
if (data.process_id != process_id || !(GetWindow(handle, GW_OWNER) == static_cast<HWND>(0) && IsWindowVisible(handle)))
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
data.window_handle = handle;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// used for reactivating a window for a program we already started.
|
||||
HWND FindMainWindow(unsigned long process_id, const bool allowNonVisible)
|
||||
{
|
||||
handle_data data;
|
||||
data.process_id = process_id;
|
||||
data.window_handle = 0;
|
||||
|
||||
if (allowNonVisible)
|
||||
{
|
||||
EnumWindows(EnumWindowsCallbackAllowNonVisible, reinterpret_cast<LPARAM>(&data));
|
||||
}
|
||||
else
|
||||
{
|
||||
EnumWindows(EnumWindowsCallback, reinterpret_cast<LPARAM>(&data));
|
||||
}
|
||||
|
||||
return data.window_handle;
|
||||
}
|
||||
|
||||
bool ShowProgram(DWORD pid, std::wstring programName, bool isNewProcess, bool minimizeIfVisible, const RECT& windowPosition, int retryCount)
|
||||
{
|
||||
// a good place to look for this...
|
||||
// https://github.com/ritchielawrence/cmdow
|
||||
|
||||
// try by main window.
|
||||
auto allowNonVisible = true;
|
||||
HWND hwnd = FindMainWindow(pid, allowNonVisible);
|
||||
|
||||
if (hwnd == NULL)
|
||||
{
|
||||
if (retryCount < 20)
|
||||
{
|
||||
auto future = std::async(std::launch::async, [=] {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
||||
ShowProgram(pid, programName, isNewProcess, minimizeIfVisible, windowPosition, retryCount + 1);
|
||||
return false;
|
||||
});
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (hwnd == GetForegroundWindow())
|
||||
{
|
||||
// only hide if this was a call from a already open program, don't make small if we just opened it.
|
||||
if (!isNewProcess && minimizeIfVisible)
|
||||
{
|
||||
return ShowWindow(hwnd, SW_MINIMIZE);
|
||||
}
|
||||
|
||||
FancyZones::SizeWindowToRect(hwnd, windowPosition, false);
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Check if the window is minimized
|
||||
if (IsIconic(hwnd))
|
||||
{
|
||||
// Show the window since SetForegroundWindow fails on minimized windows
|
||||
if (!ShowWindow(hwnd, SW_RESTORE))
|
||||
{
|
||||
std::wcout << L"ShowWindow failed" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
//INPUT inputs[1] = { {.type = INPUT_MOUSE } };
|
||||
//SendInput(ARRAYSIZE(inputs), inputs, sizeof(INPUT));
|
||||
|
||||
if (!SetForegroundWindow(hwnd))
|
||||
{
|
||||
return false;
|
||||
IAsyncOperation<bool> launchOperation = appEntries.GetAt(0).LaunchAsync();
|
||||
bool launchResult = launchOperation.get();
|
||||
return launchResult;
|
||||
}
|
||||
else
|
||||
{
|
||||
FancyZones::SizeWindowToRect(hwnd, windowPosition, false);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isNewProcess)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (false)
|
||||
{
|
||||
// try by console.
|
||||
hwnd = FindWindow(nullptr, nullptr);
|
||||
if (AttachConsole(pid))
|
||||
{
|
||||
// Get the console window handle
|
||||
hwnd = GetConsoleWindow();
|
||||
auto showByConsoleSuccess = false;
|
||||
if (hwnd != NULL)
|
||||
{
|
||||
ShowWindow(hwnd, SW_RESTORE);
|
||||
if (SetForegroundWindow(hwnd))
|
||||
{
|
||||
showByConsoleSuccess = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Detach from the console
|
||||
FreeConsole();
|
||||
if (showByConsoleSuccess)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// try to just show them all (if they have a title)!.
|
||||
hwnd = FindWindow(nullptr, nullptr);
|
||||
if (hwnd)
|
||||
{
|
||||
while (hwnd)
|
||||
{
|
||||
DWORD pidForHwnd;
|
||||
GetWindowThreadProcessId(hwnd, &pidForHwnd);
|
||||
if (pid == pidForHwnd)
|
||||
{
|
||||
int length = GetWindowTextLength(hwnd);
|
||||
|
||||
if (length > 0)
|
||||
{
|
||||
ShowWindow(hwnd, SW_RESTORE);
|
||||
|
||||
// hwnd is the window handle with targetPid
|
||||
if (SetForegroundWindow(hwnd))
|
||||
{
|
||||
FancyZones::SizeWindowToRect(hwnd, windowPosition, false);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
hwnd = FindWindowEx(NULL, hwnd, NULL, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool HideProgram(DWORD pid, std::wstring programName, int retryCount)
|
||||
{
|
||||
HWND hwnd = FindMainWindow(pid, false);
|
||||
if (hwnd == NULL)
|
||||
{
|
||||
if (retryCount < 20)
|
||||
{
|
||||
auto future = std::async(std::launch::async, [=] {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
||||
HideProgram(pid, programName, retryCount + 1);
|
||||
std::wcout << L"No app entries found for the package." << std::endl;
|
||||
return false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
hwnd = FindWindow(nullptr, nullptr);
|
||||
while (hwnd)
|
||||
{
|
||||
DWORD pidForHwnd;
|
||||
GetWindowThreadProcessId(hwnd, &pidForHwnd);
|
||||
if (pid == pidForHwnd)
|
||||
{
|
||||
if (IsWindowVisible(hwnd))
|
||||
{
|
||||
ShowWindow(hwnd, SW_HIDE);
|
||||
}
|
||||
}
|
||||
hwnd = FindWindowEx(NULL, hwnd, NULL, NULL);
|
||||
}
|
||||
}
|
||||
catch (const hresult_error& ex)
|
||||
{
|
||||
std::wcerr << L"Error: " << ex.message().c_str() << std::endl;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Launch(const Project::Application& app)
|
||||
{
|
||||
bool launched = false;
|
||||
if (!app.packageFullName.empty() && app.commandLineArgs.empty())
|
||||
{
|
||||
std::wcout << L"Launching packaged " << app.name << std::endl;
|
||||
launched = LaunchPackagedApp(app.packageFullName);
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: verify app path is up to date.
|
||||
// Packaged apps have version in the path, it will be outdated after update.
|
||||
std::wcout << L"Launching " << app.name << " at " << app.path << std::endl;
|
||||
|
||||
DWORD dwAttrib = GetFileAttributesW(app.path.c_str());
|
||||
if (dwAttrib == INVALID_FILE_ATTRIBUTES)
|
||||
{
|
||||
std::wcout << L" File not found at " << app.path << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
launched = LaunchApp(app.path, app.commandLineArgs);
|
||||
}
|
||||
}
|
||||
|
||||
void Launch(const std::wstring& appPath, bool startMinimized, const std::wstring& commandLineArgs, const RECT& windowPosition) noexcept
|
||||
{
|
||||
WCHAR fullExpandedFilePath[MAX_PATH];
|
||||
ExpandEnvironmentStrings(appPath.c_str(), fullExpandedFilePath, MAX_PATH);
|
||||
|
||||
auto fileNamePart = KBM::GetFileNameFromPath(fullExpandedFilePath);
|
||||
if (launched)
|
||||
{
|
||||
std::wcout << L"Launched " << app.name << std::endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::wcout << L"Failed to launch " << app.name << std::endl;
|
||||
}
|
||||
|
||||
DWORD dwAttrib = GetFileAttributesW(fullExpandedFilePath);
|
||||
if (dwAttrib == INVALID_FILE_ATTRIBUTES)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
DWORD processId = 0;
|
||||
if (Common::Utils::Elevation::run_non_elevated(fullExpandedFilePath, commandLineArgs, &processId, nullptr, !startMinimized))
|
||||
{
|
||||
std::wcout << "Launched " << fileNamePart << std::endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::wcout << "Failed to launch " << fileNamePart << std::endl;
|
||||
}
|
||||
|
||||
if (processId == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
auto targetPid = KBM::GetProcessIdByName(fileNamePart);
|
||||
if (!startMinimized)
|
||||
{
|
||||
KBM::ShowProgram(targetPid, fileNamePart, false, false, windowPosition, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
KBM::HideProgram(targetPid, fileNamePart, 0);
|
||||
}
|
||||
|
||||
return;
|
||||
return launched;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include <Windows.h>
|
||||
#include "../projects-common/Data.h"
|
||||
|
||||
void Launch(const std::wstring& appPath, bool startMinimized, const std::wstring& commandLineArgs, const RECT& rect) noexcept;
|
||||
bool Launch(const Project::Application& app);
|
||||
@@ -51,7 +51,7 @@ int main(int argc, char* argv[])
|
||||
// launch apps
|
||||
for (const auto& app : projectToLaunch.apps)
|
||||
{
|
||||
Launch(app.appPath, app.isMinimized, app.commandLineArgs, app.position.toRect());
|
||||
Launch(app);
|
||||
}
|
||||
|
||||
// update last-launched time
|
||||
|
||||
169
src/modules/Projects/ProjectsSnapshotTool/PackagedAppUtils.cpp
Normal file
169
src/modules/Projects/ProjectsSnapshotTool/PackagedAppUtils.cpp
Normal file
@@ -0,0 +1,169 @@
|
||||
#include "pch.h"
|
||||
#include "PackagedAppUtils.h"
|
||||
|
||||
#include <windows.h>
|
||||
#include <winreg.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <filesystem>
|
||||
|
||||
#include <atlbase.h>
|
||||
#include <wil/registry.h>
|
||||
#include <ShlObj.h>
|
||||
#include <propvarutil.h>
|
||||
|
||||
namespace Utils
|
||||
{
|
||||
namespace Apps
|
||||
{
|
||||
namespace NonLocalizable
|
||||
{
|
||||
const wchar_t* PackageFullNameProp = L"System.AppUserModel.PackageFullName";
|
||||
const wchar_t* PackageInstallPathProp = L"System.AppUserModel.PackageInstallPath";
|
||||
const wchar_t* InstallPathProp = L"System.Link.TargetParsingPath";
|
||||
|
||||
const wchar_t* FileExplorerName = L"File Explorer";
|
||||
const wchar_t* FileExplorerPath = L"C:\\WINDOWS\\EXPLORER.EXE";
|
||||
}
|
||||
|
||||
AppList IterateAppsFolder()
|
||||
{
|
||||
AppList result{};
|
||||
|
||||
// get apps folder
|
||||
CComPtr<IShellItem> folder;
|
||||
HRESULT hr = SHGetKnownFolderItem(FOLDERID_AppsFolder, KF_FLAG_DEFAULT, nullptr, IID_PPV_ARGS(&folder));
|
||||
if (FAILED(hr))
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
CComPtr<IEnumShellItems> enumItems;
|
||||
hr = folder->BindToHandler(nullptr, BHID_EnumItems, IID_PPV_ARGS(&enumItems));
|
||||
if (FAILED(hr))
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
IShellItem* items;
|
||||
while (enumItems->Next(1, &items, nullptr) == S_OK)
|
||||
{
|
||||
CComPtr<IShellItem> item = items;
|
||||
CComHeapPtr<wchar_t> name;
|
||||
|
||||
if (FAILED(item->GetDisplayName(SIGDN_NORMALDISPLAY, &name)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
std::wcout << name.m_pData << std::endl;
|
||||
AppData data
|
||||
{
|
||||
.name = std::wstring(name.m_pData),
|
||||
};
|
||||
|
||||
// properties
|
||||
CComPtr<IPropertyStore> store;
|
||||
if (FAILED(item->BindToHandler(NULL, BHID_PropertyStore, IID_PPV_ARGS(&store))))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
DWORD count = 0;
|
||||
store->GetCount(&count);
|
||||
for (DWORD i = 0; i < count; i++)
|
||||
{
|
||||
PROPERTYKEY pk;
|
||||
if (FAILED(store->GetAt(i, &pk)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
CComHeapPtr<wchar_t> pkName;
|
||||
PSGetNameFromPropertyKey(pk, &pkName);
|
||||
|
||||
std::wstring prop(pkName.m_pData);
|
||||
if (prop == NonLocalizable::PackageFullNameProp ||
|
||||
prop == NonLocalizable::PackageInstallPathProp ||
|
||||
prop == NonLocalizable::InstallPathProp)
|
||||
{
|
||||
PROPVARIANT pv;
|
||||
PropVariantInit(&pv);
|
||||
if (SUCCEEDED(store->GetValue(pk, &pv)))
|
||||
{
|
||||
CComHeapPtr<wchar_t> propVariantString;
|
||||
propVariantString.Allocate(512);
|
||||
PropVariantToString(pv, propVariantString, 512);
|
||||
PropVariantClear(&pv);
|
||||
|
||||
if (prop == NonLocalizable::PackageFullNameProp)
|
||||
{
|
||||
data.packageFullName = propVariantString.m_pData;
|
||||
}
|
||||
else if (prop == NonLocalizable::PackageInstallPathProp || prop == NonLocalizable::InstallPathProp)
|
||||
{
|
||||
data.installPath = propVariantString.m_pData;
|
||||
}
|
||||
}
|
||||
|
||||
if (!data.packageFullName.empty() && !data.installPath.empty())
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!data.name.empty())
|
||||
{
|
||||
result.push_back(data);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
AppList Utils::Apps::GetAppsList()
|
||||
{
|
||||
return IterateAppsFolder();
|
||||
}
|
||||
|
||||
std::optional<AppData> GetApp(const std::wstring& appPath, const AppList& apps)
|
||||
{
|
||||
for (const auto& appData : apps)
|
||||
{
|
||||
std::wstring appPathUpper(appPath);
|
||||
std::transform(appPathUpper.begin(), appPathUpper.end(), appPathUpper.begin(), towupper);
|
||||
|
||||
// edge case, "Windows Software Development Kit" has the same app path as "File Explorer"
|
||||
if (appPathUpper == NonLocalizable::FileExplorerPath)
|
||||
{
|
||||
return AppData
|
||||
{
|
||||
.name = NonLocalizable::FileExplorerName,
|
||||
.installPath = appPath,
|
||||
};
|
||||
}
|
||||
|
||||
std::wstring installPathUpper(appData.installPath);
|
||||
std::transform(installPathUpper.begin(), installPathUpper.end(), installPathUpper.begin(), towupper);
|
||||
|
||||
if (appPathUpper.contains(installPathUpper))
|
||||
{
|
||||
return appData;
|
||||
}
|
||||
|
||||
// edge case, some apps (e.g., Gitkraken) have different .exe files in the subfolders.
|
||||
// apps list contains only one path, so in this case app is not found
|
||||
if (std::filesystem::path(appPath).filename() == std::filesystem::path(appData.installPath).filename())
|
||||
{
|
||||
return appData;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: not all installed apps found
|
||||
return AppData {
|
||||
.installPath = appPath
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
19
src/modules/Projects/ProjectsSnapshotTool/PackagedAppUtils.h
Normal file
19
src/modules/Projects/ProjectsSnapshotTool/PackagedAppUtils.h
Normal file
@@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
namespace Utils
|
||||
{
|
||||
namespace Apps
|
||||
{
|
||||
struct AppData
|
||||
{
|
||||
std::wstring name;
|
||||
std::wstring installPath;
|
||||
std::wstring packageFullName;
|
||||
};
|
||||
|
||||
using AppList = std::vector<AppData>; // path; data
|
||||
|
||||
AppList GetAppsList();
|
||||
std::optional<AppData> GetApp(const std::wstring& appPath, const AppList& apps);
|
||||
}
|
||||
}
|
||||
@@ -70,7 +70,7 @@
|
||||
<PreprocessorDefinitions>_CONSOLE;WIN32_LEAN_AND_MEAN;WINRT_LEAN_AND_MEAN;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<WarningLevel>Level4</WarningLevel>
|
||||
<AdditionalOptions>%(AdditionalOptions) /permissive- /bigobj</AdditionalOptions>
|
||||
<LanguageStandard>stdcpplatest</LanguageStandard>
|
||||
<LanguageStandard>stdcpplatest</LanguageStandard>
|
||||
</ClCompile>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)'=='Debug'">
|
||||
@@ -81,7 +81,7 @@
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateWindowsMetadata>false</GenerateWindowsMetadata>
|
||||
<AdditionalDependencies Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">shcore.lib;Shell32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalDependencies Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">shcore.lib;Shell32.lib;propsys.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Platform)'=='Win32'">
|
||||
@@ -101,7 +101,7 @@
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateWindowsMetadata>false</GenerateWindowsMetadata>
|
||||
<AdditionalDependencies Condition="'$(Configuration)|$(Platform)'=='Release|x64'">shcore.lib;Shell32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalDependencies Condition="'$(Configuration)|$(Platform)'=='Release|x64'">shcore.lib;Shell32.lib;propsys.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)'=='Debug'">
|
||||
@@ -117,6 +117,7 @@
|
||||
<ItemGroup>
|
||||
<ClInclude Include="MonitorUtils.h" />
|
||||
<ClInclude Include="OnThreadExecutor.h" />
|
||||
<ClInclude Include="PackagedAppUtils.h" />
|
||||
<ClInclude Include="pch.h" />
|
||||
<ClInclude Include="VirtualDesktop.h" />
|
||||
<ClInclude Include="WindowFilter.h" />
|
||||
@@ -126,6 +127,7 @@
|
||||
<ClCompile Include="main.cpp" />
|
||||
<ClCompile Include="MonitorUtils.cpp" />
|
||||
<ClCompile Include="OnThreadExecutor.cpp" />
|
||||
<ClCompile Include="PackagedAppUtils.cpp" />
|
||||
<ClCompile Include="pch.cpp">
|
||||
<PrecompiledHeader>Create</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
@@ -138,7 +140,8 @@
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
<Import Project="..\packages\Microsoft.Windows.CppWinRT.2.0.220531.1\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('..\packages\Microsoft.Windows.CppWinRT.2.0.220531.1\build\native\Microsoft.Windows.CppWinRT.targets')" />
|
||||
<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>
|
||||
@@ -146,5 +149,6 @@
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('..\packages\Microsoft.Windows.CppWinRT.2.0.220531.1\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Windows.CppWinRT.2.0.220531.1\build\native\Microsoft.Windows.CppWinRT.props'))" />
|
||||
<Error Condition="!Exists('..\packages\Microsoft.Windows.CppWinRT.2.0.220531.1\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Windows.CppWinRT.2.0.220531.1\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>
|
||||
@@ -33,6 +33,9 @@
|
||||
<ClInclude Include="OnThreadExecutor.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="PackagedAppUtils.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="pch.cpp">
|
||||
@@ -53,9 +56,15 @@
|
||||
<ClCompile Include="OnThreadExecutor.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="PackagedAppUtils.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="PropertySheet.props" />
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Natvis Include="$(MSBuildThisFileDirectory)..\..\natvis\wil.natvis" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "../projects-common/WindowEnumerator.h"
|
||||
|
||||
#include "MonitorUtils.h"
|
||||
#include "PackagedAppUtils.h"
|
||||
#include "WindowFilter.h"
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
@@ -77,6 +78,9 @@ int main(int argc, char* argv[])
|
||||
// get list of windows
|
||||
auto windows = WindowEnumerator::Enumerate(WindowFilter::Filter);
|
||||
|
||||
// get installed apps list
|
||||
auto apps = Utils::Apps::GetAppsList();
|
||||
|
||||
for (const auto& window : windows)
|
||||
{
|
||||
// filter by window rect size
|
||||
@@ -95,7 +99,13 @@ int main(int argc, char* argv[])
|
||||
|
||||
// filter by app path
|
||||
std::wstring processPath = Common::Utils::ProcessPath::get_process_path_waiting_uwp(window);
|
||||
if (WindowUtils::IsExcludedByDefault(window, processPath, title))
|
||||
if (processPath.empty() || WindowUtils::IsExcludedByDefault(window, processPath, title))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
auto data = Utils::Apps::GetApp(processPath, apps);
|
||||
if (!data.has_value())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -112,9 +122,10 @@ int main(int argc, char* argv[])
|
||||
}
|
||||
|
||||
Project::Application app {
|
||||
.hwnd = window,
|
||||
.appPath = processPath,
|
||||
.appTitle = title,
|
||||
.name = data.value().name,
|
||||
.title = title,
|
||||
.path = data.value().installPath,
|
||||
.packageFullName = data.value().packageFullName,
|
||||
.commandLineArgs = L"",
|
||||
.isMinimized = WindowUtils::IsMinimized(window),
|
||||
.isMaximized = WindowUtils::IsMaximized(window),
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Microsoft.Windows.CppWinRT" version="2.0.220531.1" targetFramework="native" />
|
||||
<package id="Microsoft.Windows.ImplementationLibrary" version="1.0.231216.1" targetFramework="native" />
|
||||
</packages>
|
||||
@@ -23,9 +23,10 @@ struct Project
|
||||
}
|
||||
};
|
||||
|
||||
HWND hwnd{};
|
||||
std::wstring appPath;
|
||||
std::wstring appTitle;
|
||||
std::wstring name;
|
||||
std::wstring title;
|
||||
std::wstring path;
|
||||
std::wstring packageFullName;
|
||||
std::wstring commandLineArgs;
|
||||
bool isMinimized{};
|
||||
bool isMaximized{};
|
||||
@@ -120,8 +121,9 @@ namespace JsonUtils
|
||||
|
||||
namespace NonLocalizable
|
||||
{
|
||||
const static wchar_t* AppPathID = L"application";
|
||||
const static wchar_t* HwndID = L"hwnd";
|
||||
const static wchar_t* AppNameID = L"application";
|
||||
const static wchar_t* AppPathID = L"application-path";
|
||||
const static wchar_t* AppPackageFullNameID = L"package-full-name";
|
||||
const static wchar_t* AppTitleID = L"title";
|
||||
const static wchar_t* CommandLineArgsID = L"command-line-arguments";
|
||||
const static wchar_t* MinimizedID = L"minimized";
|
||||
@@ -133,9 +135,10 @@ namespace JsonUtils
|
||||
inline json::JsonObject ToJson(const Project::Application& data)
|
||||
{
|
||||
json::JsonObject json{};
|
||||
json.SetNamedValue(NonLocalizable::HwndID, json::value(static_cast<double>(reinterpret_cast<long long>(data.hwnd))));
|
||||
json.SetNamedValue(NonLocalizable::AppPathID, json::value(data.appPath));
|
||||
json.SetNamedValue(NonLocalizable::AppTitleID, json::value(data.appTitle));
|
||||
json.SetNamedValue(NonLocalizable::AppNameID, json::value(data.name));
|
||||
json.SetNamedValue(NonLocalizable::AppPathID, json::value(data.path));
|
||||
json.SetNamedValue(NonLocalizable::AppTitleID, json::value(data.title));
|
||||
json.SetNamedValue(NonLocalizable::AppPackageFullNameID, json::value(data.packageFullName));
|
||||
json.SetNamedValue(NonLocalizable::CommandLineArgsID, json::value(data.commandLineArgs));
|
||||
json.SetNamedValue(NonLocalizable::MinimizedID, json::value(data.isMinimized));
|
||||
json.SetNamedValue(NonLocalizable::MaximizedID, json::value(data.isMaximized));
|
||||
@@ -150,9 +153,18 @@ namespace JsonUtils
|
||||
Project::Application result;
|
||||
try
|
||||
{
|
||||
result.hwnd = reinterpret_cast<HWND>(static_cast<long long>(json.GetNamedNumber(NonLocalizable::HwndID)));
|
||||
result.appPath = json.GetNamedString(NonLocalizable::AppPathID);
|
||||
result.appTitle = json.GetNamedString(NonLocalizable::AppTitleID);
|
||||
if (json.HasKey(NonLocalizable::AppNameID))
|
||||
{
|
||||
result.name = json.GetNamedString(NonLocalizable::AppNameID);
|
||||
}
|
||||
|
||||
result.path = json.GetNamedString(NonLocalizable::AppPathID);
|
||||
result.title = json.GetNamedString(NonLocalizable::AppTitleID);
|
||||
if (json.HasKey(NonLocalizable::AppPackageFullNameID))
|
||||
{
|
||||
result.packageFullName = json.GetNamedString(NonLocalizable::AppPackageFullNameID);
|
||||
}
|
||||
|
||||
result.commandLineArgs = json.GetNamedString(NonLocalizable::CommandLineArgsID);
|
||||
result.isMaximized = json.GetNamedBoolean(NonLocalizable::MaximizedID);
|
||||
result.isMinimized = json.GetNamedBoolean(NonLocalizable::MinimizedID);
|
||||
|
||||
Reference in New Issue
Block a user