Prevent Windows workstation (desktop) from locking while running a WPF program - c#

Issue:
I have a WPF fullscreen application, which acts as a dashboard. The computer is in domain and domain policies enforce the computer to be locked in 10 minutes after the last user activity. I want to prevent the workstation (or desktop) from locking automatically.
An example of such behavior: Windows Media Player, which prevents this while a movie is running.
Known solutions (kinda workarounds):
It is possible to send a Win32 Mouse Move event every fixed interval of time (for example, every minute)
It is possible to send a key to the program (for example "Left Shift" key up) every fixed interval of time (for example, every minute)
QUESTION:
How can I prevent windows workstation from locking without using these workarounds?
Disclaimer:
I was pretty sure, there should be a similar question answered somewhere on StackOverflow, but i didn't find any. I would appreciate, if you could point me into the right direction.

The solution has been pointed out through the comments, but I'm providing a simple starter solution for anyone else arriving via a web search:
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern EXECUTION_STATE SetThreadExecutionState(EXECUTION_STATE esFlags);
public App()
{
InitializeComponent();
App.Current.Startup += new StartupEventHandler((sender, e) =>
{
SetThreadExecutionState(EXECUTION_STATE.ES_DISPLAY_REQUIRED | EXECUTION_STATE.ES_CONTINUOUS);
});
App.Current.Exit += new ExitEventHandler((sender, e) =>
{
SetThreadExecutionState(EXECUTION_STATE.ES_CONTINUOUS);
});
}
}
[FlagsAttribute]
public enum EXECUTION_STATE : uint
{
ES_AWAYMODE_REQUIRED = 0x00000040,
ES_CONTINUOUS = 0x80000000,
ES_DISPLAY_REQUIRED = 0x00000002,
ES_SYSTEM_REQUIRED = 0x00000001
// Legacy flag, should not be used.
// ES_USER_PRESENT = 0x00000004
}
An alternative place to put the logic would be within an event handler for StateChanged on your main application window:
this.StateChanged += new EventHandler((sender, e) =>
{
if (WindowState == System.Windows.WindowState.Maximized)
{
SetThreadExecutionState(EXECUTION_STATE.ES_DISPLAY_REQUIRED | EXECUTION_STATE.ES_CONTINUOUS);
}
else
{
SetThreadExecutionState(EXECUTION_STATE.ES_CONTINUOUS);
}
});

Related

Use a transparent, click-through form to monitor for activity

