[RESOLVED] How to refresh logon screensaver parameter changes?
I have a Windows service that may change the timeout of the logon screensaver in Windows (as described here.) To do that I change the following registry key to the timeout in seconds:
Code:
HKEY_USERS\.DEFAULT\Control Panel\Desktop\ScreenSaveTimeOut
The issue is that how do I make OS "read" or refresh the actual screensaver timeout after a change in the registry key above?
My practice shows that it is refreshed (for sure) only when I reboot the system, but in my case I need it to be applied without the reboot.
Re: How to refresh logon screensaver parameter changes?
I think you are doing it the wrong way. Instead use SystemParametersInfo with parameter SPI_SETSCREENSAVETIMEOUT (seconds).
Re: How to refresh logon screensaver parameter changes?
Quote:
Originally Posted by
zerver
I think you are doing it the wrong way. Instead use SystemParametersInfo with parameter SPI_SETSCREENSAVETIMEOUT (seconds).
If I call SystemParametersInfo from a service it returns error 1459, or ERROR_REQUIRES_INTERACTIVE_WINDOWSTATION.
Re: How to refresh logon screensaver parameter changes?
Re: How to refresh logon screensaver parameter changes?
Note that UpdatePerUserSystemParameters is an undocumented function and therefore could change between OS releases and is not supported by Microsoft.
SystemParametersInfo() gets/sets the value for the current user and the OP is trying to change the value for the .default user (ie the user when no user is logged on).
Re: How to refresh logon screensaver parameter changes?
Thanks, fellas.
@zerver: I tried to call UpdatePerUserSystemParameters defined as such in User32.dll module:
Code:
BOOL (WINAPI *pfnUpdatePerUserSystemParameters)(DWORD, BOOL);
//Load it and then
if(pfnUpdatePerUserSystemParameters)
{
BOOL bResult = pfnUpdatePerUserSystemParameters(NULL, 1);
}
but it kept crashing my process, and I can't seem to find which parameters I'm supposed to call it with.
@2kaud: Yes, SystemParametersInfo API unfortunately is useless for my case. If I was calling it from the user-mode process, I'd definitely go with it.
The only solution that I was able to come across is to kill LogonUI.exe, which doesn't seem like the best way to go. Nonetheless, I tried it, but so far it didn't yield any results. The screensaver timeout remains unchanged until I reboot the system.
Re: How to refresh logon screensaver parameter changes?
If you look at the sample I sent, it appears you should call UpdatePerUserSystemParameters(NULL), or possibly without any parameters at all.
You could also try to call system("path to windows folder\\System32\\RUNDLL32.EXE user32.dll, UpdatePerUserSystemParameters");
I tried the RUNDLL thing on my w7 x64 system and it does not crash.
Re: How to refresh logon screensaver parameter changes?
you need to change your service to be an interactive service (set the checkbox "Allow service to interact with desktop").
Doing so will however have consequences, so be sure to read up on what exactly this does.
Then use SystemParametersInfo().
Typically speaking, a noninteractive service is not allowed to make any direct changes to the user experience, which is why the screen saver delay change isn't doing anything for a non-interactive service until reboot.
Re: How to refresh logon screensaver parameter changes?
Quote:
Originally Posted by
OReubens
you need to change your service to be an interactive service (set the checkbox "Allow service to interact with desktop").
Doing so will however have consequences, so be sure to read up on what exactly this does.
Then use SystemParametersInfo().
Typically speaking, a noninteractive service is not allowed to make any direct changes to the user experience, which is why the screen saver delay change isn't doing anything for a non-interactive service until reboot.
I'm not convinced that using SystemParametersInfo(SPI_SETSCREENSAVETIMEOUT) even if the service is changed to be interactive will accomplish what is required as this will change the parameter for the current user and it is the parameters for .DEFAULT user that are required to be changed.
Re: How to refresh logon screensaver parameter changes?
Quote:
Originally Posted by
dc_2000
Yes, SystemParametersInfo API unfortunately is useless for my case. If I was calling it from the user-mode process, I'd definitely go with it.
The only solution that I was able to
come across is to kill LogonUI.exe, which doesn't seem like the best way to go.
So it seems that SystemParametersInfo called in user-mode is the very only solution you are to put up with. :)
What way you do it, thread injection, or specialized exe, or surrogate process running your dll, or whatever else, it's up to you.
Re: How to refresh logon screensaver parameter changes?
First off, thank you fellas for sticking with this issue!
@zerver: I'm not sure what to think about that UpdatePerUserSystemParameters() API. I tried calling it in a seemingly any possible combination and it failed in all. The only "promising" result was when I called it without any parameters, it returned control back to my function, although still did some "damage" to the stack (one of my variables had garbage in it afterwards and then the host function later crashed) but it also set GetLastError to ERROR_REQUIRES_INTERACTIVE_WINDOWSTATION, which tells me that it probably acts analogous to SystemParametersInfo().
As for calling it via RUNDLL32.EXE... I'm not sure I want to go this route. I'd be ashamed to show this code to any fellow-C/C++ developer... It's like coding in JScript or Visual Basic, ya know.
@OReubens: I tried setting my service as an interactive (in despite of ample warnings not to do it if it's intended to run on the OS later than Windows XP.) Nonetheless it ran fine, and SystemParametersInfo() succeeded, but ... it didn't do any changes to my actual parameters. The logon screensaver timeout was not changed. My guess is that my service was running in a totally different desktop (or session.)
@2kaud: Good point! .DEFAULT is not the user account that any service will run in. So I did a small test. I put a logging function into my logon screensaver to see what user account and SID it was running under and I got this:
Code:
user=LOCAL SERVICE
domain=MyComputersDomain
sid=S-1-5-19
If you check the SID, it turns out that the logon screensaver is running under "LOCAL_SERVICE" account.
@Igor Vartanov: I thought your suggestion was my last-ditch attempt to make it work. Before doing any dll-injection (apart from not even knowing, what and where to inject), having the user account above I decided to simply run my test user process from my service under the LOCAL_SERVICE credentials and try to see if that may fix the issue.
So I came up with the following code to call my test user process from my service:
(Just a pseudo-code to eliminate the full-size clunker that it is)
Code:
//INFO: Error handling is omitted
HANDLE hToken;
LogonUser(L"LOCAL SERVICE", L"NT AUTHORITY", NULL, LOGON32_LOGON_SERVICE, LOGON32_PROVIDER_DEFAULT, &hToken);
HANDLE hToken2;
DuplicateTokenEx(hToken, MAXIMUM_ALLOWED, NULL, SecurityIdentification, TokenPrimary, &hToken2);
LPVOID pEnvBlock;
CreateEnvironmentBlock(&pEnvBlock, hToken2, FALSE);
STARTUPINFO si = {0};
si.cb = sizeof(STARTUPINFO);
si.lpDesktop = _T("winsta0\\default");
PROCESS_INFORMATION pi = {0};
ImpersonateLoggedOnUser(hToken2);
bResult = CreateProcessAsUser(
hToken2, // client's access token
pStrExeFilePath, // file to execute
pBuffCmdLine[0] != 0 ? pBuffCmdLine : NULL, // command line
NULL, // pointer to process SECURITY_ATTRIBUTES
NULL, // pointer to thread SECURITY_ATTRIBUTES
FALSE, // handles are not inheritable
NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE | CREATE_UNICODE_ENVIRONMENT, // creation flags
pEnvBlock, // pointer to new environment block
NULL, // name of current directory
&si, // pointer to STARTUPINFO structure
&pi // receives information about new process
);
RevertToSelf();
DWORD dwResWait = ::WaitForSingleObject(pi.hProcess, 5 * 1000);
if(dwResWait == WAIT_OBJECT_0)
{
//Get return code with results from my test exe
DWORD dwExitCode;
GetExitCodeProcess(pi.hProcess, &dwExitCode);
//Analyze the 'dwExitCode'
}
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
DestroyEnvironmentBlock(pEnvBlock);
CloseHandle(hToken2);
CloseHandle(hToken);
And the test exe is simply a console application, statically linked to the CRT (/MT) to do this:
Code:
int APIENTRY _tWinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPTSTR lpCmdLine,
int nCmdShow)
{
int nExitCode = 0;
if(SystemParametersInfo(SPI_SETSCREENSAVETIMEOUT, 15, 0, SPIF_UPDATEINIFILE | SPIF_SENDWININICHANGE))
{
//Success
nExitCode = 1000;
}
else
{
//Error
nExitCode = 0xdead;
}
return nExitCode;
}
So when the above construct runs, I get to the point when CreateProcessAsUser() succeeds, and returns the process handle but then GetExitCodeProcess() returns exit code `0xC0000142`, which is STATUS_DLL_INIT_FAILED, that means that my small test exe failed to initialize.
I ran it through Dependency Walker and all I have it linked to is: kernel32.dll and user32.dll.
From what I understand kernel32 is present all the time, and user32 is needed to actually run SystemParametersInfo.
So at this point I'm almost ready to throw in the towel. Unless you have any other ideas how to make it work???
Re: How to refresh logon screensaver parameter changes?
Hi dc2000,
I think if you read this you will find the soluition:
http://blogs.msdn.com/b/winsdk/archi...and-later.aspx
Re: How to refresh logon screensaver parameter changes?
Quote:
Originally Posted by
zerver
Thanks. It's a nice article. But unfortunately like one of the commenters noted there:
Quote:
This is a very good article.
But only theorie.
I'll play around with CreateProcessWithLogonW, CreateProcessWithTokenW, and ImpersonateLoggedOnUser to see if maybe they can fix the error I get from my small test exe.
Re: How to refresh logon screensaver parameter changes?
A short update. I can't seem to find a way how to run my test process in the following context from the LocalSystem service. Any idea how to do it, guys?
Code:
LOCAL SERVICE
sid=S-1-5-19
Re: How to refresh logon screensaver parameter changes?