Get idle time from system services with windows API

There are several methods to get user idle time.

No 1. one of most widely used is GetLastInputInfo.
However, the API returns invalid values when called by system services, because system services are isolated to session 0 and no input triggers signaled.

No 2. second option is WTSQuerySessionInformation.
The 3rd parameter, WTSInfoClass can be set to WTSSessionInfo and WTSINFO structure could be obtained.
API itself can be used by system services, but LastInputTime would not be valid if the API’s called for local (Console) user.

No 3. third option is to build another user mode app with Inter Process Communication (IPC).
To achieve that, service process must launch a new user mode process and communicate each other.
To create user mode process, valid user token should be obtained by WTSQueryUserToken, and then call CreateProcessAsUser with acquired token.

#include <windows.h>
#include <wtsapi32.h>
HANDLE CreateProcessActiveConsole(LPCTSTR app, LPTSTR cmd)
{
static HANDLE hToken;
static STARTUPINFO si;
static PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(STARTUPINFO));
si.cb = sizeof(STARTUPINFO);
WTSQueryUserToken(WTSGetActiveConsoleSessionId(), &hToken);
CreateProcessAsUser(hToken, app, cmd, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
return pi.hProcess;
}

No 4. Querying WMI
System service process can query WMI and obtain Last Input data.
ReadOperationCount would be changed if any keyboard or mouse input occurred.
Query Win32_process, csrss.exe
There were two process handle in my computer. '516' was session 0 (service process) and '12516' was Console session.
SessionId and ReadOperationCount can be found.

#pragma comment (lib, "ole32")
#pragma comment (lib, "oleaut32")
#pragma comment (lib, "wbemuuid")
#include <stdio.h>
#include <windows.h>
#include <wbemidl.h>
void GetLastInputInfoFromWmi()
{
HRESULT hr = 0;
IWbemLocator *locator = NULL;
IWbemServices *services = NULL;
IEnumWbemClassObject *results = NULL;
BSTR resource = SysAllocString(L"ROOT\\CIMV2");
BSTR language = SysAllocString(L"WQL");
BSTR query = SysAllocString(L"SELECT * FROM Win32_process where name=\"csrss.exe\"");
hr = CoInitializeEx(0, COINIT_MULTITHREADED);
hr = CoInitializeSecurity(NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_DEFAULT, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE, NULL);
hr = CoCreateInstance(&CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER, &IID_IWbemLocator, (LPVOID *) &locator);
hr = locator->lpVtbl->ConnectServer(locator, resource, NULL, NULL, NULL, 0, NULL, NULL, &services);
hr = services->lpVtbl->ExecQuery(services, language, query, WBEM_FLAG_BIDIRECTIONAL, NULL, &results);
if (results != NULL)
{
IWbemClassObject *result = NULL;
ULONG returnedCount = 0;
while((hr = results->lpVtbl->Next(results, WBEM_INFINITE, 1, &result, &returnedCount)) == S_OK)
{
VARIANT roCnt, seId;
ULONGLONG lCnt = 0;
hr = result->lpVtbl->Get(result, L"SessionId", 0, &seId, 0, 0);
printf("Session ID %d\n", seId.uintVal);
hr = result->lpVtbl->Get(result, L"ReadOperationCount", 0, &roCnt, 0, 0);
VarUI8FromStr(roCnt.bstrVal, GetSystemDefaultLCID(), 0, &lCnt);
printf("Read Operation Count %llu\n", lCnt);
result->lpVtbl->Release(result);
}
}
results->lpVtbl->Release(results);
services->lpVtbl->Release(services);
locator->lpVtbl->Release(locator);
CoUninitialize();
SysFreeString(query);
SysFreeString(language);
SysFreeString(resource);
}
void main()
{
GetLastInputInfoFromWmi();
}