public static int Main(string[] args)
    {
        if (args.Length != 1)
        {
            Console.Error.WriteLine("Usage: FindZombieProcess.exe <trace.etl>");
            return(1);
        }

        string tracePath = args[0];

        TraceProcessorSettings settings = new TraceProcessorSettings {
            AllowLostEvents = true
        };

        using (ITraceProcessor trace = TraceProcessor.Create(tracePath, settings))
        {
            IPendingResult <IHandleDataSource> pendingHandleData = trace.UseHandles();
            IPendingResult <ISymbolDataSource> pendingSymbolData = trace.UseSymbols();

            trace.Process();

            IHandleDataSource handleData = pendingHandleData.Result;
            ISymbolDataSource symbolData = pendingSymbolData.Result;

            symbolData.LoadSymbolsForConsoleAsync(SymCachePath.Automatic, SymbolPath.Automatic).GetAwaiter().GetResult();

            foreach (IProcessHandle processHandle in handleData.ProcessHandles)
            {
                // Zombie processes are processes which have exited but which still have a running process holding a handle to them
                if (processHandle.Process != null && !processHandle.CloseTime.HasValue &&
                    processHandle.Process.ExitTime.HasValue)
                {
                    string owningProcessName = processHandle.Owner?.ImageName ?? "Unknown";
                    string targetProcessName = processHandle.Process?.ImageName ?? "Unknown";
                    Console.WriteLine($"Owning process: {owningProcessName} has handle to: {targetProcessName}");
                }
            }

            return(0);
        }
    }
    public static int Main(string[] args)
    {
        if (args.Length != 1)
        {
            Console.Error.WriteLine("Usage: OutstandingHandleCountByProcess.exe <trace.etl>");
            return(1);
        }

        string tracePath = args[0];
        TraceProcessorSettings settings = new TraceProcessorSettings {
            AllowLostEvents = true
        };

        using (ITraceProcessor trace = TraceProcessor.Create(tracePath, settings))
        {
            IPendingResult <IHandleDataSource> pendingHandleData = trace.UseHandles();

            trace.Process();

            IHandleDataSource handleData = pendingHandleData.Result;
            // Dictionary Key is Owning Process Name
            // Dictionary Value is a struct containing outstanding handle counts by type (Process Handles & Other Handles)
            Dictionary <string, HandleCounts> outstandingHandleCounts = new Dictionary <string, HandleCounts>();

            foreach (IProcessHandle processHandle in handleData.ProcessHandles)
            {
                if (!processHandle.CloseTime.HasValue)
                {
                    string owningProcessName = processHandle.Owner?.ImageName ?? "Unknown";

                    if (outstandingHandleCounts.ContainsKey(owningProcessName))
                    {
                        HandleCounts handleCounts = outstandingHandleCounts[owningProcessName];
                        ++handleCounts.ProcessHandleCount;
                        outstandingHandleCounts[owningProcessName] = handleCounts;
                    }
                    else
                    {
                        outstandingHandleCounts[owningProcessName] = new HandleCounts(1, 0);
                    }
                }
            }

            foreach (IHandle otherHandle in handleData.OtherHandles)
            {
                if (!otherHandle.CloseTime.HasValue)
                {
                    string owningProcessName = otherHandle.Owner?.ImageName ?? "Unknown";

                    if (outstandingHandleCounts.ContainsKey(owningProcessName))
                    {
                        HandleCounts handleCounts = outstandingHandleCounts[owningProcessName];
                        ++handleCounts.OtherHandleCount;
                        outstandingHandleCounts[owningProcessName] = handleCounts;
                    }
                    else
                    {
                        outstandingHandleCounts[owningProcessName] = new HandleCounts(0, 1);
                    }
                }
            }

            foreach (string process in outstandingHandleCounts.Keys)
            {
                int openProcessHandleCount = outstandingHandleCounts[process].ProcessHandleCount;
                int openOtherHandleCount   = outstandingHandleCounts[process].OtherHandleCount;
                Console.WriteLine($"Owning process: {process}");
                Console.WriteLine($"\t{openProcessHandleCount} outstanding Process Handles" +
                                  $"\t{openOtherHandleCount} outstanding Other Handles");
            }

            return(0);
        }
    }