mirror of
https://github.com/reactos/reactos.git
synced 2024-09-04 09:39:07 +00:00
![Hermès Bélusca-Maïto](/assets/img/avatar_default.png)
as documented in "Advanced Windows NT" by Jeffrey M. Richter (Microsoft Press), and in https://is.muni.cz/el/1433/jaro2010/PB167/um/cv5/undocumented_CreateProcess.pdf . [INCLUDE][SERVICES][WIN32K:NTUSER] Add an undocumented STARTF_INHERITDESKTOP flag for the STARTUPINFO::dwFlags structure member, whose purpose is to tell Win32k that the created handles to the window station and desktop to which the process is connecting to, can be inherited by its child processes. It is used when starting interactive services. Observed via API monitoring on Windows 2003.
389 lines
11 KiB
C
389 lines
11 KiB
C
/*
|
|
* COPYRIGHT: See COPYING in the top level directory
|
|
* PROJECT: ReactOS Winlogon
|
|
* FILE: base/system/winlogon/screensaver.c
|
|
* PURPOSE: Screen saver management
|
|
* PROGRAMMERS: Hervé Poussineau (hpoussin@reactos.org)
|
|
*/
|
|
|
|
/* INCLUDES *****************************************************************/
|
|
|
|
#include "winlogon.h"
|
|
|
|
/* FUNCTIONS ****************************************************************/
|
|
|
|
#ifndef USE_GETLASTINPUTINFO
|
|
static
|
|
LRESULT
|
|
CALLBACK
|
|
KeyboardActivityProc(
|
|
IN INT nCode,
|
|
IN WPARAM wParam,
|
|
IN LPARAM lParam)
|
|
{
|
|
InterlockedExchange((LONG*)&WLSession->LastActivity, ((PKBDLLHOOKSTRUCT)lParam)->time);
|
|
return CallNextHookEx(NULL, nCode, wParam, lParam);
|
|
}
|
|
|
|
|
|
static
|
|
LRESULT
|
|
CALLBACK
|
|
MouseActivityProc(
|
|
IN INT nCode,
|
|
IN WPARAM wParam,
|
|
IN LPARAM lParam)
|
|
{
|
|
InterlockedExchange((LONG*)&WLSession->LastActivity, ((PMSLLHOOKSTRUCT)lParam)->time);
|
|
return CallNextHookEx(NULL, nCode, wParam, lParam);
|
|
}
|
|
#endif
|
|
|
|
|
|
static
|
|
VOID
|
|
LoadScreenSaverParameters(
|
|
OUT LPDWORD Timeout)
|
|
{
|
|
BOOL Enabled;
|
|
|
|
if (!SystemParametersInfoW(SPI_GETSCREENSAVETIMEOUT, 0, Timeout, 0))
|
|
{
|
|
WARN("WL: Unable to get screen saver timeout (error %lu). Disabling it\n", GetLastError());
|
|
*Timeout = INFINITE;
|
|
}
|
|
else if (!SystemParametersInfoW(SPI_GETSCREENSAVEACTIVE, 0, &Enabled, 0))
|
|
{
|
|
WARN("WL: Unable to check if screen saver is enabled (error %lu). Disabling it\n", GetLastError());
|
|
*Timeout = INFINITE;
|
|
}
|
|
else if (!Enabled)
|
|
{
|
|
TRACE("WL: Screen saver is disabled\n");
|
|
*Timeout = INFINITE;
|
|
}
|
|
else
|
|
{
|
|
TRACE("WL: Screen saver timeout: %lu seconds\n", *Timeout);
|
|
*Timeout *= 1000;
|
|
}
|
|
}
|
|
|
|
|
|
static
|
|
DWORD
|
|
WINAPI
|
|
ScreenSaverThreadMain(
|
|
IN LPVOID lpParameter)
|
|
{
|
|
PWLSESSION Session = (PWLSESSION)lpParameter;
|
|
HANDLE HandleArray[3];
|
|
#ifdef USE_GETLASTINPUTINFO
|
|
LASTINPUTINFO lastInputInfo;
|
|
#else
|
|
DWORD LastActivity;
|
|
#endif
|
|
DWORD TimeToWait;
|
|
DWORD Timeout; /* Timeout before screen saver starts, in milliseconds */
|
|
DWORD ret;
|
|
|
|
if (!ImpersonateLoggedOnUser(Session->UserToken))
|
|
{
|
|
ERR("ImpersonateLoggedOnUser() failed with error %lu\n", GetLastError());
|
|
return 0;
|
|
}
|
|
|
|
Session->hUserActivity = CreateEventW(NULL, FALSE, FALSE, NULL);
|
|
if (!Session->hUserActivity)
|
|
{
|
|
ERR("WL: Unable to create event (error %lu)\n", GetLastError());
|
|
goto cleanup;
|
|
}
|
|
|
|
Session->hEndOfScreenSaver = CreateEventW(NULL, FALSE, FALSE, NULL);
|
|
if (!Session->hEndOfScreenSaver)
|
|
{
|
|
ERR("WL: Unable to create event (error %lu)\n", GetLastError());
|
|
goto cleanup;
|
|
}
|
|
|
|
HandleArray[0] = Session->hEndOfScreenSaverThread;
|
|
HandleArray[1] = Session->hScreenSaverParametersChanged;
|
|
HandleArray[2] = Session->hEndOfScreenSaver;
|
|
|
|
LoadScreenSaverParameters(&Timeout);
|
|
|
|
#ifndef USE_GETLASTINPUTINFO
|
|
InterlockedExchange((LONG*)&Session->LastActivity, GetTickCount());
|
|
#else
|
|
lastInputInfo.cbSize = sizeof(LASTINPUTINFO);
|
|
#endif
|
|
for (;;)
|
|
{
|
|
/* See the time of last activity and calculate a timeout */
|
|
#ifndef USE_GETLASTINPUTINFO
|
|
LastActivity = InterlockedCompareExchange((LONG*)&Session->LastActivity, 0, 0);
|
|
TimeToWait = Timeout - (GetTickCount() - LastActivity);
|
|
#else
|
|
if (GetLastInputInfo(&lastInputInfo))
|
|
TimeToWait = Timeout - (GetTickCount() - lastInputInfo.dwTime);
|
|
else
|
|
{
|
|
WARN("GetLastInputInfo() failed with error %lu\n", GetLastError());
|
|
TimeToWait = 10; /* Try again in 10 ms */
|
|
}
|
|
#endif
|
|
|
|
if (TimeToWait > Timeout)
|
|
{
|
|
/* GetTickCount() got back to 0 */
|
|
TimeToWait = Timeout;
|
|
}
|
|
|
|
/* Wait for the timeout, or the end of this thread */
|
|
ret = WaitForMultipleObjects(2, HandleArray, FALSE, TimeToWait);
|
|
if (ret == WAIT_OBJECT_0)
|
|
break;
|
|
else if (ret == WAIT_OBJECT_0 + 1)
|
|
LoadScreenSaverParameters(&Timeout);
|
|
|
|
/* Check if we didn't had recent activity */
|
|
#ifndef USE_GETLASTINPUTINFO
|
|
LastActivity = InterlockedCompareExchange((LONG*)&Session->LastActivity, 0, 0);
|
|
if (LastActivity + Timeout > GetTickCount())
|
|
continue;
|
|
#else
|
|
if (!GetLastInputInfo(&lastInputInfo))
|
|
{
|
|
WARN("GetLastInputInfo() failed with error %lu\n", GetLastError());
|
|
continue;
|
|
}
|
|
|
|
if (lastInputInfo.dwTime + Timeout > GetTickCount())
|
|
continue;
|
|
#endif
|
|
|
|
/* Run screen saver */
|
|
PostMessageW(Session->SASWindow, WLX_WM_SAS, WLX_SAS_TYPE_SCRNSVR_TIMEOUT, 0);
|
|
|
|
/* Wait for the end of this thread or of the screen saver */
|
|
ret = WaitForMultipleObjects(3, HandleArray, FALSE, INFINITE);
|
|
if (ret == WAIT_OBJECT_0)
|
|
break;
|
|
else if (ret == WAIT_OBJECT_0 + 1)
|
|
LoadScreenSaverParameters(&Timeout);
|
|
else if (ret == WAIT_OBJECT_0 + 2)
|
|
SystemParametersInfoW(SPI_SETSCREENSAVERRUNNING, FALSE, NULL, 0);
|
|
}
|
|
|
|
cleanup:
|
|
if (Session->hUserActivity)
|
|
CloseHandle(Session->hUserActivity);
|
|
|
|
if (Session->hEndOfScreenSaver)
|
|
CloseHandle(Session->hEndOfScreenSaver);
|
|
|
|
RevertToSelf();
|
|
|
|
#ifndef USE_GETLASTINPUTINFO
|
|
if (Session->KeyboardHook)
|
|
UnhookWindowsHookEx(Session->KeyboardHook);
|
|
|
|
if (Session->MouseHook)
|
|
UnhookWindowsHookEx(Session->MouseHook);
|
|
#endif
|
|
|
|
CloseHandle(Session->hEndOfScreenSaverThread);
|
|
CloseHandle(Session->hScreenSaverParametersChanged);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
BOOL
|
|
InitializeScreenSaver(
|
|
IN OUT PWLSESSION Session)
|
|
{
|
|
HANDLE ScreenSaverThread;
|
|
|
|
#ifndef USE_GETLASTINPUTINFO
|
|
/* Register hooks to detect keyboard and mouse activity */
|
|
Session->KeyboardHook = SetWindowsHookEx(WH_KEYBOARD_LL, KeyboardActivityProc, hAppInstance, 0);
|
|
if (!Session->KeyboardHook)
|
|
{
|
|
ERR("WL: Unable to register keyboard hook\n");
|
|
return FALSE;
|
|
}
|
|
|
|
Session->MouseHook = SetWindowsHookEx(WH_MOUSE_LL, MouseActivityProc, hAppInstance, 0);
|
|
if (!Session->MouseHook)
|
|
{
|
|
ERR("WL: Unable to register mouse hook\n");
|
|
return FALSE;
|
|
}
|
|
#endif
|
|
|
|
Session->hScreenSaverParametersChanged = CreateEventW(NULL, FALSE, FALSE, NULL);
|
|
if (!Session->hScreenSaverParametersChanged)
|
|
{
|
|
WARN("WL: Unable to create screen saver event (error %lu)\n", GetLastError());
|
|
return TRUE;
|
|
}
|
|
|
|
Session->hEndOfScreenSaverThread = CreateEventW(NULL, FALSE, FALSE, NULL);
|
|
if (!Session->hEndOfScreenSaverThread)
|
|
{
|
|
WARN("WL: Unable to create screen saver event (error %lu)\n", GetLastError());
|
|
CloseHandle(Session->hScreenSaverParametersChanged);
|
|
return TRUE;
|
|
}
|
|
|
|
ScreenSaverThread = CreateThread(NULL,
|
|
0,
|
|
ScreenSaverThreadMain,
|
|
Session,
|
|
0,
|
|
NULL);
|
|
if (ScreenSaverThread)
|
|
CloseHandle(ScreenSaverThread);
|
|
else
|
|
ERR("WL: Unable to start screen saver thread\n");
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
VOID
|
|
StartScreenSaver(
|
|
IN PWLSESSION Session)
|
|
{
|
|
HKEY hKey = NULL, hCurrentUser = NULL;
|
|
WCHAR szApplicationName[MAX_PATH];
|
|
WCHAR szCommandLine[MAX_PATH + 3];
|
|
DWORD bufferSize = sizeof(szApplicationName) - sizeof(WCHAR);
|
|
DWORD dwType;
|
|
STARTUPINFOW StartupInfo;
|
|
PROCESS_INFORMATION ProcessInformation;
|
|
HANDLE HandleArray[2];
|
|
LONG rc;
|
|
DWORD Status;
|
|
BOOL ret = FALSE;
|
|
|
|
if (!ImpersonateLoggedOnUser(Session->UserToken))
|
|
{
|
|
ERR("WL: ImpersonateLoggedOnUser() failed with error %lu\n", GetLastError());
|
|
goto cleanup;
|
|
}
|
|
|
|
rc = RegOpenCurrentUser(KEY_READ,
|
|
&hCurrentUser);
|
|
if (rc != ERROR_SUCCESS)
|
|
{
|
|
ERR("WL: RegOpenCurrentUser error %lu\n", rc);
|
|
goto cleanup;
|
|
}
|
|
|
|
rc = RegOpenKeyExW(hCurrentUser,
|
|
L"Control Panel\\Desktop",
|
|
0,
|
|
KEY_QUERY_VALUE,
|
|
&hKey);
|
|
if (rc != ERROR_SUCCESS)
|
|
{
|
|
ERR("WL: RegOpenKeyEx error %lu\n", rc);
|
|
goto cleanup;
|
|
}
|
|
|
|
rc = RegQueryValueExW(hKey,
|
|
L"SCRNSAVE.EXE",
|
|
0,
|
|
&dwType,
|
|
(LPBYTE)szApplicationName,
|
|
&bufferSize);
|
|
if (rc != ERROR_SUCCESS || dwType != REG_SZ)
|
|
{
|
|
if (rc != ERROR_FILE_NOT_FOUND)
|
|
ERR("WL: RegQueryValueEx error %lu\n", rc);
|
|
goto cleanup;
|
|
}
|
|
|
|
if (bufferSize == 0)
|
|
{
|
|
ERR("WL: Buffer size is NULL!\n");
|
|
goto cleanup;
|
|
}
|
|
|
|
szApplicationName[bufferSize / sizeof(WCHAR)] = 0; /* Terminate the string */
|
|
|
|
if (wcslen(szApplicationName) == 0)
|
|
{
|
|
ERR("WL: Application Name length is zero!\n");
|
|
goto cleanup;
|
|
}
|
|
|
|
wsprintfW(szCommandLine, L"%s /s", szApplicationName);
|
|
TRACE("WL: Executing %S\n", szCommandLine);
|
|
|
|
ZeroMemory(&StartupInfo, sizeof(STARTUPINFOW));
|
|
ZeroMemory(&ProcessInformation, sizeof(PROCESS_INFORMATION));
|
|
StartupInfo.cb = sizeof(STARTUPINFOW);
|
|
StartupInfo.dwFlags = STARTF_SCREENSAVER;
|
|
|
|
/* FIXME: run the screen saver on the screen saver desktop */
|
|
ret = CreateProcessW(szApplicationName,
|
|
szCommandLine,
|
|
NULL,
|
|
NULL,
|
|
FALSE,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
&StartupInfo,
|
|
&ProcessInformation);
|
|
if (!ret)
|
|
{
|
|
ERR("WL: Unable to start %S, error %lu\n", szApplicationName, GetLastError());
|
|
goto cleanup;
|
|
}
|
|
|
|
CloseHandle(ProcessInformation.hThread);
|
|
|
|
SystemParametersInfoW(SPI_SETSCREENSAVERRUNNING, TRUE, NULL, 0);
|
|
|
|
CallNotificationDlls(Session, StartScreenSaverHandler);
|
|
|
|
/* Wait the end of the process or some other activity */
|
|
ResetEvent(Session->hUserActivity);
|
|
HandleArray[0] = ProcessInformation.hProcess;
|
|
HandleArray[1] = Session->hUserActivity;
|
|
Status = WaitForMultipleObjects(2, HandleArray, FALSE, INFINITE);
|
|
if (Status == WAIT_OBJECT_0 + 1)
|
|
{
|
|
/* Kill the screen saver */
|
|
TerminateProcess(ProcessInformation.hProcess, 0);
|
|
}
|
|
|
|
SetEvent(Session->hEndOfScreenSaver);
|
|
|
|
CloseHandle(ProcessInformation.hProcess);
|
|
|
|
CallNotificationDlls(Session, StopScreenSaverHandler);
|
|
|
|
cleanup:
|
|
if (hKey)
|
|
RegCloseKey(hKey);
|
|
|
|
if (hCurrentUser)
|
|
RegCloseKey(hCurrentUser);
|
|
|
|
RevertToSelf();
|
|
|
|
if (!ret)
|
|
{
|
|
PostMessageW(Session->SASWindow, WLX_WM_SAS, WLX_SAS_TYPE_SCRNSVR_ACTIVITY, 0);
|
|
#ifndef USE_GETLASTINPUTINFO
|
|
InterlockedExchange((LONG*)&Session->LastActivity, GetTickCount());
|
|
#endif
|
|
}
|
|
}
|