locked
WaitForIdleInput fails with WAIT_FAILED and GetLastError returns 0 RRS feed

  • Question

  • The following code fails when the process which attempts the WaitforIdleInput Api is launched from a program launched by a windows service. The same code succeeds if the windows service launches the process as the currently logged in user.

    The sequence of operations is

    Windows Service [Launches]->Program A-[Launches]->ProcessMonitor [which contains the above code]

    BOOL WaitForProcess(DWORD targetProcessId) { HANDLE hProcess = OpenProcess(MAXIMUM_ALLOWED, FALSE, targetProcessId); if (hProcess == NULL) { cout << "Failed to open process:" << endl; cout << "GetLastError returned " << GetLastError() << endl; LogC(L"Failed to open process",GetLastError()); return FALSE; } DWORD result = WaitForInputIdle(hProcess, INFINITE); if (result == WAIT_TIMEOUT) { CloseHandle(hProcess); cout << "Timed out waiting" << endl; LogC(L"Timed out waiting"); return FALSE; } else if (result == WAIT_FAILED) { DWORD err = GetLastError(); cout << "Wait Failed" << endl; CloseHandle(hProcess); cout << "GetLastError returned " << err<< endl; LogC(L"Wait Failed", err); if ((nWaitCount--)>0) { LogC(L"Sleeping ", nWaitCount + 1); Sleep(SLEEP_WAIT); return WaitForProcess(targetProcessId); } return FALSE; } CloseHandle(hProcess); LogC(L"Success"); return TRUE; }

    GetLastError() returns 0, and waiting for 25 seconds too has no effect. 

    Please help.

    Wednesday, August 21, 2019 6:01 AM

Answers

  • One way to accomplish running ProcessMonitor-x64 in the user's session -

    1) Windows service gets the session id of the logged-on interactive user.

    2) The service duplicates its own token (LocalSystem Account) and uses SetTokenInformation to set the duplicate's session id to the interactive session.

    3) Service uses CreateProcessAsUser with the duplicate token to start ProcessMonitor-x64 in the interactive session running as LocalSystem.


    • Edited by RLWA32 Wednesday, August 21, 2019 4:10 PM
    • Marked as answer by Sagar R. Kapadia Thursday, August 22, 2019 3:48 AM
    Wednesday, August 21, 2019 4:09 PM

