Below is the code I am using to run a GUI app from service application. I am passing cmd string "C:\Windows\notepad.exe".
It is not opening the Notepad and not even giving any error. hToken is null even after using WTSQueryUserToken. Here is a documentation link for create process as user : https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessasusera
private void cpasUser(String cmd) {
HANDLE h = null;
final HANDLEByReference childStdInRead = new HANDLEByReference();
final HANDLEByReference childStdInWrite = new HANDLEByReference();
final HANDLEByReference childStdOutRead = new HANDLEByReference();
final HANDLEByReference childStdOutWrite = new HANDLEByReference();
final int HANDLE_FLAG_INHERIT = 0x00000001;
final int HANDLE_FLAG_PROTECT_FROM_CLOSE = 0x00000002;
final int BUFSIZE = 4096;
final int GENERIC_READ = 0x80000000;
final int FILE_ATTRIBUTE_READONLY = 1;
final int OPEN_EXISTING = 3;
final DWORD STD_OUTPUT_HANDLE = new DWORD(-11);
final int STARTF_USESTDHANDLES = 0x00000100;
String szCmdline = cmd;
PROCESS_INFORMATION processInformation = new PROCESS_INFORMATION();
STARTUPINFO startupInfo = new STARTUPINFO();
startupInfo.cb = new DWORD(processInformation.size());
startupInfo.hStdError = childStdOutWrite.getValue();
startupInfo.hStdOutput = childStdOutWrite.getValue();
startupInfo.hStdInput = childStdInRead.getValue();
startupInfo.dwFlags |= STARTF_USESTDHANDLES;
// Create the child process.
HANDLE hToken = null;
MyWtsapi32 mw = MyWtsapi32.INSTANCE;
mw.WTSQueryUserToken(Kernel32Ext.INSTANCE.WTSGetActiveConsoleSessionId(), hToken) ;
//be sure that the handle is correct ! (can be the issue)
if (hToken == null) logger.info("Token error.");
if (!Advapi32.INSTANCE.CreateProcessAsUser(
hToken,
szCmdline,
null,
null,
null,
true,
32,
null,
null,
startupInfo,
processInformation)){
// System.err.println(Advapi32.INSTANCE.GetLastError());
logger.error("Cannot create process as User ");
logger.error("error code "+Native.getLastError());
}
MyWtsApi32.java
public interface MyWtsapi32 extends Wtsapi32 {
// Your own instance to access your functions
MyWtsapi32 INSTANCE = Native.load("Wtsapi32", MyWtsapi32.class, W32APIOptions.DEFAULT_OPTIONS);
// From https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-messagebox
int MB_OK = 0;
// The function to send the message
boolean WTSSendMessageW(HANDLE hServer, int SessionId,
LPWSTR pTitle, int TitleLength,
LPWSTR pMessage, int MessageLength,
int Style, int Timeout, IntByReference pResponse, boolean bWait);
boolean WTSQueryUserToken(long SessionId,HANDLE hServer);
}
Kernel32Ext.java
public interface Kernel32Ext extends Kernel32{
Kernel32Ext INSTANCE = Native.load("Kernel32",Kernel32Ext.class,W32APIOptions.DEFAULT_OPTIONS);
int WTSGetActiveConsoleSessionId();
}
When your JNA function mappings don't work, the first debugging step should be to check your function mappings.
WTSQueryUserToken
is defined as:
BOOL WTSQueryUserToken(
ULONG SessionId,
PHANDLE phToken
);
The Windows type ULONG
is an unsigned 32-bit integer; it should be mapped as int
, not long
.
PHANDLE
is a pointer to a HANDLE
, not the handle itself. The correct JNA mapping is HANDLEByReference
.
So your interface function mapping should be:
boolean WTSQueryUserToken(int SessionId, HANDLEByReference hServer);
And your code to call it should be:
HANDLEByReference phToken = new HANDLEByReference();
MyWtsapi32 mw = MyWtsapi32.INSTANCE;
// you should probably check the return value here
// on failure throw LastErrorException
mw.WTSQueryUserToken(Kernel32Ext.INSTANCE.WTSGetActiveConsoleSessionId(), phToken);
// Extract the HANDLE for use in later code
HANDLE hToken = phToken.getValue();