A client has a problem with employees opening a program displaying sensitive information and walking away without closing it. They've asked me to monitor when the application is open and close it after a certain amount of inactivity.
What I've done is created a new program that launches the original, but with another form on top of it. If my form is transparent, I can click through the form with no problems, and manipulate the underlying program. If my form is (slightly) opaque, clicks will register with my program, but will not pass through.
What I need is a way to let my form register that a click has happened, reset its timer, and pass the click through to the underlying application. I can reset the timer, or I can pass the click through, but not both.
Here's the code I'm currently trying. The first section defines my form as transparent and keeps it on top of the other application.
private void Form1_Load(object sender, EventArgs e)
{
this.Opacity = 0.40;
this.TopMost = true;
}
I thought this would let me monitor for a click, reset the timer, then pass it through, but it's not working, so I must be missing something. EDIT: WT_NCHITTEST = 0x84, and HTTRANSPARENT = -1, as indicated here: https://msdn.microsoft.com/en-us/library/windows/desktop/ms645618%28v=vs.85%29.aspx?f=255&MSPPError=-2147217396
[System.Security.Permissions.PermissionSet(System.Security.Permissions.SecurityAction.Demand, Name = "FullTrust")]
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_NCHITTEST) // m.Msg 0x84 means that Windows is asking our form whether it is "transparent" or not.
{
timeToClose = 10;
m.Result = new IntPtr(HTTRANSPARENT); // tell Windows yes, to pass everything through.
}
base.WndProc(ref m);
}
That is way over engineered, I implement inactivity checks with System.Timers.Timer and PInvoke GetLastInputInfo. Run a second application that monitors for workstation inactivity and close sensitive applications when the threshold is violated.
Initialize the timer with an interval and set it to auto-reset, then just check for inactivity every time the timer elapses. If the inactivity time is greater than your threshold, shut it down.
Here is the code for GetLastInputInfo.
public static class Tracker
{
/// <summary>
/// Reference to the GetLastInputInfo in the user32.dll file.
/// </summary>
/// <param name="plii">LASTINPUTINFO struct</param>
/// <returns></returns>
[DllImport("user32.dll")]
private static extern bool GetLastInputInfo(ref LASTINPUTINFO plii);
/// <summary>
/// Polls the system for the milliseconds elapsed since last user input.
/// </summary>
/// <returns>Milliseconds since last user input.</returns>
public static uint GetIdleTime()
{
LASTINPUTINFO lastInput = new LASTINPUTINFO();
lastInput.cbSize = (uint)Marshal.SizeOf(lastInput);
GetLastInputInfo(ref lastInput);
return ((uint)Environment.TickCount - lastInput.dwTime);
}
}
/// <summary>
/// Struct required and populated by the user32.dll GetLastInputInfo method.
/// </summary>
internal struct LASTINPUTINFO
{
public uint cbSize; //Size of the struct.
public uint dwTime; //TickCount at last input.
}
My inactivity timer implementation looks like this:
public void InitializeTimer()
{
ActivityTimer = new System.Timers.Timer(Configuration.TimeoutSettings["IntervalMilliseconds"]);
ActivityTimer.AutoReset = true;
ActivityTimer.Elapsed += OnElapsedPollForActivity;
ActivityTimer.Start();
}
/// <summary>
/// Method is called in ValidationForm_FormClosing, unsubscribes handler from event and stops the clock.
/// </summary>
public void TerminateTimer()
{
ActivityTimer.Elapsed -= OnElapsedPollForActivity;
ActivityTimer.Stop();
}
/// <summary>
/// Fires on ActivityTimer.Elapsed, polls workstation for time since last user input.
/// </summary>
private void OnElapsedPollForActivity(object sender, System.Timers.ElapsedEventArgs e)
{
if (Tracker.GetIdleTime() > Configuration.TimeoutSettings["TriggerMilliseconds"])
{
Logger.LogException(CurrentSession.SessionID, DateTime.Now, new InactivityException("Detected extended period of workstation inactivity."));
Application.Exit();
}
}

Detect if on-screen keyboard is open (TabTip.exe)