All replies

  • Hello,

    according to this: https://groups.google.com/forum/#!topic/comp.os.ms-windows.programmer.win32/aOMbNR7TKrk

    "Is the process you're creating a GUI app or a command-line app?

    If it's a GUI app, you may need to re-think your approach as it will
    probably not work from Vista onwards where services run on an isolated
    desktop. The standard way to achieve this is to have a normal user
    process which starts when the user logs in. (It starts like any other
    user app, e.g. via the Start Menu Startup folder; it's not created by
    the service itself.) That user process then talks to the system-
    service via some IPC mechanism (take your pick). It might be
    completely hidden from the user until it needs to show some UI.

    If it's a command-line app then WaitForInputIdle won't work with it,
    even under normal circumstances."

    Regards, Guido

    Wednesday, August 21, 2019 8:20 AM
  • Guido Franzke's observation about console applications is clearly stated in the documentation for WaitForInputIdle function - "If this process is a console application or does not have a message queue, WaitForInputIdle returns immediately."

    And, WaitForInputIdle is not documented to set the error code; there is no instruction to call GetLastError for extended information upon failure.

    Wednesday, August 21, 2019 9:06 AM
  • Hi,

    Which user does your service run as, try to modify its log on account to current user in services.msc.

    It's not mentioned in the document that you can use GetLastError to get error codes, So GetLastError doesn't make sense.

    What process type are you wait for? I have tested a windows desktop application process(with a GUI thread), and it just return 0(which means The wait was satisfied successfully.)

    Best regards,

    Drake


    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    Wednesday, August 21, 2019 9:56 AM
  • It is a GUI application, not a console application. 
    Wednesday, August 21, 2019 10:53 AM
  • Are all the processes running in the same session?
    Wednesday, August 21, 2019 10:58 AM
  • Guido, I have a windows service which launches a process [using the CreateProcess API], This process monitors program startup and shut down. If if detects that a specific process has started ,[for example, notepad.exe], then it attempts to find its main window and inject a DLL into it. The first part works ok [I can detect start and stop of processes], but I cant find the window from the processId in the second step.

    If instead of the windows service starting the first process, I start the first process manually [as administrator], then the second step works properly and the window is found.

    Any suggestions to achieve this would help. 

    The following code shows how I try to find the window [which fails]

    BOOL FindTargetWindowAndInjectHook(DWORD targetProcessId, LPCWSTR  lpWindowName)
    {
    	LogC(L"Begin FindTargetWindowAndInjectHook");
    	if (!WaitForProcess(targetProcessId))
    	{
    		cout << "Failed to wait for Process" << endl;
    		LogC(L"Failed to wait for Process");
    		return FALSE;
    	}
    	int max = MAX_TRIES;
    	int ctr = 0;
    	HWND hWnd = NULL;
    	do {
    		LogC(L"Before FindMainWindow");
    		hWnd = FindMainWindow(targetProcessId);
    		LogC(L"After FindMainWindow",(LONG)hWnd);
    		if (hWnd)
    		{
    			cout << "Found Window:" << lpWindowName << endl;
    			LogC(L"Found Window", lpWindowName);
    			ZeroMemory(windowName, sizeof(windowName));
    			int length = GetWindowTextLengthA(hWnd);
    			GetWindowTextA(hWnd, windowName, MAX_LENGTH);
    			ZeroMemory(className, sizeof(className));
    			GetClassNameA(hWnd, className, MAX_LENGTH);
    			DWORD dwProcessId = 0;
    			GetWindowThreadProcessId(hWnd, &dwProcessId);
    
    
    			cout << "Found:" << className << ":" << windowName << ":" << dwProcessId << endl;
    			cout << "Target:" << lpWindowName << ":" << dwTargetProcessId << endl;
    
    			LogC(L"Found a potential match");
    
    
    			if (dwProcessId == dwTargetProcessId)  //ProcessIds match. We have found the process to be monitored
    			{
    
    				cout << "ProcessIds Match" << endl;
    				LogC(L"ProcessIds Match");
    				injectionParams.className = className;
    				injectionParams.windowName = windowName;
    				injectionParams.nIdleTime = nIdleTime;
    				cout << "Searching for Window with className " << injectionParams.className << " And WindowName " << injectionParams.windowName << endl;
    
    				DWORD dwThreadId = NULL;
    
    				if (injected) {
    					cout << "DLL Already Injected";
    					LogC(L"DLL Already Injected");
    					cout << "End EnumWindowsProc" << endl;
    					return TRUE;
    				}
    
    
    
    				//ZeroMemory(&mp, sizeof(mp));
    				monitorParams.hInst = hInst;
    				monitorParams.lpszWindowTitle = s2ws(windowName);
    
    				LogC(L"Title before CreateWindow", monitorParams.lpszWindowTitle);
    				hThreadWindow = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)CreateMonitorWindowThreadProc, NULL, 0, &lpThreadId);
    				LogC(L"hThread of Monitor Window Thread", (LONG)hThreadWindow);
    				cout << "hThread of Monitor Window Thread " << hThreadWindow << endl;
    				//Assign to element of handles arrary
    				//When this window is closed, this method should return, and the application must exit
    				//So wait on this thread
    				handles[0] =/* hEventMonitorWindowClosed;*/ hThreadWindow;
    
    
    				//Get the ThreadId of the Target Window
    				//We need this to wait upon, so that the process can exit when this thread exits, that is, when this window is closed
    				unsigned long targetWindowThreadId = GetTargetThreadIdFromWindow(className, windowName);
    
    				cout << "ThreadId:" << targetWindowThreadId << endl;
    				LogC(L"ThreadId of Target Window", (LONG)targetWindowThreadId);
    
    				if (targetWindowThreadId == NULL) {
    					long err = GetLastError();
    					cout << "Error:" << err << endl;
    					cout << "End EnumWindowsProc" << endl;
    					return -1;
    				}
    				HANDLE hThreadTarget = OpenThread(THREAD_ALL_ACCESS, 0, targetWindowThreadId);
    				cout << "hThread:" << hThreadTarget << endl;
    				LogC(L"hThread of Target Window", (LONG)hThreadTarget);
    				cout << "hThread of Target Window " << hThreadTarget << endl;
    				if (hThreadTarget == NULL) {
    					long err = GetLastError();
    					cout << "Error:" << err << endl;
    					LogC(L"Error", (LONG)err);
    				}
    
    
    				//Assign to element of handles arrary
    				//When this window is closed, this method should return, and the application must exit
    				//So wait on this thread
    				handles[1] = hThreadTarget;
    
    				/*We need to wait for either of two events
    				a. Target window is closed
    				b. The Monitor window is closed by a menu action
    				On either of these events, we need to exit the application. so wait on these two thread handles
    				When this method returns, the main method will exit
    				*/
    				WaitForMultipleObjectsEx(2, handles, FALSE, INFINITE, TRUE);
    				SendMessage(hWndMonitor, WM_COMMAND, MAKEWPARAM(ID_MENU_EXIT, 0), NULL);
    				//WaitForSingleObject(hEventMonitorWindowClosed, INFINITE);
    				cout << "End EnumWindowsProc" << endl;
    				LogC(L"End EnumWindowsProc");
    				return TRUE;
    
    			}
    
    		}
    		Sleep(SLEEP_INTERVAL);
    	} while (hWnd == NULL && (ctr++ < max));
    	LogC(L"End FindTargetWindowAndInjectHook");
    	return FALSE;
    }

    If I bypass the WaitForProcess method , [which in turn calls WaitForIdleInput], I still dont find the window

    Thanks,

    Wednesday, August 21, 2019 11:02 AM
  • Drake,

    It runs as Local System Account [it is launched by windows service using CreateProcess API]. The process I am waiting for is a GUI app, not a console app.

    Wednesday, August 21, 2019 11:03 AM
  • Guido, I have a windows service which launches a process [using the CreateProcess API], This process monitors program startup and shut down. If if detects that a specific process has started ,[for example, notepad.exe], then it attempts to find its main window and inject a DLL into it. The first part works ok [I can detect start and stop of processes], but I cant find the window from the processId in the second step.


    This is a losing proposition. A process started by a Windows service using CreateProcess runs in session 0.  From what you describe you are attempting to use an HWND for a process that is running in a different session using a different window station and desktop.

    Edit:

    Simplifying, if a windows service starts a gui process in a user's interactive session with CreateProcessAsUser and then calls WaitForInputIdle with the returned process handle the call will fail.

    • Edited by RLWA32 Wednesday, August 21, 2019 12:28 PM added observation
    Wednesday, August 21, 2019 12:20 PM
  • RLWA32,

    If I call CreateProcessAsUser, and the user is an administrator, then the whole thing works. It does not work if I use an api like CreateProcess [because of the different window station and desktop?]

    I would always use CreateProcessAsUser, but I need administrator rights to monitor process startup and shutdown

    So, is there any way to get the HWND of the running process, especially since I can detect that it started or stopped , and get its process Id

    I cant use CreateProcessAsUser, because the user may not be an administrator, and in that case, I cant monitor start and stop of processes.

    Thanks,

    Sagar


    • Edited by Sagar R. Kapadia Wednesday, August 21, 2019 1:09 PM Additional Information
    Wednesday, August 21, 2019 1:07 PM
  • You need to clearly explain the interactions and relationships between the windows service, started processes and the process used for monitoring.  Which accounts are involved and for which processes, and the sessions in which the various processes are running.

    Wednesday, August 21, 2019 1:27 PM
  • RLWA32, I tried to upload/drag a diagram, but thats not working for me. 

    So I will try to explain it in text

    I am trying to monitor idle time in a specific app [say notepad.exe] by injecting a DLL into that process. I launch another process if the idle time passes without any keystrokes or mouse movements.

    The interactions are as follows:


    Windows Service -CreateProcessAsUser->ProcessMonitor-X64->Monitors Start and stop of notepad.exe [using ETW events, which requires that ProcessMonitor-X64 is adminstrator ]

    When notepad.exe starts, ProcessMonitor-X64  instantiates ProcessMonitorX64 [A C++ COM app], and passes the processId and title of window of notepad.exe, and a wait interval to it. 

    ProcessMonitorX64->WaitForIdleInput->FindWindow using processid and window title->if found a matching process->Inject a DLL into notepad.exe

    The dll monitors keystrokes and mouse movements and launches a process after waiting for an idle time

    The above works if I use CreateProcessAsUser from the windows service, and the user is an administrator [The WaitForIdleInput and find window methods work]

    .If I use CreateProcess, then WaitForIdleInput and Find window methods do not work.

    Components

    Windows Service: Launches ProcessMonitor-X64 

    ProcesMonitor-X64 : C# console app. Monitors start and stop of specific processes, and instantiates a COM object ProcessMonitorX64 on start of specified process

    ProcessMonitorX64: C++  Console COM App which uses WaitForIdleInput and Finds window of specific process. Once window is found, injects a dll [Hook.dll] into it

    Hook.dll: The hook dll which is injected into the target process to monitor keystrokes and mouse movements

    Notepad.exe: The monitored process [GUI process for which I want WaitForIdleInput]


    User Accounts:

    a. Windows Service: Local System Account

    b. ProcessMonitor-X64 :  Account of Logged in User [Needs to be administrator for ETW events to work], and the whole system to work.

    I want to use the Local System Account instead of Logged In Users account because ETW events need to be monitored by a process with administrator rights , and the logged on user may not be an administrator.

    Observations:

    If I use CreateProcess instead of CreateProcessAsUser, the console of ProcessMonitor-X64 , launched by windows service, is invisible

    If I use CreateProcessAsUser, the console of ProcessMonitor-X64, launched by windows service, is visible

    Please let me know if I need to add anything more.

    Thanks, I really appreciate your help.

    Sagar

    Wednesday, August 21, 2019 1:58 PM
  • Based on your description this is my understanding -

    Windows service is running as LocalSystem in session 0.

    Windows service starts a ProcessMonitor-X64 process under the account of the logged-on user and it is running in the user's interactive session. Notepad.exe is running under the account of the logged-on user in the user's interactive session.

    ProcessMonitor-x64 instantiates an out-of-process COM Server -- ProcessMonitorX64. ProcessMonitorX64 is also running as the logged-on user and in the user's interactive session.

    You should be able to confirm this by using process explorer.

    If the windows service starts ProcessMonitor-X64 using CreateProcess then it runs under the LocalSystem account in session 0.  In this case, under what account is the COM Server running and in what session?  Also LocalSystem and session 0?  Process explorer can provide answers to these questions.


    Is it required that ProcessMonitor-X64 is visible on the user's interactive desktop?
    Wednesday, August 21, 2019 2:26 PM
  • No, ProcessMonitor-X64 and ProcessMonitorX64 both should not be visible.

    COM Server runs as follows

    Running Process Explorer as admin gives the following results when the process is created using CreateProcess :Account: NT AUTHORITY\SYSTEM and session 0.

    CreateProcessAsUser: Account: THRINT\Sagar kapadia and Session 4




    • Edited by Sagar R. Kapadia Wednesday, August 21, 2019 3:35 PM Additional Information
    Wednesday, August 21, 2019 3:33 PM
  • One way to accomplish running ProcessMonitor-x64 in the user's session -

    1) Windows service gets the session id of the logged-on interactive user.

    2) The service duplicates its own token (LocalSystem Account) and uses SetTokenInformation to set the duplicate's session id to the interactive session.

    3) Service uses CreateProcessAsUser with the duplicate token to start ProcessMonitor-x64 in the interactive session running as LocalSystem.


    • Edited by RLWA32 Wednesday, August 21, 2019 4:10 PM
    • Marked as answer by Sagar R. Kapadia Thursday, August 22, 2019 3:48 AM
    Wednesday, August 21, 2019 4:09 PM
  • Does this imply that even if the interactive user is not an administrator, the new process will have administrator privileges? 

    I was told this is impossible, that is, there is no way a process with credentials of a standard user can have administrator privileges. 

    https://social.msdn.microsoft.com/Forums/vstudio/en-US/a6744c34-ec80-46bc-adb7-2467d0b737e8/while-trying-to-launch-a-process-with-credentials-of-the-interactive-user-gettokeninformation?forum=vcgeneral

    If it is possible, I will give it another try.

    Thanks,

    Sagar


    Wednesday, August 21, 2019 5:55 PM
  • Does this imply that even if the interactive user is not an administrator, the new process will have administrator privileges?

    Again, the process will be started in the interactive user's session under the LocalSystem account.  It will not be running as a standard user.
    Wednesday, August 21, 2019 6:16 PM
  • I tried the following

    STDMETHODIMP CProcessManager::LaunchProcessElevated(BSTR commandLine, DWORD* dwProcessId)
    {
    	//LaunchDebugger();
    	logger->ClearLog();
    	ZeroMemory(buffer, sizeof(buffer));
    
    
    	CComBSTR temp(commandLine);
    	LPWSTR lpszProcessName = temp.operator LPWSTR();
    	Report(L"Process Name: %s", (LPCWSTR)lpszProcessName);
    
    	DWORD dwSessionId = WTSGetActiveConsoleSessionId();
    	
    	DWORD dwProcessIdWinLogon = GetProcessIdByNameAndSessionId(L"winlogon.exe", dwSessionId);
    	Report(L"Winlogon ProcessId: %d", dwProcessIdWinLogon);
    
    	
    	HANDLE hTokenSrc = GetTokenForProcessId(dwProcessIdWinLogon);
    	Report(L"Winlogn hTokenSrc: %d", hTokenSrc);
    	
    
    		if (!SetTokenInformation(hTokenSrc, TokenSessionId, &dwSessionId, sizeof(dwSessionId)))
    		{
    			Report(L"Failed to SetTokenInformation:Error: %d", GetLastError());
    			return S_FALSE;
    
    		}
    		else {
    			Report(L"SetTokenInformation succeeded");
    		}
    
    		//Report(lpszProcessName);
    		DWORD dwProcessIdNew = 0;
    		if (CreateProcessWithToken(hTokenSrc, lpszProcessName, (DWORD*)& dwProcessIdNew))
    		{
    			*dwProcessId = dwProcessIdNew;
    			return S_OK;
    		}
    		else
    		{
    			Report(L"Failed to CreateProcessWithToken: Error: %d", GetLastError());
    			return S_FALSE;
    		}
    
    
    	
    }


    SetTokenInformation does not result in an error, and the session id is that of the interactive user.

    However, the user is NT Authority\System, not the interactive user.

    However, I still have to check if I can find the window handle or whether the API WaitForIdelInput succeeds in case the session id is that of the interactive user.

    if I use the following sequence of operations

    STDMETHODIMP CProcessManager::LaunchProcessElevated(BSTR commandLine, DWORD* dwProcessId)
    {
    	//LaunchDebugger();
    	logger->ClearLog();
    	ZeroMemory(buffer, sizeof(buffer));
    
    
    	CComBSTR temp(commandLine);
    	LPWSTR lpszProcessName = temp.operator LPWSTR();
    	Report(L"Process Name: %s", (LPCWSTR)lpszProcessName);
    
    	DWORD dwSessionId = WTSGetActiveConsoleSessionId();
    	DWORD dwProcessIdExplorer = GetProcessIdByNameAndSessionId(L"explorer.exe", dwSessionId);
    	Report(L"Explorer ProcessId: %d", dwProcessIdExplorer);
    	DWORD dwProcessIdWinLogon = GetProcessIdByNameAndSessionId(L"winlogon.exe", dwSessionId);
    	Report(L"Winlogon ProcessId: %d", dwProcessIdWinLogon);
    
    	HANDLE hTokenDst = GetTokenForProcessId(dwProcessIdExplorer);
    	Report(L"Explorer hTokenDst: %d", hTokenDst);
    	HANDLE hTokenSrc = GetTokenForProcessId(dwProcessIdWinLogon);
    	Report(L"Winlogn hTokenSrc: %d", hTokenSrc);
    	if (CopyPrivileges(hTokenSrc, hTokenDst)) 
    	{
    
    
    
    		if (!SetTokenInformation(hTokenSrc, TokenSessionId, &dwSessionId, sizeof(dwSessionId)))
    		{
    			Report(L"Failed to SetTokenInformation:Error: %d", GetLastError());
    			return S_FALSE;
    
    		}
    		else {
    			Report(L"SetTokenInformation succeeded");
    		}
    
    		//Report(lpszProcessName);
    		DWORD dwProcessIdNew = 0;
    		if (CreateProcessWithToken(hTokenDst, lpszProcessName, (DWORD*)& dwProcessIdNew))
    		{
    			*dwProcessId = dwProcessIdNew;
    			return S_OK;
    		}
    		else
    		{
    			Report(L"Failed to CreateProcessWithToken: Error: %d", GetLastError());
    			return S_FALSE;
    		}
    
    
    	}
    	else
    	{
    		Report(L"Failed to Copy Privileges:Error: %d", GetLastError());
    		return S_FALSE;
    	}
    }

    the process has the interactive users credentials.

    But this sequence fails if the user is not an administrator, because the ProcessId of explorer.exe is not found by the method GetProcessIdByNameAndSessionId.

    DWORD CProcessManager::GetProcessIdByNameAndSessionId(LPCWSTR lpszProcessName, DWORD sessionId)
    {
    	Report(L"GetProcessIdByNameAndSessionId:%s:%d", lpszProcessName, sessionId);
    	//LaunchDebugger();
    	wchar_t* p = 0;
    	wchar_t* q = 0;
    	wchar_t* ProcessNameW = (wchar_t*)lpszProcessName;
    	DWORD processId = 0;
    	wchar_t lowerCaseInputProcess[512];
    	ZeroMemory(lowerCaseInputProcess, sizeof(lowerCaseInputProcess));
    	//Convert InputProcess to lower case
    	p = ProcessNameW;
    	q = lowerCaseInputProcess;
    
    	for (; *p; ++p, ++q) * q = tolower(*p);
    
    	//wprintf(L"Target:%s\n", lowerCaseInputProcess);
    
    
    	NTSTATUS status;
    	PVOID buffer;
    	PSYSTEM_PROCESS_INFO spi;
    
    	buffer = VirtualAlloc(NULL, 1024 * 1024, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); // We need to allocate a large buffer because the process list can be large.
    
    	if (!buffer)
    	{
    		//	Report(L"\nError: Unable to allocate memory for process list: %d\n", GetLastError());
    		return -1;
    	}
    
    	//Report(L"\nProcess list allocated at address %#x\n", buffer);
    	spi = (PSYSTEM_PROCESS_INFO)buffer;
    
    	if (!NT_SUCCESS(status = NtQuerySystemInformation(SystemProcessInformation, spi, 1024 * 1024, NULL)))
    	{
    		//Report(L"\nError: Unable to query process list (%#x)\n", status);
    
    		VirtualFree(buffer, 0, MEM_RELEASE);
    		return -1;
    	}
    
    
    
    
    	wchar_t imageName[512];
    	wchar_t lowerCaseImageName[512];
    
    	
    	Report(L"Proceeding to search for process %s", lpszProcessName);
    	while (spi->NextEntryOffset)
    	{
    		// Loop over the list until we reach the last entry.
    		//The following wsprintf is necessary because any access , include wcslen , other than wprintf causes an exception
    		//So copied buffer to another buffer for further processing.
    		//Report(lowerCaseImageName, L"%s", spi->ImageName.Buffer);
    		cout << spi->ImageName.Buffer << endl;
    		p = spi->ImageName.Buffer;
    		q = lowerCaseImageName;
    		ZeroMemory(lowerCaseImageName, sizeof(lowerCaseImageName));
    		if (p) {
    			for (; *p; ++p, ++q) * q = tolower(*p);
    		}
    		//Report(L"Lower case Image Name:%s", lowerCaseImageName);
    		//Compare Names
    		if ((StrCmpCW(lowerCaseInputProcess, lowerCaseImageName)) == 0) {
    
    			processId = (DWORD)spi->ProcessId;
    			Report(L"ProcessId names match:%d:", processId);
    
    			DWORD dwSessionId = 0;
    
    			if (ProcessIdToSessionId(processId, &dwSessionId))
    			{
    				Report(L"sessionId:%d", sessionId);
    				Report(L"ProcessIdToSessionId returned:%d", dwSessionId);
    
    
    				if (sessionId == dwSessionId) {
    
    					Report(L"SessionIds match");
    					//Report(L"Found a Match:%s\d", processId);
    					VirtualFree(buffer, 0, MEM_RELEASE); // Free the allocated buff
    					return processId;
    
    				}
    				else
    				{
    					Report(L"SessionIds do not match");
    				}
    			}
    			else
    			{
    				Report(L"ProcessIdToSessionId failed with error %d", GetLastError());
    			}
    
    
    		}
    		spi = (PSYSTEM_PROCESS_INFO)((LPBYTE)spi + spi->NextEntryOffset); // Calculate the address of the next entry.
    	}
    	processId = 0;
    	VirtualFree(buffer, 0, MEM_RELEASE); // Free the allocated buffer.
    
    	return processId;
    }


    Thursday, August 22, 2019 3:34 AM
  • Is there any way to give the interactive user administrator privileges, by using logic similar to that above? This is a separate question, but related to the above. your previous answer resolved my issue. By having the process in the interactive users session, I was able to do WaitForIdleInput and Find window.

    Thanks a lot, RLWA32

    Thursday, August 22, 2019 3:50 AM
  • your previous answer resolved my issue. By having the process in the interactive users session, I was able to do WaitForIdleInput and Find window.

    Thanks a lot, RLWA32

    Thursday, August 22, 2019 3:50 AM
  • Is there any way to give the interactive user administrator privileges, by using logic similar to that above?

    No, there is no magic method to grant a standard user account administrator privileges.

    Thursday, August 22, 2019 9:14 AM
  • Thanks for the clarification, RLWA32
    Thursday, August 22, 2019 12:01 PM