launcher: option to run as user without elevation (#695)

## Link to GitHub Issue or related Pull Request, if one exists
#694 

## Description of change
Before this change, we have it set in the executable manifest to always
ask for UAC elevation.

This change removes that and instead attempts to prompt for elevation
when we launch, depending on the new developer option that is being
added here to run as current user instead of elevating.

The default behavior continues to run as admin. This is still the safest
in terms of regression risk.

A bit of history on this: while spicecfg never technically needed to
elevate, we continue to do so to avoid difference in behavior when the
game is launched. For example, if the user is using an input remapper
running without elevation, it would inject input into spicecfg running
as user but fail to do so if the game runs elevated.

WinXP builds are unaffected (there is no UAC).

Some features will straight up fail without elevation so this will
remain as a debug option. To name a few (not a complete list):

* reboot/shutdown of PC
* running at realtime process priority
* `-nvprofile` option
* ...

## Testing
This commit is contained in:
bicarus
2026-05-16 16:27:59 -07:00
committed by GitHub
parent 5bec3d5db7
commit e9dcc5717e
7 changed files with 108 additions and 8 deletions
+55
View File
@@ -10,6 +10,9 @@
#include <windows.h>
#undef WIN32_NO_STATUS
#include <shellapi.h>
#include <shlwapi.h>
#include "hooks/graphics/graphics.h"
#include "avs/game.h"
#include "util/detour.h"
@@ -585,4 +588,56 @@ namespace sysutils {
(void*)EnumDisplayDevicesA_hook,
(void**)&EnumDisplayDevicesA_orig);
}
#if !SPICE_XP
bool is_running_as_admin() {
HANDLE token_handle = nullptr;
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token_handle)) {
return false;
}
TOKEN_ELEVATION elevation = {};
DWORD size = sizeof(elevation);
const BOOL ok = GetTokenInformation(
token_handle, TokenElevation, &elevation, size, &size);
CloseHandle(token_handle);
if (!ok) {
return false;
}
return elevation.TokenIsElevated != 0;
}
bool relaunch_as_admin() {
// get the current executable path
wchar_t executable_path[MAX_PATH];
if (!GetModuleFileNameW(nullptr, executable_path, MAX_PATH)) {
return false;
}
// get the current working directory to pass to the elevated process
wchar_t working_dir[MAX_PATH];
if (!GetCurrentDirectoryW(MAX_PATH, working_dir)) {
return false;
}
// skip past argv[0] in the original command line
const wchar_t *args = PathGetArgsW(GetCommandLineW());
// use ShellExecuteW with "runas" verb to elevate
HINSTANCE result = ShellExecuteW(
nullptr,
L"runas",
executable_path,
args,
working_dir,
SW_SHOW
);
// ShellExecute returns > 32 on success
return (intptr_t)result > 32;
}
#endif // !SPICE_XP
}
+9 -2
View File
@@ -21,5 +21,12 @@ namespace sysutils {
const std::vector<MonitorEntry> &enumerate_monitors();
extern std::string SECOND_MONITOR_OVERRIDE;
void hook_EnumDisplayDevicesA();
}
void hook_EnumDisplayDevicesA();
#if !SPICE_XP
bool is_running_as_admin();
bool relaunch_as_admin();
#endif // !SPICE_XP
}