I use Java JNA to execute Windows API commands.
I have a background service running as local-system. From this background service, I'd like to spawn a new process, e.g. notepad.exe on the currently active desktop and user. The new process should not run elevated but with the active logged-in users rights.
I use
- WTSGetActiveConsoleSessionId to get the active session
- WTSQueryUserToken to get the related token
- CreateProcessAsUser to start the process
This works fine if I build a jar and launch the jar with java -jar my.jar from the local-system account.
However, I run the code from an Install4j Installer, which is, more or less, a JVM wrapped in an exe. The same code fails now, and CreateProcessAsUser results in error code 5 (no access)
- I compared the tokens, and could not find any difference. Same user, owner, groups, and privileges.
- I tried using process monitor to find anything special: nothing
- I tried to use CreateProcessWithTokenW instead of CreateProcessAsUser. The process launches but exits immediately(Maybe a Windowstation/Desktop issue)
Expected result: The "child" process starts and shows the UI on the currently active desktop.
Actual result: Errorcode 5 on CreateProcessAsUser
final PROCESS_INFORMATION processInformation = new PROCESS_INFORMATION();
final int activeConsoleSessionId = Kernel32Ext.INSTANCE.WTSGetActiveConsoleSessionId();
final PointerByReference userPrimaryProcessToken = new PointerByReference();
if (!Wtsapi32Ext.INSTANCE.WTSQueryUserToken(activeConsoleSessionId, userPrimaryProcessToken)) {
throw new Win32Exception(Kernel32.INSTANCE.GetLastError());
}
final STARTUPINFO startupInfo = new STARTUPINFO();
startupInfo.clear();
startupInfo.lpDesktop = "winsta0\\default";
startupInfo.wShowWindow = new WORD(1);
startupInfo.cb = new DWORD(processInformation.size());
final String cmdString = "C:\\Windows\\System32\\notepad.exe";
if (!AdvapiExt.INSTANCE.CreateProcessAsUser(new HANDLE(userPrimaryProcessToken.getValue()), null, cmdString, null, null, true, Kernel32.CREATE_UNICODE_ENVIRONMENT, null, "c:\\", startupInfo, processInformation)) {
final int error = Kernel32.INSTANCE.GetLastError();
throw new Win32Exception(error);
}