From f4841209d099af238e23319656779bcf1d943a21 Mon Sep 17 00:00:00 2001 From: ryannewington Date: Sat, 22 Jul 2017 16:23:22 +1000 Subject: [PATCH] Adds support for detecting if an application has requested the screen stay on, such as during a presentation, media playback or video conference --- src/Lithnet.IdleLogoff/AdminCheck.cs | 6 ++ src/Lithnet.IdleLogoff/ChangeLog.txt | 6 -- src/Lithnet.IdleLogoff/EventLogging.cs | 1 + src/Lithnet.IdleLogoff/ExecutionState.cs | 18 +++++ src/Lithnet.IdleLogoff/NativeMethods.cs | 32 +++++++- .../en-US/lithnet.idlelogoff.adml | 2 + .../PolicyDefinitions/lithnet.idlelogoff.admx | 17 +++- .../PowerInformationLevel.cs | 31 ++++++++ src/Lithnet.IdleLogoff/Program.cs | 27 ++++--- src/Lithnet.IdleLogoff/Settings.cs | 77 +++++++++++++++++-- .../frmSettings.Designer.cs | 18 ++++- src/Lithnet.IdleLogoff/frmSettings.cs | 38 +++++---- src/Lithnet.IdleLogoff/frmSettings.resx | 4 +- .../lithnet.idlelogoff.csproj | 10 +-- src/lithnet.idlelogoff.setup/Product.wxs | 2 +- 15 files changed, 235 insertions(+), 54 deletions(-) delete mode 100644 src/Lithnet.IdleLogoff/ChangeLog.txt create mode 100644 src/Lithnet.IdleLogoff/ExecutionState.cs create mode 100644 src/Lithnet.IdleLogoff/PowerInformationLevel.cs diff --git a/src/Lithnet.IdleLogoff/AdminCheck.cs b/src/Lithnet.IdleLogoff/AdminCheck.cs index 0a55647..7dae0f2 100644 --- a/src/Lithnet.IdleLogoff/AdminCheck.cs +++ b/src/Lithnet.IdleLogoff/AdminCheck.cs @@ -15,6 +15,12 @@ public static bool TryRestartElevated(out bool userCanceled) startInfo.UseShellExecute = true; startInfo.WorkingDirectory = Environment.CurrentDirectory; startInfo.FileName = Application.ExecutablePath; + + if (System.Diagnostics.Debugger.IsAttached) + { + startInfo.Arguments = "/attach"; + } + startInfo.Verb = "runas"; userCanceled = false; diff --git a/src/Lithnet.IdleLogoff/ChangeLog.txt b/src/Lithnet.IdleLogoff/ChangeLog.txt deleted file mode 100644 index d726cfb..0000000 --- a/src/Lithnet.IdleLogoff/ChangeLog.txt +++ /dev/null @@ -1,6 +0,0 @@ -25/11/2014 1.0.5442 -Added support for user-based group policy settings - -25/2/2012 1.0.4438 -Fixed an issue where the program would terminate if the event log on the computer was full - diff --git a/src/Lithnet.IdleLogoff/EventLogging.cs b/src/Lithnet.IdleLogoff/EventLogging.cs index 33f8c78..350137a 100644 --- a/src/Lithnet.IdleLogoff/EventLogging.cs +++ b/src/Lithnet.IdleLogoff/EventLogging.cs @@ -77,6 +77,7 @@ public static void LogEvent(string eventText, int eventId) public static void LogEvent(string eventText, int eventID, EventLogEntryType entryType) { + Trace.WriteLine($"{entryType}: {eventID}: {eventText}"); if (LogEnabled) { EventLog.WriteEntry(evtSource, eventText, entryType, eventID); diff --git a/src/Lithnet.IdleLogoff/ExecutionState.cs b/src/Lithnet.IdleLogoff/ExecutionState.cs new file mode 100644 index 0000000..94c2171 --- /dev/null +++ b/src/Lithnet.IdleLogoff/ExecutionState.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; + +namespace Lithnet.idlelogoff +{ + [Flags] + public enum EXECUTION_STATE : ulong + { + NONE = 0, + AWAYMODE_REQUIRED = 0x00000040, + CONTINUOUS = 0x80000000, + DISPLAY_REQUIRED = 0x00000002, + SYSTEM_REQUIRED = 0x00000001, + } +} \ No newline at end of file diff --git a/src/Lithnet.IdleLogoff/NativeMethods.cs b/src/Lithnet.IdleLogoff/NativeMethods.cs index 4e610e4..f2e06e1 100644 --- a/src/Lithnet.IdleLogoff/NativeMethods.cs +++ b/src/Lithnet.IdleLogoff/NativeMethods.cs @@ -1,16 +1,40 @@ -namespace Lithnet.idlelogoff -{ - using System.Runtime.InteropServices; - using System.ComponentModel; +using System; +using System.Runtime.InteropServices; +using System.ComponentModel; +namespace Lithnet.idlelogoff +{ public class NativeMethods { + [DllImport("powrprof.dll")] + private static extern int CallNtPowerInformation( + POWER_INFORMATION_LEVEL informationLevel, + IntPtr lpInputBuffer, + int nInputBufferSize, + out EXECUTION_STATE state, + int nOutputBufferSize + ); + [DllImport("User32.dll", CharSet = CharSet.Unicode, SetLastError = true)] private static extern bool GetLastInputInfo(ref LastInputInfo lastInputInfo); [DllImport("User32.dll", CharSet = CharSet.Unicode, SetLastError = true)] private static extern bool ExitWindowsEx(ShutdownFlags flags, int reason); + public static bool IsDisplayRequested() + { + EXECUTION_STATE state; + + int retval = CallNtPowerInformation(POWER_INFORMATION_LEVEL.SystemExecutionState, IntPtr.Zero, 0, out state, sizeof(EXECUTION_STATE)); + + if (retval != 0) + { + throw new Win32Exception(retval); + } + + return (state.HasFlag(EXECUTION_STATE.DISPLAY_REQUIRED)); + } + public static int GetLastInputTime() { LastInputInfo info = new LastInputInfo(); diff --git a/src/Lithnet.IdleLogoff/PolicyDefinitions/en-US/lithnet.idlelogoff.adml b/src/Lithnet.IdleLogoff/PolicyDefinitions/en-US/lithnet.idlelogoff.adml index b12c798..d51fd60 100644 --- a/src/Lithnet.IdleLogoff/PolicyDefinitions/en-US/lithnet.idlelogoff.adml +++ b/src/Lithnet.IdleLogoff/PolicyDefinitions/en-US/lithnet.idlelogoff.adml @@ -14,9 +14,11 @@ Idle timeout (minutes) + Ignore sleep prevention requests from applications such as media playback Idle timeout (minutes) + Ignore sleep prevention requests from applications such as media playback diff --git a/src/Lithnet.IdleLogoff/PolicyDefinitions/lithnet.idlelogoff.admx b/src/Lithnet.IdleLogoff/PolicyDefinitions/lithnet.idlelogoff.admx index 528c889..c2c6e01 100644 --- a/src/Lithnet.IdleLogoff/PolicyDefinitions/lithnet.idlelogoff.admx +++ b/src/Lithnet.IdleLogoff/PolicyDefinitions/lithnet.idlelogoff.admx @@ -23,6 +23,14 @@ + + + + + + + + @@ -30,7 +38,14 @@ - + + + + + + + + \ No newline at end of file diff --git a/src/Lithnet.IdleLogoff/PowerInformationLevel.cs b/src/Lithnet.IdleLogoff/PowerInformationLevel.cs new file mode 100644 index 0000000..36fa8a7 --- /dev/null +++ b/src/Lithnet.IdleLogoff/PowerInformationLevel.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Lithnet.idlelogoff +{ + public enum POWER_INFORMATION_LEVEL + { + AdministratorPowerPolicy = 9, + LastSleepTime = 15, + LastWakeTime = 14, + ProcessorInformation = 11, + ProcessorPowerPolicyAc = 18, + ProcessorPowerPolicyCurrent = 22, + ProcessorPowerPolicyDc = 19, + SystemBatteryState = 5, + SystemExecutionState = 16, + SystemPowerCapabilities = 4, + SystemPowerInformation = 12, + SystemPowerPolicyAc = 0, + SystemPowerPolicyCurrent = 8, + SystemPowerPolicyDc = 1, + SystemReserveHiberFile = 10, + VerifyProcessorPowerPolicyAc = 20, + VerifyProcessorPowerPolicyDc = 21, + VerifySystemPolicyAc = 2, + VerifySystemPolicyDc = 3 + } +} diff --git a/src/Lithnet.IdleLogoff/Program.cs b/src/Lithnet.IdleLogoff/Program.cs index ec99662..ca250cd 100644 --- a/src/Lithnet.IdleLogoff/Program.cs +++ b/src/Lithnet.IdleLogoff/Program.cs @@ -11,7 +11,6 @@ public static class Program private static Timer eventTimer; private static bool inTimer = false; private static bool backgroundMode = false; - private static bool debug = false; private static int initialTime = 0; [STAThread] @@ -19,11 +18,6 @@ public static void Main() { try { - if (Debugger.IsAttached) - { - debug = true; - } - EventLogging.InitEventLog(); ValidateCommandLineArgs(); @@ -94,6 +88,10 @@ private static void ValidateCommandLineArgs() { backgroundMode = true; } + else if (arg.ToLower() == "/attach") + { + System.Diagnostics.Debugger.Launch(); + } else { MessageBox.Show("An invalid command line argument was specified: " + arg); @@ -125,12 +123,23 @@ private static void EventTimer_Tick(object sender, EventArgs e) if (initialTime != logoffidletime) { EventLogging.TryLogEvent($"Idle timeout limit has changed. User {Environment.UserDomainName}\\{Environment.UserName} will now be logged off after {Settings.IdleLimit} minutes", EventLogging.EVT_TIMERINTERVALCHANGED); + initialTime = logoffidletime; } int currentticks = NativeMethods.GetLastInputTime(); + if (!Settings.IgnoreDisplayRequested && NativeMethods.IsDisplayRequested()) + { + Trace.WriteLine("An application has requested the system display stay on"); + Program.lastDateTime = DateTime.Now; + Program.lastTicks = currentticks; + return; + } + + if (currentticks != Program.lastTicks) { + Trace.WriteLine("Input received"); Program.lastTicks = currentticks; Program.lastDateTime = DateTime.Now; return; @@ -138,9 +147,9 @@ private static void EventTimer_Tick(object sender, EventArgs e) if (DateTime.Now.Subtract(Program.lastDateTime).TotalMilliseconds > logoffidletime) { - EventLogging.TryLogEvent($"User {Environment.UserName} has passed the idle time limit of {Settings.IdleLimit} minutes. Initating logoff.", EventLogging.EVT_LOGOFFEVENT); + EventLogging.TryLogEvent($"User {Environment.UserName} has passed the idle time limit of {Settings.IdleLimit} minutes. Initiating logoff.", EventLogging.EVT_LOGOFFEVENT); - if (!debug) + if (!Settings.Debug) { try { @@ -151,7 +160,7 @@ private static void EventTimer_Tick(object sender, EventArgs e) } catch (Exception ex) { - EventLogging.TryLogEvent("An error occured trying to log off the user\n" + ex.Message, EventLogging.EVT_LOGOFFFAILED); + EventLogging.TryLogEvent("An error occurred trying to log off the user\n" + ex.Message, EventLogging.EVT_LOGOFFFAILED); } } else diff --git a/src/Lithnet.IdleLogoff/Settings.cs b/src/Lithnet.IdleLogoff/Settings.cs index b593447..3524d63 100644 --- a/src/Lithnet.IdleLogoff/Settings.cs +++ b/src/Lithnet.IdleLogoff/Settings.cs @@ -11,6 +11,7 @@ public static class Settings private static string strBaseSettingsKey = "Software\\Lithnet\\IdleLogOff"; private static string strBasePolicyKey = "Software\\Policies\\Lithnet\\IdleLogOff"; private static RegistryKey _regkeySettings; + private static RegistryKey _regkeySettingsWritable; private static RegistryKey _regkeyMachinePolicy; private static RegistryKey _regkeyUserPolicy; @@ -52,19 +53,20 @@ private static RegistryKey SettingsKeyWriteable try { - if (_regkeySettings == null) + if (_regkeySettingsWritable == null) { - _regkeySettings = Registry.LocalMachine.CreateSubKey(strBaseSettingsKey); + + _regkeySettingsWritable = Registry.LocalMachine.CreateSubKey(strBaseSettingsKey); } else { try { - int x = _regkeySettings.ValueCount; + int x = _regkeySettingsWritable.ValueCount; } catch // catch handler for a key that is missing and no longer valid { - _regkeySettings = Registry.LocalMachine.CreateSubKey(strBaseSettingsKey); + _regkeySettingsWritable = Registry.LocalMachine.CreateSubKey(strBaseSettingsKey); } } } @@ -73,7 +75,7 @@ private static RegistryKey SettingsKeyWriteable // could not open registry key } - return _regkeySettings; + return _regkeySettingsWritable; } @@ -223,6 +225,39 @@ public static int IdleLimit } } + public static bool Debug + { + get + { + if (System.Diagnostics.Debugger.IsAttached) + { + return true; + } + + object value = null; + bool status = false; + + value = GetSetting("Debug"); + if (value != null) + { + try + { + if ((int) value == 1) + { + status = true; + } + } + catch + { + //unable to cast + } + + } + + return status; + } + } + public static bool Enabled { get @@ -254,6 +289,38 @@ public static bool Enabled } } + public static bool IgnoreDisplayRequested + { + get + { + object value = null; + bool status = false; + + value = GetPolicyOrSetting("IgnoreDisplayRequested"); + if (value != null) + { + try + { + if ((int)value == 1) + { + status = true; + } + } + catch + { + //unable to cast + } + + } + return status; + } + set + { + SaveSetting("IgnoreDisplayRequested", Convert.ToInt32(value), RegistryValueKind.DWord); + } + } + + public static void Release() { if (_regkeyMachinePolicy != null) diff --git a/src/Lithnet.IdleLogoff/frmSettings.Designer.cs b/src/Lithnet.IdleLogoff/frmSettings.Designer.cs index 43e003b..16fc202 100644 --- a/src/Lithnet.IdleLogoff/frmSettings.Designer.cs +++ b/src/Lithnet.IdleLogoff/frmSettings.Designer.cs @@ -37,13 +37,14 @@ private void InitializeComponent() this.lbProductVersion = new System.Windows.Forms.Label(); this.lbGPControlled = new System.Windows.Forms.Label(); this.lbHiddenRefresh = new System.Windows.Forms.Label(); + this.ckIgnoreDisplayRequested = new System.Windows.Forms.CheckBox(); ((System.ComponentModel.ISupportInitialize)(this.udMinutes)).BeginInit(); this.SuspendLayout(); // // ckEnableIdleLogoff // this.ckEnableIdleLogoff.AutoSize = true; - this.ckEnableIdleLogoff.Location = new System.Drawing.Point(100, 54); + this.ckEnableIdleLogoff.Location = new System.Drawing.Point(100, 40); this.ckEnableIdleLogoff.Name = "ckEnableIdleLogoff"; this.ckEnableIdleLogoff.Size = new System.Drawing.Size(118, 17); this.ckEnableIdleLogoff.TabIndex = 0; @@ -52,7 +53,7 @@ private void InitializeComponent() // // udMinutes // - this.udMinutes.Location = new System.Drawing.Point(227, 72); + this.udMinutes.Location = new System.Drawing.Point(227, 58); this.udMinutes.Maximum = new decimal(new int[] { 35791, 0, @@ -75,7 +76,7 @@ private void InitializeComponent() // label1 // this.label1.AutoSize = true; - this.label1.Location = new System.Drawing.Point(97, 74); + this.label1.Location = new System.Drawing.Point(97, 60); this.label1.Name = "label1"; this.label1.Size = new System.Drawing.Size(124, 13); this.label1.TabIndex = 2; @@ -137,6 +138,15 @@ private void InitializeComponent() this.lbHiddenRefresh.TabIndex = 7; this.lbHiddenRefresh.Click += new System.EventHandler(this.lbHiddenRefresh_Click); // + // ckIgnoreDisplayRequested + // + this.ckIgnoreDisplayRequested.Location = new System.Drawing.Point(100, 88); + this.ckIgnoreDisplayRequested.Name = "ckIgnoreDisplayRequested"; + this.ckIgnoreDisplayRequested.Size = new System.Drawing.Size(315, 34); + this.ckIgnoreDisplayRequested.TabIndex = 8; + this.ckIgnoreDisplayRequested.Text = "Ignore sleep prevention requests from applications such as media playback"; + this.ckIgnoreDisplayRequested.UseVisualStyleBackColor = true; + // // frmSettings // this.AcceptButton = this.btOK; @@ -144,6 +154,7 @@ private void InitializeComponent() this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.CancelButton = this.btCancel; this.ClientSize = new System.Drawing.Size(427, 178); + this.Controls.Add(this.ckIgnoreDisplayRequested); this.Controls.Add(this.lbHiddenRefresh); this.Controls.Add(this.lbGPControlled); this.Controls.Add(this.lbProductVersion); @@ -177,5 +188,6 @@ private void InitializeComponent() private System.Windows.Forms.Label lbProductVersion; private System.Windows.Forms.Label lbGPControlled; private System.Windows.Forms.Label lbHiddenRefresh; + private System.Windows.Forms.CheckBox ckIgnoreDisplayRequested; } } \ No newline at end of file diff --git a/src/Lithnet.IdleLogoff/frmSettings.cs b/src/Lithnet.IdleLogoff/frmSettings.cs index 28e0a90..b53ecc5 100644 --- a/src/Lithnet.IdleLogoff/frmSettings.cs +++ b/src/Lithnet.IdleLogoff/frmSettings.cs @@ -23,11 +23,10 @@ private void RefreshUI() lbProductVersion.Text = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString(3); ckEnableIdleLogoff.Checked = Settings.Enabled; + ckEnableIdleLogoff.Enabled = !Settings.IsSettingFromPolicy(nameof(Settings.Enabled)); - if (Settings.IsSettingFromPolicy("Enabled")) - ckEnableIdleLogoff.Enabled = false; - else - ckEnableIdleLogoff.Enabled = true; + ckIgnoreDisplayRequested.Checked = Settings.IgnoreDisplayRequested; + ckIgnoreDisplayRequested.Enabled = !Settings.IsSettingFromPolicy(nameof(Settings.IgnoreDisplayRequested)); try { @@ -38,14 +37,12 @@ private void RefreshUI() udMinutes.Value = 60; } - if (Settings.IsSettingFromPolicy("IdleLimit")) - udMinutes.Enabled = false; - else - udMinutes.Enabled = true; + udMinutes.Enabled = !Settings.IsSettingFromPolicy(nameof(Settings.IdleLimit)); - if ((!udMinutes.Enabled) | (!ckEnableIdleLogoff.Enabled)) + if (!udMinutes.Enabled | !ckEnableIdleLogoff.Enabled | !ckIgnoreDisplayRequested.Enabled) + { lbGPControlled.Visible = true; - + } } private void btOK_Click(object sender, EventArgs e) @@ -53,14 +50,23 @@ private void btOK_Click(object sender, EventArgs e) try { if (ckEnableIdleLogoff.Enabled) + { Settings.Enabled = ckEnableIdleLogoff.Checked; + } if (udMinutes.Enabled) - Settings.IdleLimit = (int)udMinutes.Value; + { + Settings.IdleLimit = (int) udMinutes.Value; + } + + if (ckIgnoreDisplayRequested.Enabled) + { + Settings.IgnoreDisplayRequested = ckIgnoreDisplayRequested.Checked; + } } catch (Exception ex) { - MessageBox.Show("An unexpected error occured when trying to save the settings.\n\n" + ex.Message); + MessageBox.Show("An unexpected error occurred when trying to save the settings.\n\n" + ex); } if (!EventLogging.IsEventSourceRegistered()) @@ -71,7 +77,7 @@ private void btOK_Click(object sender, EventArgs e) } catch (Exception ex) { - MessageBox.Show("An error occured registering the event source. Event log messages will not be logged for application events\n\n" + ex.Message); + MessageBox.Show("An error occurred registering the event source. Event log messages will not be logged for application events\n\n" + ex.Message); } } @@ -81,14 +87,14 @@ private void btOK_Click(object sender, EventArgs e) } catch (Exception ex) { - MessageBox.Show("An error occured while configuring the tool to automatically run at logon. The tool must be started with the '/start' switch for each user that logs on to take effect\n\n" + ex.Message); + MessageBox.Show("An error occurred while configuring the tool to automatically run at logon. The tool must be started with the '/start' switch for each user that logs on to take effect\n\n" + ex.Message); } Environment.Exit(0); } - private void lbHiddenRefresh_Click(object sender, EventArgs e) + private void lbHiddenRefresh_Click(object sender, EventArgs e) { RefreshUI(); } @@ -97,7 +103,5 @@ private void btCancel_Click(object sender, EventArgs e) { Environment.Exit(0); } - - } } diff --git a/src/Lithnet.IdleLogoff/frmSettings.resx b/src/Lithnet.IdleLogoff/frmSettings.resx index 7080a7d..1af7de1 100644 --- a/src/Lithnet.IdleLogoff/frmSettings.resx +++ b/src/Lithnet.IdleLogoff/frmSettings.resx @@ -112,9 +112,9 @@ 2.0 - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 \ No newline at end of file diff --git a/src/Lithnet.IdleLogoff/lithnet.idlelogoff.csproj b/src/Lithnet.IdleLogoff/lithnet.idlelogoff.csproj index 6132454..a9c6bba 100644 --- a/src/Lithnet.IdleLogoff/lithnet.idlelogoff.csproj +++ b/src/Lithnet.IdleLogoff/lithnet.idlelogoff.csproj @@ -99,6 +99,7 @@ + Form @@ -111,15 +112,18 @@ + frmSettings.cs PreserveNewest + Designer PreserveNewest + Designer @@ -140,12 +144,6 @@ true - - - - Designer - -