From 03130abd9c8a6536f7889795dc34dbd68e46f2da Mon Sep 17 00:00:00 2001 From: wj32 Date: Sat, 27 Dec 2008 06:52:14 +0000 Subject: [PATCH] Tray icon now displays the current CPU usage, and the tooltip is almost exactly the same as Process Explorer's (current CPU usage + process using most of the CPU) git-svn-id: svn://svn.code.sf.net/p/processhacker/code@433 21ef857c-d57f-4fe0-8362-d861dc6d29cd --- trunk/CHANGELOG.txt | 3 + .../Components/ProcessTree/ProcessNode.cs | 19 ++++- .../ProcessTree/ProcessTree.Designer.cs | 2 +- trunk/ProcessHacker/Forms/HackerWindow.cs | 71 ++++++++++++------- .../Forms/OptionsWindow.Designer.cs | 1 + trunk/ProcessHacker/Forms/OptionsWindow.cs | 13 ++++ trunk/ProcessHacker/Misc/UsageIcon.cs | 43 +++++++++-- .../Providers/ProcessSystemProvider.cs | 65 ++++++++++++++--- trunk/ProcessHacker/Win32/API/Functions.cs | 3 +- 9 files changed, 180 insertions(+), 40 deletions(-) diff --git a/trunk/CHANGELOG.txt b/trunk/CHANGELOG.txt index bbe248246..164be0ade 100644 --- a/trunk/CHANGELOG.txt +++ b/trunk/CHANGELOG.txt @@ -10,6 +10,9 @@ Process Hacker techniques * NEW: Highlighting for UAC elevated processes * NEW: Struct reader (with support for user-defined structs) + * NEW: Tray icon now displays the current CPU usage, and the tooltip is almost + exactly the same as Process Explorer's (current CPU usage + process using most + of the CPU) * FIXED: Shows SIDs without names in SDDL format * FIXED: Less "Access denied" errors - uses PROCESS_QUERY_LIMITED_INFORMATION on Vista diff --git a/trunk/ProcessHacker/Components/ProcessTree/ProcessNode.cs b/trunk/ProcessHacker/Components/ProcessTree/ProcessNode.cs index 5d3c57bec..51885c404 100644 --- a/trunk/ProcessHacker/Components/ProcessTree/ProcessNode.cs +++ b/trunk/ProcessHacker/Components/ProcessTree/ProcessNode.cs @@ -71,6 +71,17 @@ namespace ProcessHacker get { return _pitem.Name; } } + public string DisplayPID + { + get + { + if (_pitem.PID >= 0) + return _pitem.PID.ToString(); + else + return ""; + } + } + public int PID { get { return _pitem.PID; } @@ -88,7 +99,13 @@ namespace ProcessHacker public string CPU { - get { return _pitem.CPUUsage.ToString("F2"); } + get + { + if (_pitem.CPUUsage == 0) + return ""; + else + return _pitem.CPUUsage.ToString("F2"); + } } private string GetBestUsername(string username, bool includeDomain) diff --git a/trunk/ProcessHacker/Components/ProcessTree/ProcessTree.Designer.cs b/trunk/ProcessHacker/Components/ProcessTree/ProcessTree.Designer.cs index 74efba1f0..62cce610f 100644 --- a/trunk/ProcessHacker/Components/ProcessTree/ProcessTree.Designer.cs +++ b/trunk/ProcessHacker/Components/ProcessTree/ProcessTree.Designer.cs @@ -135,7 +135,7 @@ // // nodePID // - this.nodePID.DataPropertyName = "PID"; + this.nodePID.DataPropertyName = "DisplayPID"; this.nodePID.EditEnabled = false; this.nodePID.IncrementalSearchEnabled = true; this.nodePID.LeftMargin = 3; diff --git a/trunk/ProcessHacker/Forms/HackerWindow.cs b/trunk/ProcessHacker/Forms/HackerWindow.cs index eef6209a2..9044a1db8 100644 --- a/trunk/ProcessHacker/Forms/HackerWindow.cs +++ b/trunk/ProcessHacker/Forms/HackerWindow.cs @@ -48,6 +48,8 @@ namespace ProcessHacker ProcessSystemProvider processP = new ProcessSystemProvider(); ServiceProvider serviceP = new ServiceProvider(); + UsageIcon cpuUsageIcon = new UsageIcon(16, 16); + Dictionary> processServices = new Dictionary>(); int processSelectedItems; @@ -756,6 +758,22 @@ namespace ProcessHacker #region Providers + private void processP_Updated() + { + processP.DictionaryAdded += new ProcessSystemProvider.ProviderDictionaryAdded(processP_DictionaryAdded); + processP.DictionaryRemoved += new ProcessSystemProvider.ProviderDictionaryRemoved(processP_DictionaryRemoved); + processP.Updated -= new ProcessSystemProvider.ProviderUpdateOnce(processP_Updated); + + if (processP.RunCount >= 1) + this.Invoke(new MethodInvoker(UpdateCommon)); + } + + private void processP_IconUpdater() + { + cpuUsageIcon.Update(processP.CurrentCPUUsage); + notifyIcon.Icon = cpuUsageIcon.GetIcon(); + } + public void processP_DictionaryAdded(ProcessItem item) { ProcessItem parent = new ProcessItem(); @@ -794,6 +812,20 @@ namespace ProcessHacker "The process " + item.Name + " (" + item.PID.ToString() + ") was terminated.", ToolTipIcon.Info); } + private void serviceP_Updated() + { + listServices.List.EndUpdate(); + HighlightedListViewItem.StateHighlighting = true; + + serviceP.DictionaryAdded += new ServiceProvider.ProviderDictionaryAdded(serviceP_DictionaryAdded); + serviceP.DictionaryModified += new ServiceProvider.ProviderDictionaryModified(serviceP_DictionaryModified); + serviceP.DictionaryRemoved += new ServiceProvider.ProviderDictionaryRemoved(serviceP_DictionaryRemoved); + serviceP.Updated -= new ServiceProvider.ProviderUpdateOnce(serviceP_Updated); + + if (processP.RunCount >= 1) + this.Invoke(new MethodInvoker(UpdateCommon)); + } + public void serviceP_DictionaryAdded(ServiceItem item) { this.QueueMessage("New Service: " + item.Status.ServiceName + @@ -1151,6 +1183,17 @@ namespace ProcessHacker private void timerFire_Tick(object sender, EventArgs e) { UpdateStatusInfo(); + + notifyIcon.Text = "Process Hacker\n" + + "CPU Usage: " + (processP.CurrentCPUUsage * 100).ToString("F2") + "%"; + + try + { + notifyIcon.Text += "\n" + processP.Dictionary[processP.PIDWithMostCPUUsage].Name + + ": " + processP.Dictionary[processP.PIDWithMostCPUUsage].CPUUsage.ToString("F2") + "%"; + } + catch + { } } private void timerMessages_Tick(object sender, EventArgs e) @@ -1383,30 +1426,6 @@ namespace ProcessHacker ((MenuItem)sender).Checked = !((MenuItem)sender).Checked; } - private void serviceP_Updated() - { - listServices.List.EndUpdate(); - HighlightedListViewItem.StateHighlighting = true; - - serviceP.DictionaryAdded += new ServiceProvider.ProviderDictionaryAdded(serviceP_DictionaryAdded); - serviceP.DictionaryModified += new ServiceProvider.ProviderDictionaryModified(serviceP_DictionaryModified); - serviceP.DictionaryRemoved += new ServiceProvider.ProviderDictionaryRemoved(serviceP_DictionaryRemoved); - serviceP.Updated -= new ServiceProvider.ProviderUpdateOnce(serviceP_Updated); - - if (processP.RunCount >= 1) - this.Invoke(new MethodInvoker(UpdateCommon)); - } - - private void processP_Updated() - { - processP.DictionaryAdded += new ProcessSystemProvider.ProviderDictionaryAdded(processP_DictionaryAdded); - processP.DictionaryRemoved += new ProcessSystemProvider.ProviderDictionaryRemoved(processP_DictionaryRemoved); - processP.Updated -= new ProcessSystemProvider.ProviderUpdateOnce(processP_Updated); - - if (processP.RunCount >= 1) - this.Invoke(new MethodInvoker(UpdateCommon)); - } - private void UpdateCommon() { timerMessages.Enabled = true; @@ -1441,8 +1460,12 @@ namespace ProcessHacker processP.Interval = RefreshInterval; treeProcesses.Provider = processP; processP.Updated += new ProcessSystemProvider.ProviderUpdateOnce(processP_Updated); + processP.Updated += new Provider.ProviderUpdateOnce(processP_IconUpdater); processP.Enabled = true; + cpuUsageIcon.BackColor = Color.Black; + cpuUsageIcon.Color = Color.Red; + HighlightedListViewItem.HighlightingDuration = Properties.Settings.Default.HighlightingDuration; HighlightedListViewItem.StateHighlighting = false; listServices.List.BeginUpdate(); diff --git a/trunk/ProcessHacker/Forms/OptionsWindow.Designer.cs b/trunk/ProcessHacker/Forms/OptionsWindow.Designer.cs index e0553efde..66c682588 100644 --- a/trunk/ProcessHacker/Forms/OptionsWindow.Designer.cs +++ b/trunk/ProcessHacker/Forms/OptionsWindow.Designer.cs @@ -194,6 +194,7 @@ this.checkShowTrayIcon.TabIndex = 7; this.checkShowTrayIcon.Text = "Show tray icon"; this.checkShowTrayIcon.UseVisualStyleBackColor = true; + this.checkShowTrayIcon.CheckedChanged += new System.EventHandler(this.checkShowTrayIcon_CheckedChanged); // // tabHighlighting // diff --git a/trunk/ProcessHacker/Forms/OptionsWindow.cs b/trunk/ProcessHacker/Forms/OptionsWindow.cs index 50d1ad0fb..61bef64d6 100644 --- a/trunk/ProcessHacker/Forms/OptionsWindow.cs +++ b/trunk/ProcessHacker/Forms/OptionsWindow.cs @@ -98,5 +98,18 @@ namespace ProcessHacker Program.HackerWindow.ProcessList.RefreshItems(); this.Close(); } + + private void checkShowTrayIcon_CheckedChanged(object sender, EventArgs e) + { + if (checkShowTrayIcon.Checked) + { + checkHideWhenMinimized.Enabled = true; + } + else + { + checkHideWhenMinimized.Enabled = false; + checkHideWhenMinimized.Checked = false; + } + } } } diff --git a/trunk/ProcessHacker/Misc/UsageIcon.cs b/trunk/ProcessHacker/Misc/UsageIcon.cs index 89fc078b9..d17aa29ff 100644 --- a/trunk/ProcessHacker/Misc/UsageIcon.cs +++ b/trunk/ProcessHacker/Misc/UsageIcon.cs @@ -19,7 +19,8 @@ using System; using System.Diagnostics; -using System.Drawing; +using System.Drawing; +using System.Drawing.Drawing2D; using System.IO; using System.Reflection; using System.Text; @@ -32,17 +33,51 @@ namespace ProcessHacker public class UsageIcon { private List _values; + private int _width; + private int _height; - public UsageIcon(int size) + public UsageIcon(int width, int height) { - _values = new List(size); + _values = new List(); + _width = width; + _height = height; + + for (int i = 0; i < width; i++) + _values.Add(0); } public void Update(float value) { - + if (value > 1) + throw new ArgumentOutOfRangeException(); + + // shift values left, push value onto the end + _values.RemoveAt(0); + _values.Add(value); } + public Color BackColor { get; set; } + public Color Color { get; set; } + + public Icon GetIcon() + { + using (Bitmap bm = new Bitmap(_width, _height)) + { + using (Graphics g = Graphics.FromImage(bm)) + { + g.FillRectangle(new SolidBrush(this.BackColor), new Rectangle(0, 0, _width, _height)); + + for (int x = 0; x < _width; x++) + { + int height = (int)(_values[x] * _height); + + g.DrawLine(new Pen(this.Color), new Point(x, _height), new Point(x, _height - height)); + } + + return Icon.FromHandle(bm.GetHicon()); + } + } + } } } diff --git a/trunk/ProcessHacker/Providers/ProcessSystemProvider.cs b/trunk/ProcessHacker/Providers/ProcessSystemProvider.cs index baf067dab..b81bd97a9 100644 --- a/trunk/ProcessHacker/Providers/ProcessSystemProvider.cs +++ b/trunk/ProcessHacker/Providers/ProcessSystemProvider.cs @@ -56,6 +56,7 @@ namespace ProcessHacker public class ProcessSystemProvider : Provider { + private long _lastIdleTime; private long _lastSysTime; public ProcessSystemProvider() @@ -69,23 +70,53 @@ namespace ProcessHacker Win32.ZwQuerySystemInformation(Win32.SYSTEM_INFORMATION_CLASS.SystemBasicInformation, ref basic, Marshal.SizeOf(basic), out retLen); this.System = basic; + this.ProcessorPerfArray = new Win32.SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION[this.System.NumberOfProcessors]; this.UpdateProcessorPerf(); _lastSysTime = this.ProcessorPerf.KernelTime + this.ProcessorPerf.UserTime; + _lastIdleTime = this.ProcessorPerf.IdleTime; } public Win32.SYSTEM_BASIC_INFORMATION System { get; private set; } public Win32.SYSTEM_PERFORMANCE_INFORMATION Performance { get; private set; } public Win32.SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION ProcessorPerf { get; private set; } + public Win32.SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION[] ProcessorPerfArray { get; private set; } + public float CurrentCPUUsage { get; private set; } + public int PIDWithMostCPUUsage { get; private set; } private void UpdateProcessorPerf() { - int retLen; - Win32.SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION procPerf = new Win32.SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION(); + using (MemoryAlloc data = + new MemoryAlloc(Marshal.SizeOf(typeof(Win32.SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION)) * + this.ProcessorPerfArray.Length)) + { + int retLen; - Win32.ZwQuerySystemInformation(Win32.SYSTEM_INFORMATION_CLASS.SystemProcessorTimes, - ref procPerf, Marshal.SizeOf(procPerf), out retLen); - this.ProcessorPerf = procPerf; + Win32.ZwQuerySystemInformation(Win32.SYSTEM_INFORMATION_CLASS.SystemProcessorTimes, + data, data.Size, out retLen); + + var newAverages = new Win32.SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION(); + + for (int i = 0; i < this.ProcessorPerfArray.Length; i++) + { + this.ProcessorPerfArray[i] = data.ReadStruct( + i); + newAverages.DpcTime += this.ProcessorPerfArray[i].DpcTime + / this.ProcessorPerfArray.Length; + newAverages.IdleTime += this.ProcessorPerfArray[i].IdleTime + / this.ProcessorPerfArray.Length; + newAverages.InterruptCount += this.ProcessorPerfArray[i].InterruptCount + / this.ProcessorPerfArray.Length; + newAverages.InterruptTime += this.ProcessorPerfArray[i].InterruptTime + / this.ProcessorPerfArray.Length; + newAverages.KernelTime += this.ProcessorPerfArray[i].KernelTime + / this.ProcessorPerfArray.Length; + newAverages.UserTime += this.ProcessorPerfArray[i].UserTime + / this.ProcessorPerfArray.Length; + } + + this.ProcessorPerf = newAverages; + } } private void UpdatePerformance() @@ -113,12 +144,17 @@ namespace ProcessHacker _lastSysTime = thisSysTime; + long thisIdleTime = this.ProcessorPerf.IdleTime; + long idleTime = thisIdleTime - _lastIdleTime; + + _lastIdleTime = thisIdleTime; + // set System Idle Process CPU time if (procs.ContainsKey(0)) { Win32.SystemProcess proc = procs[0]; - proc.Process.KernelTime = this.ProcessorPerf.IdleTime * 4; // must not be divided by 4 + proc.Process.KernelTime = this.ProcessorPerf.IdleTime * this.System.NumberOfProcessors; procs.Remove(0); procs.Add(0, proc); } @@ -130,7 +166,7 @@ namespace ProcessHacker { ProcessId = -2, InheritedFromProcessId = 0, - KernelTime = this.ProcessorPerf.DpcTime * 4, + KernelTime = this.ProcessorPerf.DpcTime * this.System.NumberOfProcessors, SessionId = -1 } }); @@ -142,11 +178,13 @@ namespace ProcessHacker { ProcessId = -3, InheritedFromProcessId = 0, - KernelTime = this.ProcessorPerf.InterruptTime * 4, + KernelTime = this.ProcessorPerf.InterruptTime * this.System.NumberOfProcessors, SessionId = -1 } }); + float mostCPUUsage = 0; + // look for dead processes foreach (int pid in Dictionary.Keys) { @@ -306,8 +344,14 @@ namespace ProcessHacker try { - newitem.CPUUsage = ((float)(newitem.LastTime - item.LastTime) * 100 / sysTime) / + newitem.CPUUsage = (float)(newitem.LastTime - item.LastTime) * 100 / sysTime / this.System.NumberOfProcessors; + + if (pid != 0 && newitem.CPUUsage > mostCPUUsage) + { + mostCPUUsage = newitem.CPUUsage; + this.PIDWithMostCPUUsage = pid; + } } catch { } @@ -349,6 +393,9 @@ namespace ProcessHacker } } + if (thisSysTime != 0) + this.CurrentCPUUsage = (float)(sysTime - idleTime) / (sysTime); + Dictionary = newdictionary; wtsEnumData.Memory.Dispose(); diff --git a/trunk/ProcessHacker/Win32/API/Functions.cs b/trunk/ProcessHacker/Win32/API/Functions.cs index 68dadce8f..b11aac6bf 100644 --- a/trunk/ProcessHacker/Win32/API/Functions.cs +++ b/trunk/ProcessHacker/Win32/API/Functions.cs @@ -610,7 +610,8 @@ namespace ProcessHacker [DllImport("ntdll.dll", SetLastError = true)] public static extern uint ZwQuerySystemInformation(SYSTEM_INFORMATION_CLASS SystemInformationClass, - ref SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION SystemInformation, int SystemInformationLength, out int ReturnLength); + [MarshalAs(UnmanagedType.LPArray)] SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION[] SystemInformation, + int SystemInformationLength, out int ReturnLength); [DllImport("ntdll.dll", SetLastError = true)] public static extern uint ZwQuerySystemInformation(SYSTEM_INFORMATION_CLASS SystemInformationClass,