How do I get the user's setting for screensaver / machine lock time?
Is there a function in C#?
If not, what is the WIN API function for this?
The screen saver timeout is obtained by calling SystemParametersInfo with the action SPI_GETSCREENSAVETIMEOUT.
Registry key - HKEY_CURRENT_USER\Software\Policies\Microsoft\Windows\Control Panel\Desktop look at the value of ScreenSaveTimeOut
Keep in mind your application will have to run with elevated rights in order to access the registry.
Related
I need your help again :)
What I want to do:
I have a c# program which runs with normal user permissions - those can't be raised - and I want to change the system date (not time) from that program.
[EDIT] To be a bit more specific: I do have administrator credentials which could be embedded to the program (yikes, I know), but I want to avoid an UAC prompt.
When I launch cmd from outside Visual Studio and try to change the date, it fails with "missing client permission" (roughly translated from German). When I launch cmd as administrator, confirm the UAC prompt the same command succeeds.
I am writing from home, so I cant provide running code at the moment. But all my tries did compile and run without error but the date wasn't changed.
What I've tried:
Created a Process instance, applied admin credentials to ProcessStartInfo, set the FileName to c:\windows\system32\cmd.exe and the argument to /C date {thedate}. I redirected StandardOutput and StandardError and after execution StandardError contains the same message as stated above: "missing client permission"
I've modified this example MSDN: WindowsIdentity.Impersonate using PInvoke with AdvAPI32.LogonUser to raise permissions and Kernel32.SetSystemTime to change the system time. AdvAPI32.LogonUser succeeds and WindowsIdentity.GetCurrent().Name returns the admin name, but calling Kernel32.SetSystemTime fails with "missing client permission".
I've tried opening the current process AdvApi32.OpenCurrentProcess and adjusting the permissions using AdvApi32.AdjustTokenPrivileges following this example StackOverflow: Change system time programmaticaly using datetimepeaker and the code runs totally fine but Kernel32.SetSystemTime fails...
[EDIT] Solution:
I ended up writing a small program with an embedded app.manifest that requests administrator privilegs. This app is called from the main program so this can still be run by a normal user.
It is not possible to change the privileges of the process, once it's started. The original process has to start another process with elevated privileges. This second process can actually be the same .exe file, but with a command parameter that tells the process to do some small stuff and exit immediately.
To start a process with elevated privileges use Process.Start, but with .Verb = "runas", as described in another question. This will of course cause UAC prompt to pop up, if it's enabled on the machine. At least one UAC prompt has to be shown, because UAC prompt is the whole point if this defense mechanism.
If you want to reduce many UAC prompts to just one then you can set the original app to be started as administrator (UAC prompt shows when original process starts) and have just one process. Or have some interprocess communication between original process and elevated process, so that elevated process is started only once and made to finish when original process ends. In the second case UAC prompt shows the first time it's needed.
Can I ask why you are needing to change the system time from an unprivileged application?
If it is to influence other applications then you will need admin privilege since date time is such a vital system function to many other applications. However if you are just needing to adjust the date in your application only then I would suggest creating your own date/time provider that can return the date offset to the date that you desire in your application.
For example.
public DateTime GetDateWithOffset(int daysToOffset)
{
return DateTime.UtcNow.AddDays(daysToOffset);
}
As part of some work I need to get done for Windows 10, I have written a code in C# that essentially detects every minute whether a PC is in screen saver mode or not, and it writes to a table in MySQL the relevant status ("PC in use" if the screen saver is off, "available PC" if the screen saver is on).
I did this using (full link if required - https://www.codeproject.com/Articles/17067/Controlling-The-Screen-Saver-With-C):
// Returns TRUE if the screen saver is actually running
public static bool GetScreenSaverRunning( )
{
bool isRunning = false;
SystemParametersInfo( SPI_GETSCREENSAVERRUNNING, 0,
ref isRunning, 0 );
return isRunning;
}
The code works flawlessly in console application mode (I made a loop to test it out over a minute with a check up on screen save status every 10 seconds), this means in MySQL the status was set correctly every time, depending on the screen save status at the moment of the check up.
The problem occurs when I use this code for a windows service. The service is installed correctly, the log on tab is set on Local System (I also tried with the logged in user instead, same results) and I allow the service to interact with the desktop, just in case, but the difference here is that no matter if the PC enters screen save or not, it always returns false on GetScreenSaverRunning(), thus setting the status of the PC in MySQL as "PC in use", even if the screen saver is on at the moment of check up.
I get the sense that the problem isn't in the code itself, since it works without any issues as a console application, but perhaps something behind the scenes. I tried to search here and on many other websites, haven't found anything related to such a problem.
Does anyone have any idea at all what might be the issue? Any help and/or suggestions will be greatly appreciated!
Thank you in advance.
(I could post the code if required, but it is pretty much straight forward and the main part of it, controlling the screen save detection, is taken from the website mentioned above, afterwards it's a simple if (GetScreenSaverRunning() == true) )
Ever since Vista, Services are barred from a Interactive Session. Even if they run under the same rights, they do not get a interactive Session. I would guess that is getting in the way here.
While you can overwrite this behavior in the Service settings, this is not adviseable for new code. Consider making this a Background Task started by the Task Sheduler instead.
Because the windows service runs in different session then the windows logon. You can't interact with the desktop related services unless you run the windows service in win logon session. There used to be an option in Windows service manager where you can set the properties to "Interact with desktop session" but I don't think that ever worked.
There's a work around to run the windows service using the win logo session.
See this helper class that can get the current logged on user session and interact with the desktop services. https://github.com/murrayju/CreateProcessAsUser/blob/master/ProcessExtensions/ProcessExtensions.cs
I have a messy collection of Windows platforms in various labs. Everything from XP to the latest. I have a simple C#/.NET app that runs at user login and logout. I need to differentiate between true Console logins and RDP sessions. I am using
System.Diagnostics.Process.GetCurrentProcess().SessionId
to get a sessionId but I don't see anything that says "you are on the Console" or "you are in an RDP session". Is there such a thing?
For my needs it turns out that the windows environment variable %sessionname% holds the info I am looking for. In my C# code I can say:
string sessionName = Environment.GetEnvironmentVariable("SESSIONNAME")
?? "CONSOLE";
%sessionname% seems to hold either the string "console" or "rdp-(something)#(number)", ex "rdt-tcp#1234". I don't know the whole range of possible values but it looks like it will work for my needs.
You can determine which session ID (if any) is currently attached to the physical console by calling the WTSGetActiveConsoleSessionId() function.
How to change screen saver timeout and screensaverissecure using user32.dll for the current user (or) any other users in windows through C# code, as my application is running with SYSTEM privileges in the background process.
When we change the value of this registry using c# or scripting HKEY_CURRENT_USER\Control Panel\Desktop\ScreensaverTimeout
and HKEY_CURRENT_USER\Control Panel\Desktop\ScreenSaverIsSecure
this effect is showing only after reboot of machine.
when we change the registry value using c# code with SYSTEM privileges those changes are not effecting with new values.
Q1: ...this effect is showing only after reboot of machine:
After updating registry, you can call UpdatePerUserSystemParameters to force Windows to reread the configuration from the registry (so the update takes effect immediately).
rundll32.exe user32.dll, UpdatePerUserSystemParameters
Q2: ...with SYSTEM privileges those changes are not effecting with new values:
You need to launch a new process which is running in the same session as the current log on user, and update the registry values from the new process. This is good question to get started.
After you change windows settings like this you need to call the WinApi SendNotifyMessage with a WM_SETTINGCHANGE message to all windows. This is why your settings are not being updated until reboot. (I think you would find a logoff-on would achieve the same for that user).
WinApi
https://msdn.microsoft.com/en-us/library/windows/desktop/ms725497%28v=vs.85%29.aspx
C# wrapper over send message
http://www.pinvoke.net/default.aspx/user32.sendnotifymessage
I know (and it works) that in C#, this property tells me if the current session is a RDP one:
System.Windows.Forms.SystemInformation.TerminalServerSession
Now, I have a service that receives logon/logoff and lock/unlock events, and I need a way to tell this service if the session being started is rdp or local.
Problem: the service runs as SYSTEM user, and the property above always returns false (I think because SYSTEM is always considered a local connected user)
I've tried to search in the register for the Volatile Environment subkey to check if there is the subkey that identify the RDP Session Name: this works in lock/unlock handler, but in the logon handler the subkey doesn't exists yet (subkey is created after login completes).
Any idea about how to work around this problem?
While a wait for the key creation for a limited amount of time works, I would like a less "kludgy" way to do it.
You can detect whether a session is a local or remote session by calling WTSQuerySessionInformation with the WTSInfoClass parameter set to WTSClientProtocolType. If you'd prefer to avoid the P/Invokes, you can use the Cassia library: new TerminalServicesManager().GetLocalServer().GetSession(sessionId).ClientProtocolType.
Caveats: This won't help you when processing session logoff messages because you will not (reliably) be able to fetch information about the session, since it's in the process of being destroyed (but that seems relatively easy to work around). Also, the ClientProtocolType Cassia property mentioned above has not yet been released, but you can grab a trunk build from the build server by logging in as a guest and using the artifacts link.