tsunami
log in
email
password
links
newest items
tag list
syntax reference
tag:time
history
item name
tags
{{ using System; using System.IO; using System.Timers; using System.Collections.Generic; using System.Text.RegularExpressions; namespace IdleTime { static class Program { static void Usage(bool showAdvanced) { // 79 dashes: // ------------------------------------------------------------------------------ Console.WriteLine((@" Author: Luke Breuer Date: 01.07.08 Purpose: Periodically poll the system for idle time information, as well as information pertaining to the currently active window. Usage: IdleTime.exe pollingInterval in seconds [logFile] tee messages to this file [-q --quiet] no console output, aside from errors [-c --class] window class name [-e --exe-name] window exe name (no path) [-t --title] window title [-o --other-title] other window title (run with -? for details) Note: the order in which the window information methods are specified, either the -- version or in the switches value, controls the order the different fields are printed Examples: IdleTime.exe 60 c:\\idle.log IdleTime.exe 1 -ecto " + (showAdvanced ? "" : @" Note: Run IdleTime with the -? switch to get advanced help. ") + (!showAdvanced ? "" : @" Parsing: All fields should be delimited by two or more spaces. All instances of one or more whitespace characters in window fields get replaced with a single space. Exe names and classes should not contain anything other than a single space at a time of whitespace, but this is not guaranteed or checked for. Other-title: Multiple document interface (MDI) programs sometimes do not reflect the currently active document in the main window title. To counteract this, some window class paths have been hard-coded into this executable. For example, if --other-title has been specified and the foreground window is wndclass_desked_gsk, then the descendents will be searched according to the path set forth by MDIClient|EzMdiContainer|DockingView: first a child window of class MDIClient is searched for, ..., lastly, the title of the first window with class DockingView is returned. If a window is not found, an error message will print out (starting with '>> '), specifying the class which was not found.")).Replace("\t\t\t\t", "")); } delegate void Action<TArg1, TArg2>(TArg1 arg1, TArg2 arg2); delegate void Action(); static T FirstNonDefault<T>(T theDefault, params T[] list) { foreach (T v in list) if (!object.Equals(v, theDefault)) return v; return theDefault; } static TRet DefaultValueIfExceptionThrown<TRet, TEx>(Func<TRet> deferredExecute, Func<TEx, TRet> getDefaultValue) where TEx : Exception { try { return deferredExecute(); } catch (Exception ex) { if (ex is TEx) return getDefaultValue((TEx)ex); throw; } } const string TimeFormatString = "yy.MM.dd HH:mm:ss"; /// <summary> /// The main entry point for the application. /// </summary> static void Main(string[] args) { throw new Exception(); Func<string, int> argIndex = GetArgIndex(args); Predicate<string> argExists = arg => argIndex(arg) >= 0; bool helpAskedFor = Array.Exists(new[] { "-?", "/?", "--help" }, argExists); if (args.Length == 0 || helpAskedFor) { Usage(helpAskedFor); return; } int pollingInterval; if (!int.TryParse(args[0], out pollingInterval) || pollingInterval <= 0) { Console.Error.WriteLine("secondsBetweenOutput must be a positive integer"); Usage(false); return; } string logFile = args.Length >= 2 && !args[1].StartsWith("-") ? args[1] : null; if (logFile != null && !IsValidFileName(logFile)) return; bool quiet = argExists("--quiet"); Func<IntPtr, string> print = GetPrinter(argIndex); Func<string> getIdleString = () => GetIdleString(print); Action<string, bool> output = Output(logFile, quiet); if (!quiet && (logFile == null || logFile != null && !File.Exists(logFile))) output("Created by IdleTime.exe; see Luke Breuer for details.\n\n" + TimeFormatString + " {minutes idle} [{window information}]\n\n", false); output(getIdleString(), true); SetUpTimerAndWaitIndefinitely(pollingInterval, getIdleString, output); } private static Func<string, int> GetArgIndex(string[] args) { string flags = Array.Find(args, s => Regex.IsMatch(s, "^-[a-z]+$")) ?? ""; Func<string, int> argIndex = arg => FirstNonDefault( -1, Array.FindIndex(args, s => s.Equals(arg, StringComparison.OrdinalIgnoreCase)), arg.StartsWith("--") && arg.Length >= 3 ? flags.IndexOf(arg[2]) : -1); return argIndex; } private static Func<IntPtr, string> GetPrinter(Func<string, int> argIndex) { Dictionary<string, string[]> classPathMap = GetClassPathMap(); Func<string, string> escape = s => Regex.Replace(s ?? "", @"\s+", " "); Func<IntPtr, string> getOtherTitle = hWnd => { string className = Win32.GetClassName(hWnd); string[] classPath; return !classPathMap.TryGetValue(className, out classPath) ? null : DefaultValueIfExceptionThrown<string, WindowNotFoundException>( () => escape(Win32.GetTitle(Win32.FindWindowByClassPath(hWnd, classPath))), ex => ">> " + ex.Message); }; var prepareToPrint = Array.FindAll(Array.ConvertAll(new[] { new { Arg = "--class", GetValue = (Func<IntPtr, string>)Win32.GetClassName }, new { Arg = "--title", GetValue = (Func<IntPtr, string>)Win32.GetTitle }, new { Arg = "--exe-name", GetValue = (Func<IntPtr, string>)Win32.GetProcessFileNameFromWindow }, new { Arg = "--other-title", GetValue = getOtherTitle }, }, p => new { Index = argIndex(p.Arg), p.GetValue }), p => p.Index >= 0); Array.Sort(prepareToPrint, (a, b) => a.Index.CompareTo(b.Index)); return hWnd => string.Join(" ", Array.ConvertAll(prepareToPrint, p => escape(p.GetValue(hWnd)))); } private static string GetIdleString(Func<IntPtr, string> print) { try { IntPtr hWnd = Win32.GetForegroundWindow(); return hWnd == IntPtr.Zero ? "GetForegroundWindow returned 0" : print(hWnd); } catch (Exception ex) { Console.Error.WriteLine(ex.ToString()); return string.Format("exception of type {0} thrown; see stderr", ex.GetType().Name); } } private static Action<string, bool> Output(string logFile, bool quiet) { Action<string, bool> output = (message, printTime) => { message = message.Replace("\n", Environment.NewLine); if (printTime) message = string.Format("{0:" + TimeFormatString + "} {1,6:0.0}{2}", DateTime.Now, Win32.GetLastInputTime() / 60.0, message != null ? " " + message : ""); if (logFile != null) File.AppendAllText(logFile, message + Environment.NewLine); if (!quiet) Console.WriteLine(message); }; return output; } private static void SetUpTimerAndWaitIndefinitely(int pollingInterval, Func<string> getIdle, Action<string, bool> output) { Timer t = new Timer(1000 * pollingInterval); t.Elapsed += (object sender, ElapsedEventArgs e) => output(getIdle(), true); t.Start(); while (true) System.Threading.Thread.Sleep(100); } private static Dictionary<string, string[]> GetClassPathMap() { var classPathMap = new Dictionary<string, string[]>(); classPathMap.Add("wndclass_desked_gsk", new[] { "MDIClient", "EzMdiContainer", "DockingView" }); return classPathMap; } static bool IsValidFileName(string name) { try { FileInfo fi = new FileInfo(name); } catch (Exception ex) { Console.Error.WriteLine(ex.Message); return false; } return true; } } } }}
some permissive license goes here
contact