From bfc483402d3b52284e09c2eebecf7f6497551cc0 Mon Sep 17 00:00:00 2001 From: wj32 Date: Sun, 26 Apr 2009 10:28:42 +0000 Subject: [PATCH] process tree now loads instantly (ugh, stupid lock statement doesn't even work properly) git-svn-id: svn://svn.code.sf.net/p/processhacker/code@1157 21ef857c-d57f-4fe0-8362-d861dc6d29cd --- trunk/CHANGELOG.txt | 6 +- trunk/ProcessHacker.Native/Api/Functions.cs | 4 + .../Memory/MemoryAlloc.cs | 4 + .../Components/ProcessTree/ProcessTree.cs | 2 + trunk/ProcessHacker/Forms/HackerWindow.cs | 23 +- trunk/ProcessHacker/Program/Logging.cs | 34 +- trunk/ProcessHacker/Program/Program.cs | 1 + .../Providers/Internal/Provider.cs | 75 ++-- .../Providers/ProcessSystemProvider.cs | 335 +++++++++++------- 9 files changed, 294 insertions(+), 190 deletions(-) diff --git a/trunk/CHANGELOG.txt b/trunk/CHANGELOG.txt index eb80835e2..978b91c89 100644 --- a/trunk/CHANGELOG.txt +++ b/trunk/CHANGELOG.txt @@ -5,8 +5,10 @@ Process Hacker * #2780260 - "add key to open Proc Properties" * #2780277 - "add to shortcut list for default action" * "Terminate Process Tree" - * Improved the perceived responsiveness of several GUI components - * Performance improvements in the tree view and other lists + * Improved the perceived responsiveness of Process Hacker in + various places + * Process tree loads instantly + * Actual performance improvements in the tree view and other lists * Thread termination now prompts * Integrity, I/O priority and page priority columns * Windows are protected from being offscreen when they load diff --git a/trunk/ProcessHacker.Native/Api/Functions.cs b/trunk/ProcessHacker.Native/Api/Functions.cs index e7581b301..34badc0fa 100644 --- a/trunk/ProcessHacker.Native/Api/Functions.cs +++ b/trunk/ProcessHacker.Native/Api/Functions.cs @@ -35,6 +35,7 @@ using ProcessHacker.Native.Security; namespace ProcessHacker.Native.Api { + [System.Security.SuppressUnmanagedCodeSecurity] public partial class Win32 { #region Cryptography @@ -773,6 +774,9 @@ namespace ProcessHacker.Native.Api uint cbSizeFileInfo, uint uFlags); + [DllImport("shell32.dll", EntryPoint = "#660")] + public static extern bool FileIconInit(bool RestoreCache); + #endregion #region Statistics diff --git a/trunk/ProcessHacker.Native/Memory/MemoryAlloc.cs b/trunk/ProcessHacker.Native/Memory/MemoryAlloc.cs index e030da101..bb4a3f17c 100644 --- a/trunk/ProcessHacker.Native/Memory/MemoryAlloc.cs +++ b/trunk/ProcessHacker.Native/Memory/MemoryAlloc.cs @@ -91,6 +91,7 @@ namespace ProcessHacker.Native { _memory = Marshal.AllocHGlobal(size); _size = size; + GC.AddMemoryPressure(size); } ~MemoryAlloc() @@ -186,8 +187,10 @@ namespace ProcessHacker.Native /// The new size of the allocation. public virtual void Resize(int newSize) { + GC.RemoveMemoryPressure(_size); _memory = Marshal.ReAllocHGlobal(_memory, new IntPtr(newSize)); _size = newSize; + GC.AddMemoryPressure(_size); } /// @@ -244,6 +247,7 @@ namespace ProcessHacker.Native protected virtual void Free() { Marshal.FreeHGlobal(this); + GC.RemoveMemoryPressure(_size); } private void Dispose(bool disposing) diff --git a/trunk/ProcessHacker/Components/ProcessTree/ProcessTree.cs b/trunk/ProcessHacker/Components/ProcessTree/ProcessTree.cs index 477031452..2e5f752a1 100644 --- a/trunk/ProcessHacker/Components/ProcessTree/ProcessTree.cs +++ b/trunk/ProcessHacker/Components/ProcessTree/ProcessTree.cs @@ -321,6 +321,8 @@ namespace ProcessHacker { node.BackColor = GetProcessColor(newItem); } + + _treeModel.Nodes[newItem.Pid].ProcessItem = newItem; } })); } diff --git a/trunk/ProcessHacker/Forms/HackerWindow.cs b/trunk/ProcessHacker/Forms/HackerWindow.cs index 65524d685..0c213a58f 100644 --- a/trunk/ProcessHacker/Forms/HackerWindow.cs +++ b/trunk/ProcessHacker/Forms/HackerWindow.cs @@ -1242,6 +1242,7 @@ namespace ProcessHacker private void selectAllProcessMenuItem_Click(object sender, EventArgs e) { Misc.SelectAll(treeProcesses.Tree.AllNodes); + treeProcesses.Tree.Invalidate(); } #endregion @@ -1259,12 +1260,20 @@ namespace ProcessHacker try { Process.GetCurrentProcess().PriorityClass = ProcessPriorityClass.High; } catch { } - Program.CollectGarbage(); + //Program.CollectGarbage(); if (processP.RunCount >= 1) - this.Invoke(new MethodInvoker(delegate + this.BeginInvoke(new MethodInvoker(delegate { treeProcesses.Tree.EndUpdate(); + treeProcesses.Tree.EndCompleteUpdate(); + treeProcesses.Tree.Invalidate(); + + // Catch any early file processing results + // NOTE: Do *not* put this statement outside this + // delegate, as that would cause a deadlock. + processP.RunOnceAsync(); + this.Cursor = Cursors.Default; this.UpdateCommon(); })); @@ -1350,7 +1359,7 @@ namespace ProcessHacker serviceP.Updated -= new ServiceProvider.ProviderUpdateOnce(serviceP_Updated); if (processP.RunCount >= 1) - this.Invoke(new MethodInvoker(UpdateCommon)); + this.BeginInvoke(new MethodInvoker(UpdateCommon)); } public void serviceP_DictionaryAdded(ServiceItem item) @@ -2367,6 +2376,7 @@ namespace ProcessHacker processP.Interval = Properties.Settings.Default.RefreshInterval; treeProcesses.Provider = processP; treeProcesses.Tree.BeginUpdate(); + treeProcesses.Tree.BeginCompleteUpdate(); this.Cursor = Cursors.WaitCursor; processP.RunOnceAsync(); processP.Updated += new ProcessSystemProvider.ProviderUpdateOnce(processP_Updated); @@ -2431,7 +2441,12 @@ namespace ProcessHacker treeProcesses.Tree.KeyDown += (sender, e) => { - if (e.Control && e.KeyCode == Keys.A) Misc.SelectAll(treeProcesses.TreeNodes); + if (e.Control && e.KeyCode == Keys.A) + { + Misc.SelectAll(treeProcesses.TreeNodes); + treeProcesses.Tree.Invalidate(); + } + if (e.Control && e.KeyCode == Keys.C) GenericViewMenu.TreeViewAdvCopy(treeProcesses.Tree, -1); }; listServices.List.KeyDown += diff --git a/trunk/ProcessHacker/Program/Logging.cs b/trunk/ProcessHacker/Program/Logging.cs index f1005a6f3..c7d2e22d4 100644 --- a/trunk/ProcessHacker/Program/Logging.cs +++ b/trunk/ProcessHacker/Program/Logging.cs @@ -30,32 +30,38 @@ namespace ProcessHacker { public static class Logging { - [DllImport("kernel32.dll", CharSet = CharSet.Unicode)] - private static extern void OutputDebugString(string OutputString); - public enum Importance : int { Information = 0, Warning, Error, Critical - } + } + + [DllImport("kernel32.dll", CharSet = CharSet.Unicode)] + private static extern void OutputDebugString(string OutputString); + + private static object _logLock = new object(); [Conditional("DEBUG")] public static void Log(Importance importance, string message) { - string debugMessage = - DateTime.Now.ToString("hh:mm:ss:fff:") + - " ProcessHacker: (" + importance.ToString() + ") " + message + "\n" + Environment.StackTrace; - - OutputDebugString(debugMessage); - - try + lock (_logLock) { - Program.HackerWindow.QueueMessage(debugMessage); + string debugMessage = + DateTime.Now.ToString("hh:mm:ss:fff:") + + " ProcessHacker (T" + System.Threading.Thread.CurrentThread.ManagedThreadId + + "): (" + importance.ToString() + ") " + message + "\n" + Environment.StackTrace; + + OutputDebugString(debugMessage); + + try + { + Program.HackerWindow.QueueMessage(debugMessage); + } + catch + { } } - catch - { } } [Conditional("DEBUG")] diff --git a/trunk/ProcessHacker/Program/Program.cs b/trunk/ProcessHacker/Program/Program.cs index af8cca764..24e4b3c75 100644 --- a/trunk/ProcessHacker/Program/Program.cs +++ b/trunk/ProcessHacker/Program/Program.cs @@ -270,6 +270,7 @@ namespace ProcessHacker ProcessProvider.Dictionary[pid].Name : null; + Win32.FileIconInit(true); new HackerWindow(); Application.Run(); } diff --git a/trunk/ProcessHacker/Providers/Internal/Provider.cs b/trunk/ProcessHacker/Providers/Internal/Provider.cs index 5b10be432..8ed7a63b0 100644 --- a/trunk/ProcessHacker/Providers/Internal/Provider.cs +++ b/trunk/ProcessHacker/Providers/Internal/Provider.cs @@ -117,6 +117,7 @@ namespace ProcessHacker private object _disposeLock = new object(); private object _busyLock = new object(); + private bool _disposing = false; private bool _disposed = false; private bool _busy = false; private bool _createThread = true; @@ -163,6 +164,8 @@ namespace ProcessHacker try { + _disposing = true; + if (disposing) { Monitor.Enter(_disposeLock); @@ -314,57 +317,55 @@ namespace ProcessHacker /// public void RunOnce() { - // Bail out if we are disposing - if (!Monitor.TryEnter(_disposeLock)) + lock (_busyLock) { - Logging.Log(Logging.Importance.Warning, "Provider (" + _name + "): RunOnce: currently disposing"); - return; - } - - try - { - lock (_busyLock) + // Bail out if we are disposing + if (_disposing) { - _busy = true; + Logging.Log(Logging.Importance.Warning, "Provider (" + _name + "): RunOnce: currently disposing"); + return; + } - if (ProviderUpdate != null) + _busy = true; + + if (ProviderUpdate != null) + { + try + { + if (BeforeUpdate != null) + BeforeUpdate(); + } + catch + { } + + try + { + ProviderUpdate(); + _runCount++; + } + catch (Exception ex) { try - { - if (BeforeUpdate != null) - BeforeUpdate(); - } - catch - { } - - try - { - ProviderUpdate(); - _runCount++; - } - catch (Exception ex) { if (Error != null) Error(ex); - - Logging.Log(ex); - } - - try - { - if (Updated != null) - Updated(); } catch { } + + Logging.Log(ex); } - _busy = false; + try + { + if (Updated != null) + Updated(); + } + catch + { } } - } - finally - { - Monitor.Exit(_disposeLock); + + _busy = false; } } diff --git a/trunk/ProcessHacker/Providers/ProcessSystemProvider.cs b/trunk/ProcessHacker/Providers/ProcessSystemProvider.cs index 73c5d1f93..2fc1005c0 100644 --- a/trunk/ProcessHacker/Providers/ProcessSystemProvider.cs +++ b/trunk/ProcessHacker/Providers/ProcessSystemProvider.cs @@ -30,6 +30,7 @@ using ProcessHacker.Native; using ProcessHacker.Native.Api; using ProcessHacker.Native.Objects; using ProcessHacker.Native.Security; +using System.Threading; namespace ProcessHacker { @@ -94,7 +95,12 @@ namespace ProcessHacker { public class FileProcessResult { - public int PID; + public int Stage; + public int Pid; + public string FileName; + public Icon Icon; + public FileVersionInfo VersionInfo; + public string CmdLine; public bool IsDotNet; public bool IsPacked; public VerifyResult VerifyResult; @@ -111,7 +117,7 @@ namespace ProcessHacker private HistoryManager _floatHistory = new HistoryManager(); private HistoryManager _mostUsageHistory = new HistoryManager(); - private delegate void ProcessFileDelegate(int pid, string fileName, bool useCache); + private delegate FileProcessResult ProcessFileDelegate(int pid, string fileName, bool useCache); public ProcessSystemProvider() : base() @@ -235,13 +241,102 @@ namespace ProcessHacker this.Performance = performance; } - private void ProcessFile(int pid, string fileName, bool forced) + private FileProcessResult ProcessFileStage1(int pid, string fileName, bool forced) + { + return ProcessFileStage1(pid, fileName, forced, true); + } + + private FileProcessResult ProcessFileStage1(int pid, string fileName, bool forced, bool addToQueue) { FileProcessResult fpResult = new FileProcessResult(); - fpResult.PID = pid; + fpResult.Pid = pid; + fpResult.Stage = 1; + + if (fileName == null) + fileName = this.GetFileName(pid); + + fpResult.FileName = fileName; + + if (fileName != null) + { + try + { + fpResult.Icon = FileUtils.GetFileIcon(fileName); + } + catch + { } + + try + { + fpResult.VersionInfo = FileVersionInfo.GetVersionInfo(fileName); + } + catch + { } + } + + try + { + using (var phandle = new ProcessHandle(pid, + Program.MinProcessQueryRights | Program.MinProcessReadMemoryRights)) + fpResult.CmdLine = phandle.GetCommandLine(); + } + catch + { } + + if (pid > 4) + { + try + { + var corpubPublishClass = new Debugger.Interop.CorPub.CorpubPublishClass(); + Debugger.Interop.CorPub.ICorPublishProcess process = null; + + try + { + int managed = 0; + + corpubPublishClass.GetProcess((uint)pid, out process); + process.IsManaged(out managed); + + if (managed > 0) + { + fpResult.IsDotNet = true; + } + } + finally + { + if (process != null) + { + Marshal.ReleaseComObject(process); + } + } + } + catch + { } + } + + if (addToQueue) + { + lock (_fpResults) + _fpResults.Enqueue(fpResult); + } + + (new ProcessFileDelegate(this.ProcessFileStage2)).BeginInvoke(pid, fileName, forced, r => { }, null); + + return fpResult; + } + + private FileProcessResult ProcessFileStage2(int pid, string fileName, bool forced) + { + FileProcessResult fpResult = new FileProcessResult(); + + fpResult.Pid = pid; + fpResult.Stage = 2; fpResult.IsPacked = false; + if (fileName == null) + return null; + // find out if it's packed // an image is packed if: // 1. it references less than 3 libraries @@ -302,29 +397,26 @@ namespace ProcessHacker { string uniName = (new System.IO.FileInfo(fileName)).FullName.ToLower(); - lock (_fileResults) + // No lock needed; verify results are never removed, only added. + if (!forced && _fileResults.ContainsKey(uniName)) { - if (!forced && _fileResults.ContainsKey(uniName)) + fpResult.VerifyResult = _fileResults[uniName]; + } + else + { + try { - fpResult.VerifyResult = _fileResults[uniName]; + fpResult.VerifyResult = Cryptography.VerifyFile(fileName); } - else + catch { - try - { - fpResult.VerifyResult = Cryptography.VerifyFile(fileName); - //fpResult.VerifyResult = NProcessHacker.PhvVerifyFile(fileName); - } - catch - { - fpResult.VerifyResult = VerifyResult.NoSignature; - } + fpResult.VerifyResult = VerifyResult.NoSignature; + } - if (!_fileResults.ContainsKey(uniName)) - _fileResults.Add(uniName, fpResult.VerifyResult); - else - _fileResults[uniName] = fpResult.VerifyResult; - } + if (!_fileResults.ContainsKey(uniName)) + _fileResults.Add(uniName, fpResult.VerifyResult); + else + _fileResults[uniName] = fpResult.VerifyResult; } } } @@ -332,48 +424,97 @@ namespace ProcessHacker catch { } - if (pid > 4) + lock (_fpResults) + _fpResults.Enqueue(fpResult); + + return fpResult; + } + + private string GetFileName(int pid) + { + string fileName = null; + + if (pid != 4) { try { - var corpubPublishClass = new Debugger.Interop.CorPub.CorpubPublishClass(); - Debugger.Interop.CorPub.ICorPublishProcess process = null; - - try + using (var phandle = new ProcessHandle(pid, ProcessAccess.QueryLimitedInformation)) { - int managed = 0; - - corpubPublishClass.GetProcess((uint)pid, out process); - process.IsManaged(out managed); - - if (managed > 0) + // first try to get the native file name, to prevent PEB + // file name spoofing. + try { - fpResult.IsPacked = false; - fpResult.IsDotNet = true; + fileName = FileUtils.DeviceFileNameToDos(phandle.GetNativeImageFileName()); } - } - finally - { - if (process != null) + catch + { } + + // if we couldn't get it or we couldn't resolve the \Device prefix, + // we'll just use the normal method (which only works on Vista). + if ((fileName == null || fileName.StartsWith("\\Device\\")) && + OSVersion.HasWin32ImageFileName) { - Marshal.ReleaseComObject(process); + try + { + fileName = phandle.GetImageFileName(); + } + catch + { } } } } catch { } + + // If all else failed, we get the main module file name. + if (fileName == null || fileName.StartsWith("\\Device\\")) + { + try + { + using (var phandle = + new ProcessHandle(pid, ProcessAccess.QueryInformation | ProcessAccess.VmRead)) + fileName = phandle.GetMainModule().FileName; + } + catch + { } + } + } + else + { + try + { + fileName = Misc.GetKernelFileName(); + } + catch + { } } - lock (_fpResults) - _fpResults.Enqueue(fpResult); - - Program.CollectGarbage(); + return fileName; } public void QueueFileProcessing(int pid) { - (new ProcessFileDelegate(this.ProcessFile)).BeginInvoke(pid, this.Dictionary[pid].FileName, true, - r => { }, null); + (new ProcessFileDelegate(this.ProcessFileStage1)).BeginInvoke( + pid, this.Dictionary[pid].FileName, true, r => { }, null); + } + + private void FillFpResult(ProcessItem item, FileProcessResult result) + { + if (result.Stage == 1) + { + item.FileName = result.FileName; + item.Icon = result.Icon; + item.VersionInfo = result.VersionInfo; + item.CmdLine = result.CmdLine; + item.IsDotNet = result.IsDotNet; + } + else if (result.Stage == 2) + { + item.IsPacked = result.IsDotNet ? false : result.IsPacked; + item.VerifyResult = result.VerifyResult; + item.ImportFunctions = result.ImportFunctions; + item.ImportModules = result.ImportModules; + } } private void UpdateOnce() @@ -400,7 +541,10 @@ namespace ProcessHacker long otherTime = _longDeltas[SystemStats.CpuOther]; if (sysKernelTime + sysUserTime + otherTime == 0) + { + Logging.Log(Logging.Importance.Warning, "Total systimes are 0, returning!"); return; + } _longDeltas.Update(SystemStats.IoRead, this.Performance.IoReadTransferCount); _longDeltas.Update(SystemStats.IoWrite, this.Performance.IoWriteTransferCount); @@ -512,15 +656,11 @@ namespace ProcessHacker // Dictionary may contain items newdictionary doesn't contain, // because we just removed terminated processes. However, // the look-for-modified-processes section relies on Dictionary! - if (Dictionary.ContainsKey(result.PID)) + if (Dictionary.ContainsKey(result.Pid)) { - ProcessItem item = this.Dictionary[result.PID]; + ProcessItem item = this.Dictionary[result.Pid]; - item.IsDotNet = result.IsDotNet; - item.IsPacked = result.IsPacked; - item.VerifyResult = result.VerifyResult; - item.ImportFunctions = result.ImportFunctions; - item.ImportModules = result.ImportModules; + this.FillFpResult(item, result); item.JustProcessed = true; } } @@ -533,15 +673,11 @@ namespace ProcessHacker if (!Dictionary.ContainsKey(pid)) { - Process p = null; ProcessItem item = new ProcessItem(); ProcessHandle queryLimitedHandle = null; if (pid >= 0) { - try { p = Process.GetProcessById(pid); } - catch { } - try { queryLimitedHandle = new ProcessHandle(pid, Program.MinProcessQueryRights); } catch { } } @@ -576,7 +712,9 @@ namespace ProcessHacker { try { - item.Name = p.MainModule.ModuleName; + using (var phandle = + new ProcessHandle(pid, ProcessAccess.QueryInformation | ProcessAccess.VmRead)) + item.Name = phandle.GetMainModule().BaseName; } catch { @@ -701,63 +839,6 @@ namespace ProcessHacker { } } - if (pid > 0) - { - if (pid != 4) - { - if (queryLimitedHandle != null) - { - // first try to get the native file name, to prevent PEB - // file name spoofing. - try - { - item.FileName = - FileUtils.DeviceFileNameToDos(queryLimitedHandle.GetNativeImageFileName()); - } - catch - { } - - // if we couldn't get it or we couldn't resolve the \Device prefix, - // we'll just use the normal method (which only works on Vista). - if ((item.FileName == null || item.FileName.StartsWith("\\Device\\")) && - OSVersion.HasWin32ImageFileName) - { - try - { - item.FileName = queryLimitedHandle.GetImageFileName(); - } - catch - { } - } - } - - // if all else failed, we go for the .NET method. - if (item.FileName == null || item.FileName.StartsWith("\\Device\\")) - { - try - { - item.FileName = Misc.GetRealPath(p.MainModule.FileName); - } - catch - { } - } - } - else - { - item.FileName = Misc.GetKernelFileName(); - } - - if (item.FileName != null) - { - try - { - item.Icon = (Icon)FileUtils.GetFileIcon(item.FileName); - } - catch - { } - } - } - if (pid == 0) { item.Name = "System Idle Process"; @@ -772,19 +853,16 @@ namespace ProcessHacker item.ParentPid = 0; item.HasParent = true; } - else + + // If this is not the first run, we process the file immediately. + if (this.RunCount > 0) { - try - { - item.VersionInfo = FileVersionInfo.GetVersionInfo(item.FileName); - } - catch - { } + this.FillFpResult(item, this.ProcessFileStage1(pid, null, false, false)); } if (pid > 0) { - (new ProcessFileDelegate(this.ProcessFile)).BeginInvoke(pid, item.FileName, false, + (new ProcessFileDelegate(this.ProcessFileStage1)).BeginInvoke(pid, item.FileName, false, r => { }, null); } @@ -812,15 +890,6 @@ namespace ProcessHacker { } } - try - { - using (var phandle = new ProcessHandle(pid, - Program.MinProcessQueryRights | Program.MinProcessReadMemoryRights)) - item.CmdLine = phandle.GetCommandLine(); - } - catch - { } - if (queryLimitedHandle != null) queryLimitedHandle.Dispose(); @@ -909,7 +978,7 @@ namespace ProcessHacker { if (item.IsPacked && item.ProcessingAttempts < 3) { - (new ProcessFileDelegate(this.ProcessFile)).BeginInvoke(pid, item.FileName, true, + (new ProcessFileDelegate(this.ProcessFileStage2)).BeginInvoke(pid, item.FileName, true, r => { }, null); item.ProcessingAttempts++; }