c#: Process hanging and recovery

Posted by tacojohn on Mon, 15 Jul 2019 21:45:28 +0200

1. Origin:

It's still the demand for modular programming. Product managers are hard to wait on, especially the female product managers, especially the elderly single product women! b

The reconstruction of VCU10 project requires each functional module to be implemented in an independent process, such as: audio-video conversion module, if implemented in an independent process, how to control its pause, continuation and other functions?

Threads can be Suspend, Resume, c# built-in Process does not have such a method, how neat?

There is no way out of the mountains and rivers, and there is another village with dark willows and bright flowers. When the feeling is strong, it turns thin, which can be remembered!

The former describes the method of data transfer between processes, and this paper also demonstrates the method of control and data interaction between processes with examples.

 

 

2. Undisclosed API functions: NtSuspendProcess, NtResumeProcess

Such functions cannot be found in MSDN.

The reason for this is that they are somewhere between Windows API and kernel API, and their power cannot be underestimated. Fear 28 rake programmers abuse and cause incidents, so hidden.

In fact, there is also a NtTerminateProcess, because Process has a Kill method, so it is not needed.

But what is hidden, as long as it has value, will be turned out, good wine is not afraid of the depth of the lane?

Well, based on it, a process management class is designed to realize the requirement of inter-process control in modular programming.

 

 

3,ProcessMgr

Go straight to the code and encapsulate a process management unit:

    public static class ProcessMgr
    {
        /// <summary>
        /// The process-specific access rights.
        /// </summary>
        [Flags]
        public enum ProcessAccess : uint
        {
            /// <summary>
            /// Required to terminate a process using TerminateProcess.
            /// </summary>
            Terminate = 0x1,

            /// <summary>
            /// Required to create a thread.
            /// </summary>
            CreateThread = 0x2,

            /// <summary>
            /// Undocumented.
            /// </summary>
            SetSessionId = 0x4,

            /// <summary>
            /// Required to perform an operation on the address space of a process (see VirtualProtectEx and WriteProcessMemory).
            /// </summary>
            VmOperation = 0x8,

            /// <summary>
            /// Required to read memory in a process using ReadProcessMemory.
            /// </summary>
            VmRead = 0x10,

            /// <summary>
            /// Required to write to memory in a process using WriteProcessMemory.
            /// </summary>
            VmWrite = 0x20,

            /// <summary>
            /// Required to duplicate a handle using DuplicateHandle.
            /// </summary>
            DupHandle = 0x40,

            /// <summary>
            /// Required to create a process.
            /// </summary>
            CreateProcess = 0x80,

            /// <summary>
            /// Required to set memory limits using SetProcessWorkingSetSize.
            /// </summary>
            SetQuota = 0x100,

            /// <summary>
            /// Required to set certain information about a process, such as its priority class (see SetPriorityClass).
            /// </summary>
            SetInformation = 0x200,

            /// <summary>
            /// Required to retrieve certain information about a process, such as its token, exit code, and priority class (see OpenProcessToken, GetExitCodeProcess, GetPriorityClass, and IsProcessInJob).
            /// </summary>
            QueryInformation = 0x400,

            /// <summary>
            /// Undocumented.
            /// </summary>
            SetPort = 0x800,

            /// <summary>
            /// Required to suspend or resume a process.
            /// </summary>
            SuspendResume = 0x800,

            /// <summary>
            /// Required to retrieve certain information about a process (see QueryFullProcessImageName). A handle that has the PROCESS_QUERY_INFORMATION access right is automatically granted PROCESS_QUERY_LIMITED_INFORMATION.
            /// </summary>
            QueryLimitedInformation = 0x1000,

            /// <summary>
            /// Required to wait for the process to terminate using the wait functions.
            /// </summary>
            Synchronize = 0x100000
        }

        [DllImport("ntdll.dll")]
        private static extern uint NtResumeProcess([In] IntPtr processHandle);

        [DllImport("ntdll.dll")]
        private static extern uint NtSuspendProcess([In] IntPtr processHandle);

        [DllImport("kernel32.dll", SetLastError = true)]
        private static extern IntPtr OpenProcess(
            ProcessAccess desiredAccess,
            bool inheritHandle,
            int processId);

        [DllImport("kernel32.dll", SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool CloseHandle([In] IntPtr handle);

        public static void SuspendProcess(int processId)
        {
            IntPtr hProc = IntPtr.Zero;
            try
            {
                // Gets the handle to the Process
                hProc = OpenProcess(ProcessAccess.SuspendResume, false, processId);
                if (hProc != IntPtr.Zero)
                    NtSuspendProcess(hProc);
            }
            finally
            {
                // Don't forget to close handle you created.
                if (hProc != IntPtr.Zero)
                    CloseHandle(hProc);
            }
        }

        public static void ResumeProcess(int processId)
        {
            IntPtr hProc = IntPtr.Zero;
            try
            {
                // Gets the handle to the Process
                hProc = OpenProcess(ProcessAccess.SuspendResume, false, processId);
                if (hProc != IntPtr.Zero)
                    NtResumeProcess(hProc);
            }
            finally
            {
                // Don't forget to close handle you created.
                if (hProc != IntPtr.Zero)
                    CloseHandle(hProc);
            }
        }
    }

 

 

4. Process Control

I have the right and the main process is the host. It calls the subprocess through the Process class and gets its ID for use. The calling code is:

        private void RunTestProcess(bool hidden = false)
        {
            string appPath = Path.GetDirectoryName(Application.ExecutablePath);
            string testAppPath = Path.Combine(appPath, "TestApp.exe");
            var pi = new ProcessStartInfo();
            pi.FileName = testAppPath;
            pi.Arguments = this.Handle.ToString();
            pi.WindowStyle = hidden ? ProcessWindowStyle.Hidden : ProcessWindowStyle.Normal;
            this.childProcess = Process.Start(pi);
            txtInfo.Text = string.Format("Subprocesses ID: {0}\r\n Subprocess name:{1}", childProcess.Id, childProcess.ProcessName);

            ...
        }

The control code is:

        private void btnWork_Click(object sender, EventArgs e)
        {
            if (this.childProcess == null || this.childProcess.HasExited)
                return;

            if ((int)btnWork.Tag == 0)
            {
                btnWork.Tag = 1;
                btnWork.Text = "recovery";
                ProcessMgr.SuspendProcess(this.childProcess.Id);
            }
            else
            {
                btnWork.Tag = 0;
                btnWork.Text = "Hang up";
                ProcessMgr.ResumeProcess(this.childProcess.Id);
            }
        }

The code is so small, simple...

 

 

5. Effect map:

For example, two graphs are made, one is to show the sub-process and the other is to hide the sub-process.

The actual project calls the independent process module, which is called in a hidden way to show the progress of its processing by the host.

 

 

Postscript:

Extended thinking, some excellent open source tools, such as youtube_dl, ffmpeg, exist in a process-independent manner, and can manage communication through CMD.

Based on this process control principle, we can make quite good GUI tools based on these open source tools. After all, compared to the powerful command line, people still use simple operation for convenience.

Topics: C# Programming Windows