I am working on a WPF/C# application for completing forms. I am trying to find a way to determine if the TapTip keyboard (TabTip.exe / metro-like keyboard for windows 8 desktop) is minimized / not visible in windows 8.
I have been able to detect if the osk keyboard (osk.exe / windows accessibility on-screen keyboard) is minimized, but the same process does not seem to work with the TabTip keyboard.
To detect if the keyboard is minimized I:
1. Find the keyboard's process
2. Get the MainWindowHandle
3. Use the showCmd property of the WINDOWPLACEMENT (found using MainWindowHandle)
4. Use showCmd value to determine if window is minimized
The problems I have run into are:
- the TabTip process has a MainWindowHandle of 0 (so I can't use it to find the WINDOWPLACEMENT information)
- the values for WINDOWPLACEMENT.showCmd are the same when TabTip is open and minimized
In order to find the handle of the TabTip window I used ENUMWINDOWS to get all the window handles, GETWINDOWTHREADPROCESSID to get the process ids, then compared the ids to the TabTip process id.
Any help with this would be appreciated. Also this is my first post. I think I did this right, but if not please let me know how to fix it.
I tried a few different methods before finding one which works. Using IsWindowVisible() didn't work and I also didn't have any joy with GetWindowPlacement() or GetIconic(). In the end I used GetWindowLong() and checked for the WS_VISIBLE coming back. A quick console app to demonstrate is as follows:
using System;
using System.Diagnostics;
using Microsoft.Win32;
using System.Runtime.InteropServices;
using System.Threading;
namespace CSharpTesting
{
class Program
{
/// <summary>
/// The window is initially visible. See http://msdn.microsoft.com/en-gb/library/windows/desktop/ms632600(v=vs.85).aspx.
/// </summary>
public const UInt32 WS_VISIBLE = 0X94000000;
/// <summary>
/// Specifies we wish to retrieve window styles.
/// </summary>
public const int GWL_STYLE = -16;
[DllImport("user32.dll")]
public static extern IntPtr FindWindow(String sClassName, String sAppName);
[DllImport("user32.dll", SetLastError = true)]
static extern UInt32 GetWindowLong(IntPtr hWnd, int nIndex);
static void Main(string[] args)
{
// Crappy loop to poll window state.
while (true)
{
if (IsKeyboardVisible())
{
Console.WriteLine("keyboard is visible");
}
else
{
Console.WriteLine("keyboard is NOT visible");
}
Thread.Sleep(1000);
}
}
/// <summary>
/// Gets the window handler for the virtual keyboard.
/// </summary>
/// <returns>The handle.</returns>
public static IntPtr GetKeyboardWindowHandle()
{
return FindWindow("IPTip_Main_Window", null);
}
/// <summary>
/// Checks to see if the virtual keyboard is visible.
/// </summary>
/// <returns>True if visible.</returns>
public static bool IsKeyboardVisible()
{
IntPtr keyboardHandle = GetKeyboardWindowHandle();
bool visible = false;
if (keyboardHandle != IntPtr.Zero)
{
UInt32 style = GetWindowLong(keyboardHandle, GWL_STYLE);
visible = (style == WS_VISIBLE);
}
return visible;
}
}
}
This totally works!
//
// BOOL IsVirtualKeyboardVisible()
//
// Returns TRUE if Virtual Keyboard/Input Pane is visible
// Returns FALSE if Virtual Keyboard/Input Pane is not visible
__declspec(dllexport) BOOL __cdecl IsVirtualKeyboardVisible()
{
BOOL bRet = FALSE;
RECT InputPaneScreenLocation = { 0, 0, 0, 0 };
__try
{
HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
IFrameworkInputPane *IinputPane = NULL;
if (SUCCEEDED(hr))
{
//
// http://msdn.microsoft.com/en-us/library/windows/desktop/hh706967(v=vs.85).aspx
//
hr = CoCreateInstance(__uuidof(FrameworkInputPane), 0, CLSCTX_ALL, __uuidof(IFrameworkInputPane), (LPVOID*)&IinputPane);
IinputPane->Location(&InputPaneScreenLocation);
if (InputPaneScreenLocation.bottom == 0 && InputPaneScreenLocation.left == 0 &&
InputPaneScreenLocation.right == 0 && InputPaneScreenLocation.top == 0)
{
// VKB is not visible
bRet = FALSE;
}
else
{
// VKB is visible
bRet = TRUE;
}
}
} // try
__finally
{
CoUninitialize();
}
return bRet;
}
If I remember correctly, the window class name for TabTip.exe is IPTip_Main_Window. You can use the Win32 API FindWindow to get the HWND of TabTip.exe. This is more reliable than using the window title and recommended as some windows can have empty titles (or the title can change).
Your current approach using EnumWindows could be flawed due to a single process having many windows (or windows with child windows). You can use a tool like Spy++ to find the actual window you want and the respective class name.
You can still use GetWindowHandleThreadProcessId to retrieve the processID at that point, though I do not think you will need it for simple window state monitoring.
Also, try using Win32 APIs instead of whatever is built into the CLR. For example GetWindowPlacement.
Note from MSDN:
The flags member of WINDOWPLACEMENT retrieved by this function is
always zero. If the window identified by the hWnd parameter is
maximized, the showCmd member is SW_SHOWMAXIMIZED. If the window is
minimized, showCmd is SW_SHOWMINIMIZED. Otherwise, it is
SW_SHOWNORMAL.
Hope that helps, if you still need further assistance leave a comment and I'll make an edit once I get back to my Win8 machine.

WPF: application Idle Time

I need to count the idle time of my WPF application (Idle time = when no keyboard input,mouse input (movement + clicks ) had occurred ).
So far I tried 2 approaches but none of them seem to be working:
Using the dispatcher to invoke a delegate each time it get a contextIdle priority, the problem is that binding and many other operations invoke it and thus I can't really use that.
using the input manager I registered to the "System.Windows.Input.InputManager.Current.PostProcessInput" event and each time it was invoked I restarted the idle time count.
The second approach seemed promising but the problem is that when the mouse is over the application (it has focus) I keep getting the event.
Any Other ideas? or maybe a way to modify the 2nd solution to work?
I solved the problem using a few different techniques rolled up to give me a pretty good solution. I use GetLastInput to work out when the system was last touched This is well documented elsewhere, but here's my method:
public static class User32Interop
{
public static TimeSpan GetLastInput()
{
var plii = new LASTINPUTINFO();
plii.cbSize = (uint)Marshal.SizeOf(plii);
if (GetLastInputInfo(ref plii))
return TimeSpan.FromMilliseconds(Environment.TickCount - plii.dwTime);
else
throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error());
}
[DllImport("user32.dll", SetLastError = true)]
static extern bool GetLastInputInfo(ref LASTINPUTINFO plii);
struct LASTINPUTINFO {
public uint cbSize;
public uint dwTime;
}
}
This only tells me when the system has been idle, not the application. If the user clicks into Word and works there for an hour, I still want a timeout. To handle this case, I simply remember when my application loses focus by overriding the OnDeactivated and OnActivated methods on the application object:
override protected void OnDeactivated(EventArgs e)
{
this._lostFocusTime = DateTime.Now;
base.OnDeactivated(e);
}
protected override void OnActivated(EventArgs e)
{
this._lostFocusTime = null;
base.OnActivated(e);
}
My IsIdle routine was added to the application object. It handles the global case where the app has focus but nothing happened (IsMachineIdle) and the specific case where the application lost focus while the user is doing other stuff (isAppIdle ):
public bool IsIdle
{
get
{
TimeSpan activityThreshold = TimeSpan.FromMinutes(1);
TimeSpan machineIdle = Support.User32Interop.GetLastInput();
TimeSpan? appIdle = this._lostFocusTime == null ? null : (TimeSpan?)DateTime.Now.Subtract(_lostFocusTime.Value);
bool isMachineIdle = machineIdle > activityThreshold ;
bool isAppIdle = appIdle != null && appIdle > activityThreshold ;
return isMachineIdle || isAppIdle;
}
}
The last thing I did was create a timer loop that polled this flag event few seconds.
This seems to work fine.
Well no one seemed to answer so I continued digging and found a relatively simple solution using the OS last input + up time. the code is really simple but this solution make me do data polling which I never recommend and also instead of being in the application level it's in the OS level which is not the exact solution I needed.
If someone ever opens this thread this is the code, just use GetIdleTime():
public class IdleTimeService
{
//Importing the Dll & declaring the necessary function
[DllImport("user32.dll")]
private static extern bool GetLastInputInfo(ref LASTINPUTINFO plii);
/// <summary>
/// return the current idle time (in ms)
/// </summary>
/// <returns>current idle time in ms</returns>
public static int GetIdleTime()
{
//Creating the object of the structure
LASTINPUTINFO lastone = new LASTINPUTINFO();
//Initialising
lastone.cbSize = (uint)Marshal.SizeOf(lastone);
lastone.dwTime = 0;
int idleTime = 0;
//To get the total time after starting the system.
int tickCount = System.Environment.TickCount;
//Calling the dll function and getting the last input time.
if (GetLastInputInfo(ref lastone))
{
idleTime = tickCount - (int)lastone.dwTime;
return idleTime;
}
else
return 0;
}
}

