开发者

C#如何实现子进程跟随主进程关闭

开发者 https://www.devze.com 2024-08-12 13:55 出处:网络 作者: CodeOfCC
目录前言一、如何实现1、创建作业对象2、子进程加入作业对象3、销毁作业对象二、完整代码三、使用示例.net8.02、异常退出自动结束子进程总结前言
目录
  • 前言
  • 一、如何实现
    • 1、创建作业对象
    • 2、子进程加入作业对象
    • 3、销毁作业对象
  • 二、完整代码
    • 三、使用示例
      • .net8.0
      • 2、异常退出自动结束子进程
    • 总结

      前言

      多进程开发经常会遇到主进程关闭,子进程需要跟随主进程一同关闭。比如调ffmpeg命令行实现的录屏程序,录屏程序关闭,ffmpeg进程也需要退出。我们通常在程序关闭时调用Process.Kill()杀掉fmpeg进程即可。但是如果是强制或异常关闭录屏程序,ffmpeg将会变成僵尸进程残留在系统中。本文将提供一种解决此类问题的方法。

      一、如何实现

      1、创建作业对象

      (1)、创建对象

      handle = CreateJobObject(IntPtr.Zero, null);
      

      (2)、设置销毁作业时,关闭拥有的进程

      var info = new JOBOBJECT_BASIC_LIMIT_INFORMATION
      {
          LimitFlags = 0x2000
      };
      var extendedInfo = new JOBOBJECT_EXTENDED_LIMIT_INFORMATION
      {
        php  BasicLimitInformation = info
      };
      
      int length = Marshal.SizeOf(typeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION));
      IntPtr extendedInfoPtr = Marshal.AllocHGlobal(length);
      Marshal.StructureToPtr(extendedInfo, extendedInfoPtr, false);
      
      if (!SetInformationJoandroidbObject(handle, JobObjectInfoType.ExtendedLimitInformation, extendedInfoPtr, (uint)length))
          throw new Exception(string.Format("Unable to set information.  Error: {0}", Marshal.GetLastWin32Error()));
      

      2、子进程加入作业对象

      AssignProcessToJobObject(handle, processHandle);
      

      3、销毁作业对象

      (1)、手动销毁

      CloseHandle(handle);
      

      (2)、所在进程结束自动销毁

      二、完整代码

      using System.Diagnostics;
      using System.Runtime.InteropServices;
      namespace JobManagement
      {
          #region Helper classes
          /// <summary>
          ///  作业对象,主要用于子进程管理。
          ///  目前版本是只支持作业销毁拥有的子进程退出
          ///  通常可以定义一个全局静态变量使用
          /// </summary>
          public class Job : IDisposable
          {
              [DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
              static extern IntPtr CreateJobObject(IntPtr a, string lpName);
              [DllImport("kernel32.dll")]
              static extern bool SetInformationJobObject(IntPtr hJob, JobObjectInfoType infoType, IntPtr lpJobObjectInfo, UInt32 cbJobObjectInfoLength);
              [DllImport("kernel32.dll", SetLastError = true)]
              static extern bool AssignProcessToJobObject(IntPtr job, IntPtr process);
              [DllImport("kernel32.dll", SetLastError = true)]
              [return: MarshalAs(UnmanagedType.Bool)]
              static extern bool CloseHandle(IntPtr hObject);
              private IntPtr handle;
              private bool disposed;
              public Job()
              {
                  handle = CreateJobObject(IntPtr.Zero, null);
                  var info = new JOBOBJECT_BASIC_LIMIT_INFORMATION
                  {
                      LimitFlandroidags = 0x2000
                  };
                  var extendedInfo = new JOBOBJECT_EXTENDED_LIMIT_INFORMATION
                  {
                      BasicLimitInformation = info
                  };
                  int length = Marshal.SizeOf(typeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION));
                  IntPtr extendedInfoPtr = Marshal.AllocHGlobal(length);
                  Marshal.StructureTo编程客栈Ptr(extendedInfo, extendedInfoPtr, false);
                  if (!SetInformationJobObject(handle, JobObjectInfoType.ExtendedLimitInformation, extendedInfoPtr, (uint)length))
                      throw new Exception(string.Format("Unable to set information.  Error: {0}", Marshal.GetLastWin32Error()));
              }
              /// <summary>
              /// 进程加入到作业对象中
              /// </summary>
              /// <param name="processHandle">进程句柄</param>
              /// <returns></returns>
              public bool AddProcess(IntPtr processHandle)
              {
                  return AssignProcessToJobObject(handle, processHandle);
              }
      
              /// <summary>
              /// 进程加入到作业对象中
              /// </summary>
              /// <param name="processId">进程Id</param>
              /// <returns></returns>
              public bool AddProcess(int processId)
              {
                  return AddProcess(Process.GetProcessById(processId).Handle);
              }
              /// <summary>
              /// 销毁作业对象,手动调用则其拥有的所有进程都会退出
              /// </summary>
              public void Dispose()
              {
                  Dispose(true);
                  GC.SuppressFinalize(this);
              }
              /// <summary>
              /// 销毁作业对象,手动调用则其拥有的所有进程都会退出
              /// </summary>
              public void Close()
              {
                  CloseHandle(handle);
                  handle = IntPtr.Zero;
              }
              private void Dispose(bool disposing)
              {
                  if (disposed)
                      return;
                  if (disposing) { }
                  Close();
                  disposed = true;
              }
          }
      
          [StructLayout(LayoutKind.Sequential)]
          struct IO_COUNTERS
          {
              public UInt64 ReadOperationCount;
              public UInt64 WriteOperationCount;
              public UInt64 OtherOperationCount;
              public UInt64 ReadTransferCount;
              public UInt64 WriteTransferCount;
              public UInt64 OtherTransferCount;
          }
      
          [StructLayout(LayoutKind.Sequential)]
          struct JOBOBJECT_BASIC_LIMIT_INFORMATION
          {
              public Int64 PerProcessUserTimeLimit;
              public Int64 PerJobUserTimeLimit;
              public UInt32 LimitFlags;
              public UIntPtr MinimumWorkingSetSize;
              public UIntPtr MaximumWorkingSetSize;
              public UInt32 ActiveProcessLimit;
              public UIntPtr Affinity;
              public UInt32 PriorityClass;
              public UInt32 SchedulingClass;
          }
      
          [StructLayout(LayoutKind.Sequential)]
          public struct SECURITY_ATTRIBUTES
          {
              public UInt32 nLength;
              public IntPtr lpSecurityDescriptor;
              public Int32 bInheritHandle;
          }
      
          [StructLayout(LayoutKind.Sequential)]
          struct JOBOBJECT_EXTENDED_LIMIT_INFORMATION
        编程客栈  {
              public JOBOBJECT_BASIC_LIMIT_INFORMATION BasicLimitInformation;
              public IO_COUNTERS IoInfo;
              public UIntPtr ProcessMemoryLimit;
              public UIntPtr JobMemoryLimit;
              public UIntPtr PeakProcessMemoryUsed;
              public UIntPtr PeakJobMemoryUsed;
          }
      
          public enum JobObjectInfoType
          {
              AssociateCompletionPortInformation = 7,
              BasicLimitInformation = 2,
              BasicUIRestrictions = 4,
              EndOfJobTimeInformation = 6,
              ExtendedLimitInformation = 9,
              SecurityLimitInformation = 5,
              GroupInformation = 11
          }
          #endregion
      }
      

      三、使用示例

      1、正常退出自动结束子进程

      .net8.0

      using System.Diagnostics;
      using JobManagement;
      //创建作业对象
      Job _job = new Job();
      //打开记事本程序
      var ps = new Process();
      ps.StartInfo.FileName = "notepad.exe";
      ps.Start();
      //记事本程序进程加入到作业对象
      _job.AddProcess(ps.Handle);
      //等待3秒后退出程序,记事本程序会自动关闭
      Thread.Sleep(3000);
      

      效果预览

      C#如何实现子进程跟随主进程关闭

      2、异常退出自动结束子进程

      .net8.0

      using System.Diagnostics;
      using JobManagement;
      //创建作业对象
      Job _job = new Job();
      //打开记事本程序
      var ps = new Process();
      ps.StartInfo.FileName = "notepad.exe";
      ps.Start();
      //记事本程序进程加入到作业对象
      _job.AddProcess(ps.Handle);
      while(true)Thread.Sleep(3000);
      

      效果预览

      C#如何实现子进程跟随主进程关闭

      总结

      本文讲述的内容是Windows多进程开发中比较重要的技术,因为大部分场景主进程退出后子进程应该跟随退出,正常流程中通过代码可以在退出时关闭所有子进程,但是异常崩溃时则不行,会出现遗留子进程。而本文的方法就很好的解决的这个问题,而且也不需要编写任何关闭子进程的相关代码,方便且省心。

      以上就是C#如何实现子进程跟随主进程关闭的详细内容,更多关于C#子进程跟随主进程关闭的资料请关注编程客栈(www.devze.com)其它相关文章!

      0

      精彩评论

      暂无评论...
      验证码 换一张
      取 消

      关注公众号