diff --git a/src/modules/Projects/ProjectsEditor/Data/ProjectData.cs b/src/modules/Projects/ProjectsEditor/Data/ProjectData.cs
index 41a3ab6a2c..a7c4171f49 100644
--- a/src/modules/Projects/ProjectsEditor/Data/ProjectData.cs
+++ b/src/modules/Projects/ProjectsEditor/Data/ProjectData.cs
@@ -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; }
diff --git a/src/modules/Projects/ProjectsEditor/Models/Application.cs b/src/modules/Projects/ProjectsEditor/Models/Application.cs
index 84dad01d21..1143b38d04 100644
--- a/src/modules/Projects/ProjectsEditor/Models/Application.cs
+++ b/src/modules/Projects/ProjectsEditor/Models/Application.cs
@@ -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;
diff --git a/src/modules/Projects/ProjectsEditor/Models/Project.cs b/src/modules/Projects/ProjectsEditor/Models/Project.cs
index 4effabbacd..3cc0c9edc5 100644
--- a/src/modules/Projects/ProjectsEditor/Models/Project.cs
+++ b/src/modules/Projects/ProjectsEditor/Models/Project.cs
@@ -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,
diff --git a/src/modules/Projects/ProjectsEditor/ProjectEditorPage.xaml b/src/modules/Projects/ProjectsEditor/ProjectEditorPage.xaml
index 9eabe75d4d..2f0a9e858a 100644
--- a/src/modules/Projects/ProjectsEditor/ProjectEditorPage.xaml
+++ b/src/modules/Projects/ProjectsEditor/ProjectEditorPage.xaml
@@ -160,11 +160,10 @@
Margin="100 5 0 0">
public partial class ProjectEditor : Page
{
- private const double ScrollSpeed = 20;
+ private const double ScrollSpeed = 15;
private MainViewModel _mainViewModel;
public ProjectEditor(MainViewModel mainViewModel)
diff --git a/src/modules/Projects/ProjectsEditor/Utils/ProjectsEditorIO.cs b/src/modules/Projects/ProjectsEditor/Utils/ProjectsEditorIO.cs
index 12a72dd46c..77b98daa9c 100644
--- a/src/modules/Projects/ProjectsEditor/Utils/ProjectsEditorIO.cs
+++ b/src/modules/Projects/ProjectsEditor/Utils/ProjectsEditorIO.cs
@@ -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
diff --git a/src/modules/Projects/ProjectsLauncher/AppLauncher.cpp b/src/modules/Projects/ProjectsLauncher/AppLauncher.cpp
index da1238b0ad..2746a78d06 100644
--- a/src/modules/Projects/ProjectsLauncher/AppLauncher.cpp
+++ b/src/modules/Projects/ProjectsLauncher/AppLauncher.cpp
@@ -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(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);
diff --git a/src/modules/Projects/ProjectsLauncher/main.cpp b/src/modules/Projects/ProjectsLauncher/main.cpp
index 570f987ff1..fb3375640e 100644
--- a/src/modules/Projects/ProjectsLauncher/main.cpp
+++ b/src/modules/Projects/ProjectsLauncher/main.cpp
@@ -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;
}
diff --git a/src/modules/Projects/ProjectsSnapshotTool/SnapshotUtils.cpp b/src/modules/Projects/ProjectsSnapshotTool/SnapshotUtils.cpp
index 1e998c3008..5165f23bd5 100644
--- a/src/modules/Projects/ProjectsSnapshotTool/SnapshotUtils.cpp
+++ b/src/modules/Projects/ProjectsSnapshotTool/SnapshotUtils.cpp
@@ -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 GetApps(const std::function getMonitorNumberFromWindowHandle)
{
std::vector 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{
diff --git a/src/modules/Projects/projects-common/Data.h b/src/modules/Projects/projects-common/Data.h
index 04f08f4c20..108db56bab 100644
--- a/src/modules/Projects/projects-common/Data.h
+++ b/src/modules/Projects/projects-common/Data.h
@@ -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(json.GetNamedNumber(NonLocalizable::MonitorID));