How to prevent my C# winforms application from stealing focus when I set the visible properly to true?

I've got a C# winforms application that runs in the background, listening for hotkeys to be pressed. When a hotkey is pressed, my form makes a brief appearance. The form is always running, but set to hidden until I receive a hotkey event, at which time I set the visible property to true. The code looks like this:
void hook_volumeDown(object sender, KeyPressedEventArgs e)
{
this.Visible = true;
}
It should be noted that the topmost property of this form is set to true.
The really odd part is, after my C# app has stolen focus from another application, it will never do it again. For example: I launch my app, then launch some fullscreep app like Team Fortress 2. Then I press my hotkey. Team Fortress 2 minimizes, and I see my form. Then, however, I can restore TF2, and press my hotkey again all I want (with the desired effect), and TF2 will remain focused.
At any rate, I'm looking for a way to fix this. I've found a lot of questions here covering similar problems, but all of them are related to creating/launching a new form, not making an existing one visible (unless I missed something). I could rework the application to create a new form every time I need one, but that would entail creating yet another form to be invisible all the time just to wait for hotkey events, so I'd rather leave it as it is.
Any ideas?
I think you problem is related to the fact that Visible = true behaves differently between the first and subsequent calls. The first time visible is called and the window handle has not been created, the Window is created by calling CreateWindowEx which has some style parameters which controls how the window should behave. I think you need to make sure that the window is created with the style WS_EX_NOACTIVATE, which you can do by overriding CreateParams.
Other things to try out:
1) The ShowWindow function (used by Visible = true) ignores the focus parameter the first time it is called (http://msdn.microsoft.com/en-us/library/ms633548%28VS.85%29.aspx) if the program provides a STARTUPINFO structure. Dig into reflector and find out if the Form class provides a STARTUPINFO structure and if so, how to manipulate it.
2) The Form has a ShowWithoutActivation property than can be overriden and set to true, have you overriden this?
Sorry for the "no exact answer", but I hope this at least gives you some starting points for further investigation. Good luck.
Seeing KeyPressedEventArgs being used in your function looks really strange. Hot keys can be implemented by P/Invoking the RegisterHotKey() API function. It sends a message to your window when the hot key is pressed. Here's an example of a form that's invisible at start up, springs alive when you press the hot key. Ctrl+Alt+U in this case:
using System;
using System.Windows.Forms;
using System.Runtime.InteropServices;
namespace WindowsFormsApplication1 {
public partial class Form1 : Form {
private const int MYKEYID = 0; // In case you want to register more than one...
public Form1() {
InitializeComponent();
this.FormClosing += (s, args) => UnregisterHotKey(this.Handle, MYKEYID);
}
protected override void SetVisibleCore(bool value) {
if (value && !this.IsHandleCreated) {
this.CreateHandle();
RegisterHotKey(this.Handle, MYKEYID, MOD_CONTROL + MOD_SHIFT, Keys.U);
value = false;
}
base.SetVisibleCore(value);
}
protected override void WndProc(ref Message m) {
if (m.Msg == WM_HOTKEY && m.WParam.ToInt32() == MYKEYID) {
this.Visible = true;
if (this.WindowState == FormWindowState.Minimized)
this.WindowState = FormWindowState.Normal;
SetForegroundWindow(this.Handle);
}
base.WndProc(ref m);
}
// P/Invoke declarations
private const int WM_HOTKEY = 0x312;
private const int MOD_ALT = 1;
private const int MOD_CONTROL = 2;
private const int MOD_SHIFT = 4;
[DllImport("user32.dll")]
private static extern int RegisterHotKey(IntPtr hWnd, int id, int modifier, Keys vk);
[DllImport("user32.dll")]
private static extern bool UnregisterHotKey(IntPtr hWnd, int id);
[DllImport("user32.dll")]
private static extern bool SetForegroundWindow(IntPtr hWnd);
}
}
Note that the SetForegroundWindow() function is the rub, possibly also the source of the problem you describe in your question. Windows doesn't permit an app to shove a window in the user's face when the user is actively using another window. At least several seconds of inactivity must expire before it will allow the window to steal the focus. With the given code, that is easy enough to see, the taskbar button of your form will be blinking. Avoid setting the ShowInTaskbar property to false. It isn't necessary to do so with this code, the taskbar button won't show up until the hot key is pressed.

