diff --git a/trunk/ProcessHacker.Native/Api/NativeDefinitions.cs b/trunk/ProcessHacker.Native/Api/NativeDefinitions.cs
index 3ff4797c1..2fc114b7b 100644
--- a/trunk/ProcessHacker.Native/Api/NativeDefinitions.cs
+++ b/trunk/ProcessHacker.Native/Api/NativeDefinitions.cs
@@ -42,6 +42,10 @@ namespace ProcessHacker.Native.Api
public const int MaximumWaitObjects = 64;
public const int MaxKeyNameLength = 512;
public const int MaxKeyValueNameLength = 32767;
+ public const int MaxStackDepth = 32;
+ public const int ProcessHandleTracingMaxStacks = 16;
+ public static readonly int ProcessHandleTracingQueryHandleTraceOffset =
+ Marshal.OffsetOf(typeof(ProcessHandleTracingQuery), "HandleTrace").ToInt32();
public const int SecurityDescriptorMinLength = 20;
public const int SecurityDescriptorRevision = 1;
public static readonly int SecurityMaxSidSize =
diff --git a/trunk/ProcessHacker.Native/Api/NativeFunctions.cs b/trunk/ProcessHacker.Native/Api/NativeFunctions.cs
index c00eaacb6..661e7f767 100644
--- a/trunk/ProcessHacker.Native/Api/NativeFunctions.cs
+++ b/trunk/ProcessHacker.Native/Api/NativeFunctions.cs
@@ -1266,6 +1266,14 @@ namespace ProcessHacker.Native.Api
[In] int ProcessInformationLength
);
+ [DllImport("ntdll.dll")]
+ public static extern NtStatus NtSetInformationProcess(
+ [In] IntPtr ProcessHandle,
+ [In] ProcessInformationClass ProcessInformationClass,
+ [In] ref ProcessHandleTracingEnable ProcessInformation,
+ [In] int ProcessInformationLength
+ );
+
[DllImport("ntdll.dll")]
public static extern NtStatus NtSetInformationThread(
[In] IntPtr ThreadHandle,
diff --git a/trunk/ProcessHacker.Native/Api/NativeStructs.cs b/trunk/ProcessHacker.Native/Api/NativeStructs.cs
index d4452d40e..d58f268ed 100644
--- a/trunk/ProcessHacker.Native/Api/NativeStructs.cs
+++ b/trunk/ProcessHacker.Native/Api/NativeStructs.cs
@@ -1075,6 +1075,39 @@ namespace ProcessHacker.Native.Api
public IntPtr InheritedFromUniqueProcessId;
}
+ [StructLayout(LayoutKind.Sequential)]
+ public struct ProcessHandleTracingEnable
+ {
+ public int Flags; // No flags. Set to 0.
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct ProcessHandleTracingEnableEx
+ {
+ public int Flags; // No flags. Set to 0.
+ public int TotalSlots;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct ProcessHandleTracingEntry
+ {
+ public IntPtr Handle;
+ public ClientId ClientId;
+ public int Type;
+
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst = Win32.ProcessHandleTracingMaxStacks)]
+ public IntPtr[] Stacks;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct ProcessHandleTracingQuery
+ {
+ public IntPtr Handle;
+ public int TotalTraces;
+ public char HandleTrace;
+ // An array of ProcessHandleTracingEntry structures follows.
+ }
+
[StructLayout(LayoutKind.Sequential)]
public struct QuotaLimits
{
@@ -1135,6 +1168,29 @@ namespace ProcessHacker.Native.Api
public IntPtr MaxReservedHandles;
}
+ [StructLayout(LayoutKind.Sequential)]
+ public struct RtlProcessBacktraceInformation
+ {
+ public IntPtr SymbolicBackTrace; // PCHAR, always NULL.
+ public int TraceCount;
+ public ushort Index;
+ public ushort Depth;
+
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst = Win32.MaxStackDepth)]
+ public IntPtr[] BackTrace;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct RtlProcessBacktraces
+ {
+ public int CommittedMemory;
+ public int ReservedMemory;
+ public int NumberOfBackTraceLookups;
+ public int NumberOfBackTraces;
+ public char BackTraces; // RtlProcessBacktraceInformation[] BackTraces
+ // Array of RtlProcessBacktraceInformation structures follows.
+ }
+
[StructLayout(LayoutKind.Sequential)]
public struct RtlUserProcessInformation
{
diff --git a/trunk/ProcessHacker.Native/Objects/ProcessHandle.cs b/trunk/ProcessHacker.Native/Objects/ProcessHandle.cs
index d9c479d85..7eefa98a8 100644
--- a/trunk/ProcessHacker.Native/Objects/ProcessHandle.cs
+++ b/trunk/ProcessHacker.Native/Objects/ProcessHandle.cs
@@ -430,6 +430,23 @@ namespace ProcessHacker.Native.Objects
Win32.ThrowLastError(status);
}
+ ///
+ /// Disables the collection of handle stack traces.
+ ///
+ public void DisableHandleTracing()
+ {
+ NtStatus status;
+
+ // Length 0 and NULL disables handle tracing.
+ if ((status = Win32.NtSetInformationProcess(
+ this,
+ ProcessInformationClass.ProcessHandleTracing,
+ IntPtr.Zero,
+ 0
+ )) >= NtStatus.Error)
+ Win32.ThrowLastError(status);
+ }
+
///
/// Removes as many pages as possible from the process' working set. This requires the
/// PROCESS_QUERY_INFORMATION and PROCESS_SET_INFORMATION permissions.
@@ -440,6 +457,24 @@ namespace ProcessHacker.Native.Objects
Win32.ThrowLastError();
}
+ ///
+ /// Enables the collection of handle stack traces. This requires
+ /// PROCESS_SET_INFORMATION access.
+ ///
+ public void EnableHandleTracing()
+ {
+ NtStatus status;
+ ProcessHandleTracingEnable phte = new ProcessHandleTracingEnable();
+
+ if ((status = Win32.NtSetInformationProcess(
+ this,
+ ProcessInformationClass.ProcessHandleTracing,
+ ref phte,
+ Marshal.SizeOf(phte)
+ )) >= NtStatus.Error)
+ Win32.ThrowLastError(status);
+ }
+
///
/// Enumerates the memory regions of the process.
///
@@ -929,6 +964,84 @@ namespace ProcessHacker.Native.Objects
}
}
+ ///
+ /// Gets a handle stack trace by its handle.
+ ///
+ /// A handle to the stack trace to retrieve.
+ /// A stack trace if the handle is valid, otherwise null.
+ public ProcessHandleTrace GetHandleTrace(IntPtr handle)
+ {
+ var collection = this.GetHandleTraces(handle);
+
+ // If the collection contains the stack trace, return it.
+ // Otherwise, return null.
+ if (collection.ContainsKey(handle))
+ return collection[handle];
+ else
+ return null;
+ }
+
+ ///
+ /// Gets a collection of handle stack traces. This requires
+ /// PROCESS_QUERY_INFORMATION access.
+ ///
+ /// A collection of handle stack traces.
+ public ProcessHandleTraceCollection GetHandleTraces()
+ {
+ return this.GetHandleTraces(IntPtr.Zero);
+ }
+
+ ///
+ /// Gets a collection of handle stack traces. This requires
+ /// PROCESS_QUERY_INFORMATION access.
+ ///
+ ///
+ /// A handle to the stack trace to retrieve. If this parameter is
+ /// zero, all stack traces will be retrieved.
+ ///
+ /// A collection of handle stack traces.
+ public ProcessHandleTraceCollection GetHandleTraces(IntPtr handle)
+ {
+ NtStatus status = NtStatus.Success;
+ int retLength;
+
+ using (var data = new MemoryAlloc(0x10000))
+ {
+ var query = new ProcessHandleTracingQuery();
+
+ // If Handle is not NULL, NtQueryInformationProcess will
+ // get a specific stack trace. Otherwise, it will get
+ // all of the stack traces.
+ query.Handle = handle;
+ data.WriteStruct(query);
+
+ for (int i = 0; i < 8; i++)
+ {
+ status = Win32.NtQueryInformationProcess(
+ this,
+ ProcessInformationClass.ProcessHandleTracing,
+ data,
+ data.Size,
+ out retLength
+ );
+
+ if (status == NtStatus.InfoLengthMismatch)
+ {
+ data.Resize(data.Size * 4);
+ continue;
+ }
+
+ if (status >= NtStatus.Error)
+ Win32.ThrowLastError(status);
+
+ return new ProcessHandleTraceCollection(data);
+ }
+
+ Win32.ThrowLastError(status);
+ return null; // Silences the compiler.
+ }
+ }
+
///
/// Gets the file name of the process' image. This requires the
/// PROCESS_QUERY_LIMITED_INFORMATION permission.
@@ -1856,6 +1969,92 @@ namespace ProcessHacker.Native.Objects
}
}
+ public class ProcessHandleTrace
+ {
+ private IntPtr _handle;
+ private ClientId _clientId;
+ private IntPtr[] _stack;
+
+ internal ProcessHandleTrace(ProcessHandleTracingEntry entry)
+ {
+ _handle = entry.Handle;
+ _clientId = entry.ClientId;
+
+ // Find the first occurrence of a NULL to find where the trace stops.
+ int zeroIndex = Array.IndexOf(entry.Stacks, IntPtr.Zero);
+
+ // If there was no NULL, copy the entire array.
+ if (zeroIndex == -1)
+ zeroIndex = entry.Stacks.Length;
+
+ // Copy the actual stack trace, excluding NULLs.
+ _stack = new IntPtr[zeroIndex];
+ Array.Copy(entry.Stacks, 0, _stack, 0, zeroIndex);
+ }
+
+ public ClientId ClientId
+ {
+ get { return _clientId; }
+ }
+
+ public IntPtr Handle
+ {
+ get { return _handle; }
+ }
+
+ public IntPtr[] Stack
+ {
+ get { return _stack; }
+ }
+ }
+
+ public class ProcessHandleTraceCollection
+ {
+ private IntPtr _handle;
+ private Dictionary _traces
+ = new Dictionary();
+
+ internal ProcessHandleTraceCollection(MemoryAlloc data)
+ {
+ if (data.Size < Marshal.SizeOf(typeof(ProcessHandleTracingQuery)))
+ throw new ArgumentException("Data memory allocation is too small.");
+
+ var query = data.ReadStruct();
+
+ _handle = query.Handle;
+
+ for (int i = 0; i < query.TotalTraces; i++)
+ {
+ var entry = data.ReadStruct(
+ Win32.ProcessHandleTracingQueryHandleTraceOffset,
+ i
+ );
+
+ _traces.Add(entry.Handle, new ProcessHandleTrace(entry));
+ }
+ }
+
+ public ProcessHandleTrace this[IntPtr handle]
+ {
+ get { return _traces[handle]; }
+ }
+
+ public IntPtr Handle
+ {
+ get { return _handle; }
+ }
+
+ public IEnumerable Traces
+ {
+ get { return _traces.Values; }
+ }
+
+ public bool ContainsKey(IntPtr handle)
+ {
+ return _traces.ContainsKey(handle);
+ }
+ }
+
public class ProcessModule
{
public ProcessModule(IntPtr baseAddress, int size, IntPtr entryPoint, string baseName, string fileName)
diff --git a/trunk/ProcessHacker/Providers/NetworkProvider.cs b/trunk/ProcessHacker/Providers/NetworkProvider.cs
index 8a1e791e8..3a2d8760a 100644
--- a/trunk/ProcessHacker/Providers/NetworkProvider.cs
+++ b/trunk/ProcessHacker/Providers/NetworkProvider.cs
@@ -115,9 +115,6 @@ namespace ProcessHacker
keyDict.Add(s + "-" + preKeyDict[s].Key.ToString(), connection);
}
- // Get resolve results.
- _messageQueue.Listen();
-
foreach (var connection in this.Dictionary.Values)
{
if (!keyDict.ContainsKey(connection.Id))
@@ -127,6 +124,9 @@ namespace ProcessHacker
}
}
+ // Get resolve results.
+ _messageQueue.Listen();
+
foreach (var connection in keyDict.Values)
{
if (!this.Dictionary.ContainsKey(connection.Id))