diff --git a/App.config b/App.config index 8e15646..3062bfd 100644 --- a/App.config +++ b/App.config @@ -1,6 +1,24 @@  + + +
+ + + + + + 3 + + + 00:00:02 + + + + + + \ No newline at end of file diff --git a/App.xaml.cs b/App.xaml.cs index 33cc881..e755a39 100644 --- a/App.xaml.cs +++ b/App.xaml.cs @@ -1,17 +1,23 @@ -using System; -using System.Collections.Generic; -using System.Configuration; -using System.Data; -using System.Linq; -using System.Threading.Tasks; -using System.Windows; +using System.Windows; namespace ProcessCpuUsageStatusWindow { - /// - /// Interaction logic for App.xaml - /// - public partial class App : Application + public partial class App { + private WindowSource _windowSource; + + protected override void OnStartup(StartupEventArgs e) + { + base.OnStartup(e); + + _windowSource = new WindowSource(); + } + + protected override void OnExit(ExitEventArgs e) + { + _windowSource.Dispose(); + + base.OnExit(e); + } } } diff --git a/ProcessCpuUsage.cs b/ProcessCpuUsage.cs new file mode 100644 index 0000000..ba7d664 --- /dev/null +++ b/ProcessCpuUsage.cs @@ -0,0 +1,60 @@ +using System; +using System.Diagnostics; + +namespace ProcessCpuUsageStatusWindow +{ + public class ProcessCpuUsage + { + #region Properties + + public string ProcessName { get; private set; } + public float PercentUsage { get; internal set; } + public DateTime LastFound { get; set; } + public bool UsageValid { get; private set; } + + internal CounterSample LastSample { get; set; } + + #endregion + + #region Constructor + + internal ProcessCpuUsage(InstanceData instanceData) + : this(instanceData, DateTime.MinValue) + { } + + internal ProcessCpuUsage(InstanceData instanceData, DateTime timestamp) + { + // Store the process details + ProcessName = instanceData.InstanceName; + + // Store the initial data + LastFound = timestamp; + LastSample = instanceData.Sample; + + // We start out as not valid + UsageValid = false; + } + + #endregion + + #region Usage update + + internal void UpdateCpuUsage(InstanceData instanceData, DateTime timestamp) + { + // Get the new sample + var newSample = instanceData.Sample; + + // Calculate percent usage + PercentUsage = CounterSample.Calculate(LastSample, newSample) / Environment.ProcessorCount; + + // Update the last sample and timestmap + LastSample = newSample; + LastFound = timestamp; + + // Usage is now valid + UsageValid = true; + } + + #endregion + } +} diff --git a/ProcessCpuUsageStatusWindow.csproj b/ProcessCpuUsageStatusWindow.csproj index 2e6de5d..56e11c8 100644 --- a/ProcessCpuUsageStatusWindow.csproj +++ b/ProcessCpuUsageStatusWindow.csproj @@ -34,13 +34,13 @@ 4 + + ..\FloatingStatusWindowLibrary\FloatingStatusWindowLibrary\bin\Debug\FloatingStatusWindowLibrary.dll + - + - - - 4.0 @@ -57,8 +57,11 @@ App.xaml Code + + + Code @@ -85,6 +88,9 @@ + + + + @@ -68,9 +69,10 @@ - + + @@ -85,9 +87,10 @@ - + + @@ -109,9 +112,25 @@ 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 + + + ..\Resources\ApplicationIcon.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + CPU: {0,4:f1}% - Total + + + + + + Loading... + + + CPU: {1,4:f1}% - {0} + \ No newline at end of file diff --git a/Properties/Settings.Designer.cs b/Properties/Settings.Designer.cs index 5eeb11c..6e0bde0 100644 --- a/Properties/Settings.Designer.cs +++ b/Properties/Settings.Designer.cs @@ -8,23 +8,55 @@ // //------------------------------------------------------------------------------ -namespace ProcessCpuUsageStatusWindow.Properties -{ - - +namespace ProcessCpuUsageStatusWindow.Properties { + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")] - internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase - { - - private static Settings defaultInstance = ((Settings) (global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); - - public static Settings Default - { - get - { + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "12.0.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default { + get { return defaultInstance; } } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("3")] + public int ProcessCount { + get { + return ((int)(this["ProcessCount"])); + } + set { + this["ProcessCount"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("00:00:02")] + public global::System.TimeSpan UpdateInterval { + get { + return ((global::System.TimeSpan)(this["UpdateInterval"])); + } + set { + this["UpdateInterval"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("")] + public string WindowSettings { + get { + return ((string)(this["WindowSettings"])); + } + set { + this["WindowSettings"] = value; + } + } } } diff --git a/Properties/Settings.settings b/Properties/Settings.settings index 033d7a5..af8bde6 100644 --- a/Properties/Settings.settings +++ b/Properties/Settings.settings @@ -1,7 +1,15 @@  - - - - - + + + + + 3 + + + 00:00:02 + + + + + \ No newline at end of file diff --git a/Resources/ApplicationIcon.ico b/Resources/ApplicationIcon.ico new file mode 100644 index 0000000..20d66eb Binary files /dev/null and b/Resources/ApplicationIcon.ico differ diff --git a/WindowSource.cs b/WindowSource.cs new file mode 100644 index 0000000..e2137dd --- /dev/null +++ b/WindowSource.cs @@ -0,0 +1,113 @@ +using FloatingStatusWindowLibrary; +using ProcessCpuUsageStatusWindow.Properties; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace ProcessCpuUsageStatusWindow +{ + public class WindowSource : IWindowSource, IDisposable + { + private readonly FloatingStatusWindow _floatingStatusWindow; + private readonly ProcessCpuUsageWatcher _processCpuUsageWatcher; + + internal WindowSource() + { + _floatingStatusWindow = new FloatingStatusWindow(this); + _floatingStatusWindow.SetText(Resources.Loading); + + _processCpuUsageWatcher = new ProcessCpuUsageWatcher(); + _processCpuUsageWatcher.Initialize(Settings.Default.UpdateInterval, UpdateDisplay); + } + + public void Dispose() + { + _processCpuUsageWatcher.Terminate(); + + _floatingStatusWindow.Save(); + _floatingStatusWindow.Dispose(); + } + + public string Name + { + get { return "Process CPU Usage"; } + } + + public System.Drawing.Icon Icon + { + get { return Resources.ApplicationIcon; } + } + + public string WindowSettings + { + get + { + return Settings.Default.WindowSettings; + } + set + { + Settings.Default.WindowSettings = value; + Settings.Default.Save(); + } + } + + #region Display updating + + private static class PredefinedProcessName + { + public const string Total = "_Total"; + public const string Idle = "Idle"; + } + + private void UpdateDisplay(Dictionary currentProcessList) + { + // Filter the process list to valid ones and exclude the idle and total values + var validProcessList = (currentProcessList.Values.Where( + process => + process.UsageValid && process.ProcessName != PredefinedProcessName.Total && + process.ProcessName != PredefinedProcessName.Idle)).ToList(); + + // Calculate the total usage by adding up all the processes we know about + var totalUsage = validProcessList.Sum(process => process.PercentUsage); + + // Sort the process list by usage and take only the top few + var sortedProcessList = (validProcessList.OrderByDescending(process => process.PercentUsage)).Take(Settings.Default.ProcessCount); + + // Create a new string builder + var stringBuilder = new StringBuilder(); + + // Add the header line (if any) + if (Resources.HeaderLine.Length > 0) + { + stringBuilder.AppendFormat(Resources.HeaderLine, totalUsage); + stringBuilder.AppendLine(); + stringBuilder.AppendLine(); + } + + // Loop over all processes in the sorted list + foreach (ProcessCpuUsage processCpuUsage in sortedProcessList) + { + // Move to the next line if it isn't the first line + if (stringBuilder.Length != 0) + stringBuilder.AppendLine(); + + // Format the process information into a string to display + stringBuilder.AppendFormat(Resources.ProcessLine, processCpuUsage.ProcessName, processCpuUsage.PercentUsage); + } + + // Add the footer line (if any) + if (Resources.FooterLine.Length > 0) + { + stringBuilder.AppendLine(); + stringBuilder.AppendLine(); + stringBuilder.AppendFormat(Resources.FooterLine, totalUsage); + } + + // Update the window with the text + _floatingStatusWindow.SetText(stringBuilder.ToString()); + } + + #endregion + } +}