Wiggling the mouse

OK. This is a bit of a vanity app, but I had a situation today at work where I was in a training class and the machine was set to lock every 10 minutes. Well, if the trainers got excited about talking - as opposed to changing slides - the machine would lock up.
I'd like to write a teeny app that has nothing but a taskbar icon that does nothing but move the mouse by 1 pixel every 4 minutes.
I can do that in 3 ways with Delphi (my strong language) but I'm moving to C# for work and I'd like to know the path of least resistance there.
for C# 3.5
without notifyicon therefore you will need to terminate this application in task manager manually
using System;
using System.Drawing;
using System.Windows.Forms;
static class Program
{
static void Main()
{
Timer timer = new Timer();
// timer.Interval = 4 minutes
timer.Interval = (int)(TimeSpan.TicksPerMinute * 4 / TimeSpan.TicksPerMillisecond);
timer.Tick += (sender, args) => { Cursor.Position = new Point(Cursor.Position.X + 1, Cursor.Position.Y + 1); };
timer.Start();
Application.Run();
}
}
The "correct" way to do this is to respond to the WM_SYSCOMMAND message. In C# this looks something like this:
protected override void WndProc(ref Message m)
{
// Abort screensaver and monitor power-down
const int WM_SYSCOMMAND = 0x0112;
const int SC_MONITOR_POWER = 0xF170;
const int SC_SCREENSAVE = 0xF140;
int WParam = (m.WParam.ToInt32() & 0xFFF0);
if (m.Msg == WM_SYSCOMMAND &&
(WParam == SC_MONITOR_POWER || WParam == SC_SCREENSAVE)) return;
base.WndProc(ref m);
}
According to MSDN, if the screensaver password is enabled by policy on Vista or above, this won't work. Presumably programmatically moving the mouse is also ignored, though I have not tested this.
When I work from home, I do this by tying the mouse cord to a desktop fan which oscillates left to right. It keeps the mouse moving and keeps the workstation from going to sleep.
Something like this should work (though, you will want to change the interval).
public Form1()
{
InitializeComponent();
Timer Every4Minutes = new Timer();
Every4Minutes.Interval = 10;
Every4Minutes.Tick += new EventHandler(MoveNow);
Every4Minutes.Start();
}
void MoveNow(object sender, EventArgs e)
{
Cursor.Position = new Point(Cursor.Position.X - 1, Cursor.Position.Y - 1);
}
(Windows 10 / .Net 5 / C# 9.0)
Instead of faking activity, you could
inform the system that it is in use, thereby preventing the system
from entering sleep or turning off the display while the application
is running
using SetThreadExecutionState, as described on PInvoke.net :
using System;
using System.Runtime.InteropServices;
using System.Threading;
namespace VanityApp
{
internal static class Program
{
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern ExecutionState SetThreadExecutionState(ExecutionState esFlags);
[Flags]
private enum ExecutionState : uint
{
ES_AWAYMODE_REQUIRED = 0x00000040,
ES_CONTINUOUS = 0x80000000,
ES_DISPLAY_REQUIRED = 0x00000002,
ES_SYSTEM_REQUIRED = 0x00000001
}
private static void Main()
{
using AutoResetEvent autoResetEvent = new AutoResetEvent(false);
using Timer timer = new Timer(state => SetThreadExecutionState(ExecutionState.ES_AWAYMODE_REQUIRED | ExecutionState.ES_CONTINUOUS | ExecutionState.ES_DISPLAY_REQUIRED | ExecutionState.ES_SYSTEM_REQUIRED), autoResetEvent, 0, -1);
autoResetEvent.WaitOne();
}
}
}
The Timer is a System.Threading.Timer, with its handy constructor, and it uses AutoResetEvent.WaitOne() to avoid exiting immediately.
Raf provided a graceful answer to the problem for Win10 world, but unfortunately, his autoResetEvent.WaitOne() instruction blocks the thread, and therefore it must be in a separate thread of its own.
What worked for me can actually run in the main thread, the code doesn't have to be placed in the Main() method, and you can actually have a button to enable this functionality and one to disable it.
First, you certainly need to define the execution state flags:
[Flags]
private enum ExecutionState : uint // options to control monitor behavior
{
ES_AWAYMODE_REQUIRED = 0x00000040, // prevent idle-to-sleep
ES_CONTINUOUS = 0x80000000, // allow monitor power down
ES_DISPLAY_REQUIRED = 0x00000002, // prevent monitor power down
ES_SYSTEM_REQUIRED = 0x00000001 // keep system awake
}
Now, whenever you want to keep your system awake and block your monitor from turning off or idling to sleep, all you need to do, is execute a single command:
SetThreadExecutionState(ExecutionState.ES_AWAYMODE_REQUIRED | ExecutionState.ES_CONTINUOUS | ExecutionState.ES_DISPLAY_REQUIRED | ExecutionState.ES_SYSTEM_REQUIRED);
Then, if you want to undo this action and return your system back to its original execution state, just issue the following command:
SetThreadExecutionState(ExecutionState.ES_CONTINUOUS);
Keep in mind, each command will return the previous execution state, which means, when you first alter this state, you can cache the returned value locally and use it if/when you want to restore the previous state.

Categories