// Portable Tray-Variante von keepawake.ps1 - laeuft ohne Adminrechte, ohne PowerShell/Add-Type. // Build: Build-KeepAwake.ps1 (nutzt das in Windows enthaltene csc.exe von .NET Framework 4.x). using System; using System.Drawing; using System.Drawing.Drawing2D; using System.Reflection; using System.Runtime.InteropServices; using System.Threading; using System.Windows.Forms; [assembly: AssemblyTitle("KeepAwake")] [assembly: AssemblyProduct("KeepAwake")] [assembly: AssemblyVersion("1.0.0.0")] [assembly: AssemblyFileVersion("1.0.0.0")] namespace KeepAwake { internal static class Native { [StructLayout(LayoutKind.Sequential)] internal struct MOUSEINPUT { public int dx; public int dy; public uint mouseData; public uint dwFlags; public uint time; public IntPtr dwExtraInfo; } [StructLayout(LayoutKind.Sequential)] internal struct INPUT { public uint type; public MOUSEINPUT mi; } [DllImport("kernel32.dll", SetLastError = true)] internal static extern uint SetThreadExecutionState(uint esFlags); [DllImport("user32.dll", SetLastError = true)] private static extern uint SendInput(uint nInputs, INPUT[] pInputs, int cbSize); internal const uint ES_CONTINUOUS = 0x80000000; internal const uint ES_SYSTEM_REQUIRED = 0x00000001; internal const uint ES_DISPLAY_REQUIRED = 0x00000002; private const uint INPUT_MOUSE = 0; private const uint MOUSEEVENTF_MOVE = 0x0001; // Relativer Move 0/0: setzt den User-Idle-Timer zurueck, Cursor bleibt stehen. internal static void NudgeIdleTimer() { INPUT[] inputs = new INPUT[1]; inputs[0].type = INPUT_MOUSE; inputs[0].mi.dwFlags = MOUSEEVENTF_MOVE; SendInput(1, inputs, Marshal.SizeOf(typeof(INPUT))); } } internal sealed class Options { public int Minutes; public int IntervalSeconds = 60; public bool NoIdleReset; public static Options Parse(string[] args) { Options o = new Options(); for (int i = 0; i < args.Length; i++) { string name = args[i].TrimStart('-', '/').ToLowerInvariant(); string value = i + 1 < args.Length ? args[i + 1] : null; switch (name) { case "minutes": o.Minutes = Math.Max(0, int.Parse(value)); i++; break; case "interval": case "intervalseconds": o.IntervalSeconds = Math.Max(10, Math.Min(600, int.Parse(value))); i++; break; case "noidlereset": o.NoIdleReset = true; break; default: throw new ArgumentException("Unbekannter Parameter: " + args[i]); } } return o; } } internal sealed class TrayApp : ApplicationContext { private readonly Options _opt; private readonly DateTime _end; private readonly NotifyIcon _tray; private readonly ToolStripMenuItem _toggle; private readonly System.Windows.Forms.Timer _timer; private readonly Icon _iconOn = MakeIcon(Color.LimeGreen); private readonly Icon _iconOff = MakeIcon(Color.Gray); private bool _active; public TrayApp(Options opt) { _opt = opt; _end = opt.Minutes > 0 ? DateTime.Now.AddMinutes(opt.Minutes) : DateTime.MaxValue; _toggle = new ToolStripMenuItem("Aktiv", null, delegate { SetActive(!_active); }); ContextMenuStrip menu = new ContextMenuStrip(); menu.Items.Add(_toggle); menu.Items.Add(new ToolStripSeparator()); menu.Items.Add("Beenden", null, delegate { ExitThread(); }); _tray = new NotifyIcon(); _tray.ContextMenuStrip = menu; _tray.DoubleClick += delegate { SetActive(!_active); }; _tray.Visible = true; _timer = new System.Windows.Forms.Timer(); _timer.Interval = opt.IntervalSeconds * 1000; _timer.Tick += delegate { Tick(); }; SetActive(true); } private void SetActive(bool on) { _active = on; _toggle.Checked = on; if (on) { Tick(); _timer.Start(); } else { _timer.Stop(); Native.SetThreadExecutionState(Native.ES_CONTINUOUS); } _tray.Icon = on ? _iconOn : _iconOff; _tray.Text = !on ? "KeepAwake: pausiert" : _opt.Minutes > 0 ? "KeepAwake: aktiv bis " + _end.ToString("HH:mm") : "KeepAwake: aktiv"; } private void Tick() { if (DateTime.Now >= _end) { ExitThread(); return; } // Flags periodisch erneuern - ueberlebt Sitzungswechsel/Policy-Resets. Native.SetThreadExecutionState(Native.ES_CONTINUOUS | Native.ES_SYSTEM_REQUIRED | Native.ES_DISPLAY_REQUIRED); if (!_opt.NoIdleReset) { Native.NudgeIdleTimer(); } } protected override void ExitThreadCore() { _timer.Stop(); Native.SetThreadExecutionState(Native.ES_CONTINUOUS); _tray.Visible = false; _tray.Dispose(); base.ExitThreadCore(); } private static Icon MakeIcon(Color color) { using (Bitmap bmp = new Bitmap(16, 16)) using (Graphics g = Graphics.FromImage(bmp)) using (SolidBrush brush = new SolidBrush(color)) { g.SmoothingMode = SmoothingMode.AntiAlias; g.Clear(Color.Transparent); g.FillEllipse(brush, 1, 1, 14, 14); return Icon.FromHandle(bmp.GetHicon()); } } } internal static class Program { [STAThread] private static int Main(string[] args) { Options opt; try { opt = Options.Parse(args); } catch (Exception ex) { MessageBox.Show(ex.Message + "\n\nKeepAwake.exe [-minutes N] [-interval 10..600] [-noidlereset]", "KeepAwake", MessageBoxButtons.OK, MessageBoxIcon.Warning); return 1; } bool createdNew; using (Mutex mutex = new Mutex(false, @"Local\KeepAwake", out createdNew)) { if (!createdNew) { MessageBox.Show("KeepAwake laeuft bereits (Symbol im Infobereich).", "KeepAwake", MessageBoxButtons.OK, MessageBoxIcon.Information); return 0; } Application.EnableVisualStyles(); Application.Run(new TrayApp(opt)); } return 0; } } }