mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-02-24 04:00:02 +01:00
Merge branch 'dev/feature/projects' of https://github.com/microsoft/PowerToys into dev/feature/projects
This commit is contained in:
@@ -33,7 +33,7 @@ namespace ProjectsEditor.Data
|
||||
|
||||
public string CommandLineArguments { get; set; }
|
||||
|
||||
public bool LaunchesAsAdmin { get; set; }
|
||||
public bool IsElevated { get; set; }
|
||||
|
||||
public bool Minimized { get; set; }
|
||||
|
||||
|
||||
@@ -47,14 +47,14 @@ namespace ProjectsEditor.Models
|
||||
|
||||
public string CommandLineArguments { get; set; }
|
||||
|
||||
private bool _launchesAsAdmin;
|
||||
private bool _isElevated;
|
||||
|
||||
public bool LaunchesAsAdmin
|
||||
public bool IsElevated
|
||||
{
|
||||
get => _launchesAsAdmin;
|
||||
get => _isElevated;
|
||||
set
|
||||
{
|
||||
_launchesAsAdmin = value;
|
||||
_isElevated = value;
|
||||
OnPropertyChanged(new PropertyChangedEventArgs(nameof(AppMainParams)));
|
||||
}
|
||||
}
|
||||
@@ -109,7 +109,7 @@ namespace ProjectsEditor.Models
|
||||
{
|
||||
get
|
||||
{
|
||||
_appMainParams = _launchesAsAdmin ? Properties.Resources.Admin : string.Empty;
|
||||
_appMainParams = _isElevated ? Properties.Resources.Admin : string.Empty;
|
||||
if (!string.IsNullOrWhiteSpace(CommandLineArguments))
|
||||
{
|
||||
_appMainParams += (_appMainParams == string.Empty ? string.Empty : " | ") + Properties.Resources.Args + ": " + CommandLineArguments;
|
||||
|
||||
@@ -231,7 +231,7 @@ namespace ProjectsEditor.Models
|
||||
AppTitle = item.AppTitle,
|
||||
CommandLineArguments = item.CommandLineArguments,
|
||||
PackageFullName = item.PackageFullName,
|
||||
LaunchesAsAdmin = item.LaunchesAsAdmin,
|
||||
IsElevated = item.IsElevated,
|
||||
Minimized = item.Minimized,
|
||||
Maximized = item.Maximized,
|
||||
MonitorNumber = item.MonitorNumber,
|
||||
|
||||
@@ -160,11 +160,10 @@
|
||||
Margin="100 5 0 0">
|
||||
<CheckBox
|
||||
Content="{x:Static props:Resources.LaunchAsAdmin}"
|
||||
IsChecked="{Binding LaunchesAsAdmin, Mode=TwoWay}"
|
||||
Visibility="Collapsed"
|
||||
IsChecked="{Binding IsElevated, Mode=TwoWay}"
|
||||
MinWidth="10"/>
|
||||
<CheckBox
|
||||
Margin="0 0 0 0"
|
||||
Margin="15 0 0 0"
|
||||
Content="{x:Static props:Resources.Maximized}"
|
||||
IsChecked="{Binding Maximized, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
|
||||
MinWidth="10"
|
||||
|
||||
@@ -18,7 +18,7 @@ namespace ProjectsEditor
|
||||
/// </summary>
|
||||
public partial class ProjectEditor : Page
|
||||
{
|
||||
private const double ScrollSpeed = 20;
|
||||
private const double ScrollSpeed = 15;
|
||||
private MainViewModel _mainViewModel;
|
||||
|
||||
public ProjectEditor(MainViewModel mainViewModel)
|
||||
|
||||
@@ -94,7 +94,7 @@ namespace ProjectsEditor.Utils
|
||||
PackageFullName = app.PackageFullName,
|
||||
Parent = newProject,
|
||||
CommandLineArguments = app.CommandLineArguments,
|
||||
LaunchesAsAdmin = app.LaunchesAsAdmin,
|
||||
IsElevated = app.IsElevated,
|
||||
Maximized = app.Maximized,
|
||||
Minimized = app.Minimized,
|
||||
IsNotFound = false,
|
||||
@@ -149,7 +149,7 @@ namespace ProjectsEditor.Utils
|
||||
Title = app.AppTitle,
|
||||
PackageFullName = app.PackageFullName,
|
||||
CommandLineArguments = app.CommandLineArguments,
|
||||
LaunchesAsAdmin = app.LaunchesAsAdmin,
|
||||
IsElevated = app.IsElevated,
|
||||
Maximized = app.Maximized,
|
||||
Minimized = app.Minimized,
|
||||
Position = new ProjectData.ApplicationWrapper.WindowPositionWrapper
|
||||
|
||||
@@ -134,53 +134,25 @@ namespace FancyZones
|
||||
}
|
||||
}
|
||||
|
||||
bool LaunchApp(const std::wstring& appPath, const std::wstring& commandLineArgs)
|
||||
bool LaunchApp(const std::wstring& appPath, const std::wstring& commandLineArgs, bool elevated)
|
||||
{
|
||||
STARTUPINFO startupInfo;
|
||||
ZeroMemory(&startupInfo, sizeof(startupInfo));
|
||||
startupInfo.cb = sizeof(startupInfo);
|
||||
SHELLEXECUTEINFO sei = { 0 };
|
||||
sei.cbSize = sizeof(SHELLEXECUTEINFO);
|
||||
sei.hwnd = nullptr;
|
||||
sei.fMask = SEE_MASK_NOCLOSEPROCESS | SEE_MASK_NO_CONSOLE;
|
||||
sei.lpVerb = elevated ? L"runas" : L"open";
|
||||
sei.lpFile = appPath.c_str();
|
||||
sei.lpParameters = commandLineArgs.c_str();
|
||||
sei.lpDirectory = nullptr;
|
||||
sei.nShow = SW_SHOWNORMAL;
|
||||
|
||||
PROCESS_INFORMATION processInfo;
|
||||
ZeroMemory(&processInfo, sizeof(processInfo));
|
||||
|
||||
std::wstring fullCommandLine = L"\"" + appPath + L"\" " + commandLineArgs;
|
||||
if (CreateProcess(nullptr, fullCommandLine.data(), nullptr, nullptr, FALSE, 0, nullptr, nullptr, &startupInfo, &processInfo))
|
||||
{
|
||||
CloseHandle(processInfo.hProcess);
|
||||
CloseHandle(processInfo.hThread);
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
if (!ShellExecuteEx(&sei))
|
||||
{
|
||||
Logger::error(L"Failed to launch process. {}", get_last_error_or_default(GetLastError()));
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool LaunchUsingUriProtocolName(const std::wstring& uriProtocolName, const std::wstring& commandLineArgs)
|
||||
{
|
||||
std::wstring command = std::wstring(uriProtocolName + (commandLineArgs.starts_with(L":") ? L"" : L":") + commandLineArgs);
|
||||
HINSTANCE result = ShellExecute(
|
||||
nullptr,
|
||||
L"open",
|
||||
command.c_str(),
|
||||
nullptr,
|
||||
nullptr,
|
||||
SW_SHOWNORMAL
|
||||
);
|
||||
|
||||
// If the function succeeds, it returns a value greater than 32.
|
||||
if (result > reinterpret_cast<HINSTANCE>(32))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger::error(L"Failed to launch process. {}", get_last_error_or_default(GetLastError()));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LaunchPackagedApp(const std::wstring& packageFullName)
|
||||
@@ -219,24 +191,29 @@ bool LaunchPackagedApp(const std::wstring& packageFullName)
|
||||
bool Launch(const Project::Application& app)
|
||||
{
|
||||
bool launched { false };
|
||||
if (!app.packageFullName.empty() && app.commandLineArgs.empty())
|
||||
if (!app.packageFullName.empty() && app.commandLineArgs.empty() && !app.isElevated)
|
||||
{
|
||||
Logger::trace(L"Launching packaged without command line args {}", app.name);
|
||||
Logger::trace(L"Launching packaged app {}", app.name);
|
||||
launched = LaunchPackagedApp(app.packageFullName);
|
||||
}
|
||||
else if (!app.packageFullName.empty() && !app.commandLineArgs.empty())
|
||||
|
||||
if (!launched && !app.packageFullName.empty())
|
||||
{
|
||||
auto names = RegistryUtils::GetUriProtocolNames(app.packageFullName);
|
||||
if (!names.empty())
|
||||
{
|
||||
Logger::trace(L"Launching packaged by protocol with command line args {}", app.name);
|
||||
launched = LaunchUsingUriProtocolName(names[0], app.commandLineArgs);
|
||||
}
|
||||
|
||||
std::wstring uriProtocolName = names[0];
|
||||
std::wstring command = std::wstring(uriProtocolName + (app.commandLineArgs.starts_with(L":") ? L"" : L":") + app.commandLineArgs);
|
||||
|
||||
launched = LaunchApp(command, L"", app.isElevated);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger::info(L"Uri protocol names not found for {}", app.packageFullName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!launched)
|
||||
{
|
||||
@@ -249,7 +226,7 @@ bool Launch(const Project::Application& app)
|
||||
return false;
|
||||
}
|
||||
|
||||
launched = LaunchApp(app.path, app.commandLineArgs);
|
||||
launched = LaunchApp(app.path, app.commandLineArgs, app.isElevated);
|
||||
}
|
||||
|
||||
Logger::trace(L"{} {} at {}", app.name, (launched ? L"launched" : L"not launched"), app.path);
|
||||
|
||||
@@ -24,6 +24,13 @@ int APIENTRY WinMain(HINSTANCE hInst, HINSTANCE hInstPrev, LPSTR cmdline, int cm
|
||||
return 0;
|
||||
}
|
||||
|
||||
// COM should be initialized before ShellExecuteEx is called.
|
||||
if (FAILED(CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE)))
|
||||
{
|
||||
Logger::error("CoInitializeEx failed");
|
||||
return 1;
|
||||
}
|
||||
|
||||
SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
|
||||
|
||||
// read projects
|
||||
@@ -91,5 +98,6 @@ int APIENTRY WinMain(HINSTANCE hInst, HINSTANCE hInstPrev, LPSTR cmdline, int cm
|
||||
}
|
||||
json::to_file(JsonUtils::ProjectsFile(), JsonUtils::ProjectsListJSON::ToJson(projects));
|
||||
|
||||
CoUninitialize();
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -147,6 +147,32 @@ namespace SnapshotUtils
|
||||
return commandLineArgs;
|
||||
}
|
||||
|
||||
bool IsProcessElevated(DWORD processID)
|
||||
{
|
||||
HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, processID);
|
||||
if (!hProcess)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
HANDLE hToken = NULL;
|
||||
if (!OpenProcessToken(hProcess, TOKEN_QUERY, &hToken))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
TOKEN_ELEVATION elevation;
|
||||
DWORD cbSize = sizeof(TOKEN_ELEVATION);
|
||||
if (!GetTokenInformation(hToken, TokenElevation, &elevation, sizeof(elevation), &cbSize))
|
||||
{
|
||||
CloseHandle(hToken);
|
||||
return false;
|
||||
}
|
||||
|
||||
CloseHandle(hToken);
|
||||
return elevation.TokenIsElevated;
|
||||
}
|
||||
|
||||
std::vector<Project::Application> GetApps(const std::function<unsigned int(HWND)> getMonitorNumberFromWindowHandle)
|
||||
{
|
||||
std::vector<Project::Application> apps{};
|
||||
@@ -219,6 +245,7 @@ namespace SnapshotUtils
|
||||
.path = processPath,
|
||||
.packageFullName = data.value().packageFullName,
|
||||
.commandLineArgs = L"", // GetCommandLineArgs(pid, wbemHelper),
|
||||
.isElevated = IsProcessElevated(pid),
|
||||
.isMinimized = WindowUtils::IsMinimized(window),
|
||||
.isMaximized = WindowUtils::IsMaximized(window),
|
||||
.position = Project::Application::Position{
|
||||
|
||||
@@ -29,6 +29,7 @@ struct Project
|
||||
std::wstring path;
|
||||
std::wstring packageFullName;
|
||||
std::wstring commandLineArgs;
|
||||
bool isElevated{};
|
||||
bool isMinimized{};
|
||||
bool isMaximized{};
|
||||
Position position{};
|
||||
@@ -137,6 +138,7 @@ namespace JsonUtils
|
||||
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* ElevatedID = L"is-elevated";
|
||||
const static wchar_t* MinimizedID = L"minimized";
|
||||
const static wchar_t* MaximizedID = L"maximized";
|
||||
const static wchar_t* PositionID = L"position";
|
||||
@@ -151,6 +153,7 @@ namespace JsonUtils
|
||||
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::ElevatedID, json::value(data.isElevated));
|
||||
json.SetNamedValue(NonLocalizable::MinimizedID, json::value(data.isMinimized));
|
||||
json.SetNamedValue(NonLocalizable::MaximizedID, json::value(data.isMaximized));
|
||||
json.SetNamedValue(NonLocalizable::PositionID, PositionJSON::ToJson(data.position));
|
||||
@@ -177,6 +180,12 @@ namespace JsonUtils
|
||||
}
|
||||
|
||||
result.commandLineArgs = json.GetNamedString(NonLocalizable::CommandLineArgsID);
|
||||
|
||||
if (json.HasKey(NonLocalizable::ElevatedID))
|
||||
{
|
||||
result.isElevated = json.GetNamedBoolean(NonLocalizable::ElevatedID);
|
||||
}
|
||||
|
||||
result.isMaximized = json.GetNamedBoolean(NonLocalizable::MaximizedID);
|
||||
result.isMinimized = json.GetNamedBoolean(NonLocalizable::MinimizedID);
|
||||
result.monitor = static_cast<int>(json.GetNamedNumber(NonLocalizable::MonitorID));
|
||||
|
||||
Reference in New Issue
Block a user