private void ResumeThreads(IEnumerable <int> suspendedThreads)
        {
            foreach (int threadId in suspendedThreads)
            {
                using SafeWin32Handle threadHandle = WindowsProcessDataReader.OpenThread(ThreadAccess.SUSPEND_RESUME, false, (uint)threadId);
                if (threadHandle.IsInvalid || WindowsProcessDataReader.ResumeThread(threadHandle.DangerousGetHandle()) == -1)
                {
                    // If we fail to resume a thread we are in a bit of trouble because the target process is likely in a bad
                    // state.  This shouldn't ever happen, but if it does there's nothing we can do about it.  We'll log an event
                    // here but we won't throw an exception for a few reasons:
                    //     1.  We really never expect this to happen.  Why would we be able to suspend a thread but not resume it?
                    //     2.  We want to finish resuming threads.
                    //     3.  There's nothing the caller can really do about it.

                    Trace.WriteLine($"Failed to resume thread id:{threadId:id} in pid:{_pid:x}.");
                }
            }
        }
        private int[] SuspendThreads()
        {
            bool          permissionFailure = false;
            HashSet <int>?suspendedThreads  = new HashSet <int>();

            // A thread may create more threads while we are in the process of walking the list.  We will keep looping through
            // the thread list over and over until we find that we haven't found any new threads to suspend.
            try
            {
                int originalCount;
                do
                {
                    originalCount = suspendedThreads.Count;

                    Process process;
                    try
                    {
                        process = Process.GetProcessById(_pid);
                    }
                    catch (ArgumentException e)
                    {
                        throw new InvalidOperationException($"Unable to inspect process {_pid:x}.", e);
                    }

                    foreach (ProcessThread?thread in process.Threads)
                    {
                        if (thread != null)
                        {
                            if (suspendedThreads.Contains(thread.Id))
                            {
                                continue;
                            }

                            using SafeWin32Handle threadHandle = WindowsProcessDataReader.OpenThread(ThreadAccess.SUSPEND_RESUME, false, (uint)thread.Id);
                            if (threadHandle.IsInvalid || WindowsProcessDataReader.SuspendThread(threadHandle.DangerousGetHandle()) == -1)
                            {
                                permissionFailure = true;
                                continue;
                            }

                            suspendedThreads.Add(thread.Id);
                        }
                    }
                } while (originalCount != suspendedThreads.Count);

                // If we fail to suspend any thread then we didn't have permission.  We'll throw an exception in that case.  If
                // we fail to suspend a few of the threads we'll treat that as non-fatal.
                if (permissionFailure && suspendedThreads.Count == 0)
                {
                    throw new InvalidOperationException($"Unable to suspend threads of process {_pid:x}.");
                }

                int[] result = suspendedThreads.ToArray();
                suspendedThreads = null;
                return(result);
            }
            finally
            {
                if (suspendedThreads != null)
                {
                    ResumeThreads(suspendedThreads);
                }
            }
        }