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

文章目录

  • 前言
  • 一、如何实现?
    • 1、创建作业对象
      • (1)、创建对象
      • (2)、设置销毁作业时,关闭拥有的进程
    • 2、子进程加入作业对象
    • 3、销毁作业对象
      • (1)、手动销毁
      • (2)、所在进程结束自动销毁
  • 二、完整代码
  • 三、使用示例
    • 1、正常退出自动结束子进程
    • 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
{BasicLimitInformation = info
};int length = Marshal.SizeOf(typeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION));
IntPtr extendedInfoPtr = Marshal.AllocHGlobal(length);
Marshal.StructureToPtr(extendedInfo, extendedInfoPtr, false);if (!SetInformationJobObject(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{LimitFlags = 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.StructureToPtr(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);

效果预览
在这里插入图片描述

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);

效果预览
在这里插入图片描述


总结

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/791203.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

从零起步:开启你的IT职业之旅

简介&#xff1a; 信息技术&#xff08;IT&#xff09;行业以其快速发展和广阔的就业前景吸引着全球众多职场新人。但对于零基础的求职者而言&#xff0c;挺进这一行业似乎是条充满挑战的道路。进入IT行业可能看起来是一项艰巨的挑战&#xff0c;尤其是对于那些没有任何相关经…

面试算法-134-最长递增子序列

题目 给你一个整数数组 nums &#xff0c;找到其中最长严格递增子序列的长度。 子序列 是由数组派生而来的序列&#xff0c;删除&#xff08;或不删除&#xff09;数组中的元素而不改变其余元素的顺序。例如&#xff0c;[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的 子序列 。 示例…

redis乱码\xac\xed\x00\x05t\x00H解决

发现数据库乱码&#xff1a; 这数据库是来自rdids队列list实现的一个简单队列&#xff0c;停止使用该list的服务&#xff0c;查看里面的值&#xff0c;发现 乱码\xac\xed\x00\x05t\x00H&#xff0c;如下图&#xff1a; 很明发送数据端的问题&#xff0c;检查代码&#xff1a; …

20240403在ubuntu20.04下解压缩gz压缩包

20240403在ubuntu20.04下解压缩gz压缩包.txt 2024/4/3 15:17 缘起&#xff1a;使用友善之臂FriendlyElec的NanoPi NEO Core开发板 https://wiki.friendlyelec.com/wiki/index.php/NanoPi_NEO/zh#.E8.BF.90.E8.A1.8CFriendlyCore NanoPi NEO/zh http://wiki.friendlyelec.com/w…

每日一题 --- 滑动窗口最大值[力扣][Go]

滑动窗口最大值 题目&#xff1a;239. 滑动窗口最大值 给你一个整数数组 nums&#xff0c;有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。 返回 滑动窗口中的最大值 。 示例 1&#xff1…

人工智能会拥有反思能力吗?

一、背景 人工智能是否能拥有真正的反思能力&#xff0c;目前仍在探索和发展之中。虽然现有的AI系统可以在一定程度上进行自我学习、自我调整和优化&#xff0c;但是它们的“反思”还远未达到人类意义上的深度和全面性。 传统的人工智能系统依赖于预设的算法和模型&#xff0c…

微信小程序怎么制作?制作一个微信小程序需要多少钱?

随着移动互联网的快速发展&#xff0c;微信小程序已成为连接用户与服务的重要桥梁。它以其便捷性和易用性&#xff0c;为各类企业和个人提供了一个全新的展示和交易平台。那么&#xff0c;如何制作一个微信小程序&#xff1f;又需要投入多少资金呢&#xff1f;本文将为您提供全…

C++实现二叉搜索树的增删查改(非递归玩法)

文章目录 一、二叉搜索树的概念结构和时间复杂度二、二叉搜索树的插入三、二叉搜索树的查找四、二叉搜索树的删除&#xff08;最麻烦&#xff0c;情况最多&#xff0c;一一分析&#xff09;3.1首先我们按照一般情况下写&#xff0c;不考虑特殊情况下4.1.1左为空的情况&#xff…

【HTML】简单制作一个动态3D正方体

目录 前言 开始 HTML部分 JS部分 CSS部分 效果图 总结 前言 无需多言&#xff0c;本文将详细介绍一段代码&#xff0c;具体内容如下&#xff1a; 开始 首先新建文件夹&#xff0c;创建两个文本文档&#xff0c;其中HTML的文件名改为[index.html]&#xff0c;JS的文件名改…

数据仓库——聚集

数据仓库基础笔记思维导图已经整理完毕&#xff0c;完整连接为&#xff1a; 数据仓库基础知识笔记思维导图 聚集 在对性能不断探索的过程中&#xff0c;聚集是最强大最、有效的数据处理工具。通过仔细规划和集成&#xff0c;聚集将队数据仓库性能产生巨大影响。无需针对特定的…

win11安装wsl报错:无法解析服务器的名称或地址

一 说明 项目开发中&#xff0c;需要用到wsl&#xff0c;因此根据wsl官方&#xff08;WSL安装教程&#xff09;命令 wsl --install 进行wsl的安装。而本文主要是记录自己在安装wsl中遇到的问题 “无法解析服务器的名称或地址” 的解决办法。 二 方法一&#xff1a;更改DNS&…

在Go语言中如何避免接口污染

在设计和构造代码时,接口是Go语言的基石之一。然而,就像许多工具或概念一样,滥用它们通常不是一个好主意。接口污染就是用不必要的抽象使我们的代码变得难以理解。这是来自另一种编程语言具有不同习惯的开发人员经常犯的错误。在深入讨论这个话题之前,让我们重新思考一下Go…

Java文件内容查找:简单实现与应用

一、Java文件内容查找的基本原理 在Java中&#xff0c;文件内容查找可以通过读取文件并逐行检查每一行内容来实现。基本的流程包括以下几个步骤&#xff1a; 打开文件&#xff1a;使用Java的文件操作类&#xff08;如FileInputStream&#xff09;打开要查找的文件。 逐行读取…

算法练习----力扣每日一题------6

原题链接&#xff1a; 1379. 找出克隆二叉树中的相同节点 - 力扣&#xff08;LeetCode&#xff09; 题目解析&#xff1a; 给两个二叉树&#xff0c;original和它的克隆树cloned,二者存的数据和数据的相对位置完全一样&#xff0c;给一个o树里的节点target&#xff0c;求对应的…

Kotlin作用域函数:let、also、run、apply、with

​​​​​​​ let函数 使用场景&#xff1a;可空变量的操作&#xff0c;无需判空 p?.let {it.name "lily"it.age "21"} also函数 使用场景&#xff1a;多个扩展函数链式调用&#xff08;返回值是本身&#xff09; p?.also {it.name "den…

HCIA-RS基础-VLAN技术原理和配置

目录 VLAN 技术原理和配置1. VLAN 技术的背景2. VLAN 标签的产生方法3. VLAN 标签的应用规则4. VLAN 的配置总结 VLAN 技术原理和配置 1. VLAN 技术的背景 VLAN&#xff08;Virtual Local Area Network&#xff09;是一种逻辑上划分网络的技术&#xff0c;可以将一个物理局域…

Postman 请求参数传递指南:Query、Path 和 Body 详解

Postman 是一个非常流行的 API 开发环境&#xff0c;它允许开发者测试、开发和文档化他们的 API。在 Postman 中&#xff0c;当你发送一个请求时&#xff0c;你可能需要将参数传递给服务器。这些参数可以通过不同的方式传递&#xff0c;例如 Query Parameters&#xff08;查询参…

使用 select 标签,1 分钟完成一个简单的下拉菜单

在网上冲浪时&#xff0c;我们经常遇到需要做出选择的情况&#xff0c;比如选择你喜爱的电影类型、预订酒店的房间类型或者在网购时挑选衣服的尺码。 这些选择往往通过一个简洁明了的下拉菜单来实现&#xff0c;让我们的决策过程变得轻松愉快。这个神奇的下拉菜单&#xff0c;…

算法打卡day25

今日任务&#xff1a; 1&#xff09;491.递增子序列 2&#xff09;46.全排列 3&#xff09;47.全排列 II 491.递增子序列 题目链接&#xff1a;491. 非递减子序列 - 力扣&#xff08;LeetCode&#xff09; 给定一个整型数组, 你的任务是找到所有该数组的递增子序列&#xff0c…

composure合成透明通道

composure合成透明通道 2022-05-23 08:59 [技巧分享]使用配有HDRI的背板的进行合成 Using Composure with a Bac - 1.Using Composure with a Backplate and H.mp41启用插件2线性色彩空间3打开合成面板4面板新建合成新建三个图层 FG前面物体&#xff0c;背景物体 去掉阴影 flo…