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.rhkb.cn/news/299838.html

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

相关文章

UE4_自定义反射和折射和法线图

UE4 自定义反射和折射和法线图 2020-05-22 09:36 将ReflectionVector和反射图像进行ViewAlignedReflection,输出的textrue和相机位置CameraPosition的onePlus进行Dot点乘之后乘以一个float系数反射度&#xff0c;输出给固有色&#xff0c;就有反射效果了。球型反射。 折射&…

【环境变量】常见的环境变量 | 相关指令 | 环境变量系统程序的结合理解

目录 常见的环境变量 HOME PWD SHELL HISTSIZE 环境变量相关的指令 echo&env export unset 本地变量 环境变量整体理解 程序现象_代码查看环境变量 整体理解 环境变量表 环境变量表的传递 环境变量表的查看 测试验证 少说废话&#x1f197; 每个用户…

K8S之Job和CronJob控制器

这里写目录标题 Job概念适用场景使用案例 CronJob概念适用场景使用案例 Job 概念 Job控制器用于管理Pod对象运行一次性任务&#xff0c;例如&#xff1a;对数据库备份&#xff0c;可以直接在k8s上启动一个mysqldump备份程序&#xff0c;也可以启动一个pod&#xff0c;这个pod…

Endnotes编辑参考文献的Style

Endnotes版本&#xff1a;X7 编辑过程&#xff1a; 1&#xff1a;打开软件&#xff0c;点击编辑—输出样式&#xff0c;可以在已有的style上编辑或则在建立新的样式。 我们选择numbered样式进行编辑。 2.我们先插入一个文献看看numbered的样式。关于下载endnotes的文件方法前…

“进击的巨人”:服务器硬件基础知识解析

引言&#xff1a; 服务器是网络环境中负责处理数据、运行应用程序和服务多用户的高性能计算机系统。了解服务器的硬件构成有助于更好地管理和优化IT资源。 服务器和普通PC的差异&#xff1a; 服务器具有比个人电脑更高的处理能力、稳定性和可靠性&#xff0c;它们通常运行在没…

B+树索引(如何设计、用好索引)

1.索引的代价 空间上的代价 时间上的代价 每次对表中的数据进⾏增、删、改操作时&#xff0c;都需要去修改各个B树索引。⽽且我们讲过&#xff0c;B树每层节点都是按照索引列的值从⼩到⼤的顺序排序⽽组成了双 向链表。不论是叶⼦节点中的记录&#xff0c;还是内节点中的记录&a…

Adobe InCopy 2024 v19.3 (macOS, Windows) - 编写和副本编辑软件

Adobe InCopy 2024 v19.3 (macOS, Windows) - 编写和副本编辑软件 Acrobat、After Effects、Animate、Audition、Bridge、Character Animator、Dimension、Dreamweaver、Illustrator、InCopy、InDesign、Lightroom Classic、Media Encoder、Photoshop、Premiere Pro、Adobe XD…

星系炸弹(蓝桥杯真题填空题)

import java.time.LocalDate; import java.time.temporal.ChronoUnit; public class BombExplosionDate { public static void main(String[] args) { // 定义贝塔炸弹的放置日期和定时天数 LocalDate placementDate LocalDate.of(2014, 11, 9); int daysToExplode 10…

【已解决】ZIP压缩文件如何设置密码?

ZIP是常用的压缩格式之一&#xff0c;对于重要的ZIP文件&#xff0c;我们还可设置密码保护&#xff0c;那ZIP压缩文件怎么设置密码呢&#xff1f;不清楚的小伙伴一起来看看吧&#xff01; 给ZIP文件设置密码&#xff0c;我们需要用到支持ZIP格式的解压缩软件&#xff0c;比如7…

IDEA连接SqlServer数据库

目录 下载jar包 下载sqljdbc_12.6压缩包 解压 导入IDEA 新建文件夹 复制粘贴进JDBC文件夹并设为library 编写类及方法 代码 下载jar包 以sqljdbc_12.6为例 下载sqljdbc_12.6压缩包 最新地址&#xff1a;sqljdbc 官方最新地址 解压 解压即用 导入IDEA 新建文件夹 复制…

秋招刷题4(动态规划)

1.购物单 import java.util.Scanner;public class Main {public static void main(String[] args){Scanner sc new Scanner(System.in);int N sc.nextInt();int m sc.nextInt();Goods[] goods new Goods[m];for(int i 0; i < m; i){goods[i] new Goods();}for(int i …

Python爬虫-爬取药膳食谱数据

&#x1f388; 博主&#xff1a;一只程序猿子 &#x1f388; 博客主页&#xff1a;一只程序猿子 博客主页 &#x1f388; 个人介绍&#xff1a;爱好(bushi)编程&#xff01; &#x1f388; 创作不易&#xff1a;喜欢的话麻烦您点个&#x1f44d;和⭐&#xff01; &#x1f388;…

【STM32】存储器和位带映射(bit band mapping)

文章目录 0 前言1 关于地址和存储器2 STM32内部存储器3 位带映射&#xff08;bit band mapping&#xff09;4 扩展&#xff1a;IAP 0 前言 最近在研究stm32标准库&#xff0c;对使用宏定义实现位操作的函数非常感兴趣&#xff0c;简单的一句PAout(1) 0;就能实现某个引脚电平的…

基于单片机光伏太阳能跟踪系统设计

**单片机设计介绍&#xff0c;基于单片机光伏太阳能跟踪系统设计 文章目录 一 概要二、功能设计三、 软件设计原理图 五、 程序六、 文章目录 一 概要 基于单片机光伏太阳能跟踪系统的设计&#xff0c;旨在通过单片机技术实现对光伏太阳能设备的自动跟踪&#xff0c;以提高太阳…

InnoDB 数据页结构

1.行格式 1.1 Compact行格式 1.1.1 示意图 1.1.2 准备一下 1&#xff09;建表 mysql> CREATE TABLE record_format_demo (-> c1 VARCHAR(10),-> c2 VARCHAR(10) NOT NULL,-> c3 CHAR(10),-> c4 VARCHAR(10)-> ) CHARSETascii ROW_FORMATCOM…

ARM、X86、RISC-V三分天下

引入&#xff1a; 简单的介绍一下X86、ARM、RISC-V三种cpu架构的区别和应用场景。 目录 简单概念讲解 1. X86架构 2. ARM架构 3. RISC-V架构 应用场景 X86、ARM和RISC-V是三种不同的CPU架构&#xff0c;它们在设计理念、指令集和应用场景上有一些区别。 简单概念讲解 1. X…

力扣Lc29---- 541. 反转字符串 II(java版)-2024年4月06日

1.题目描述 2.知识点 &#xff08;1&#xff09;执行步骤如下&#xff1a; 初始化 s “abcdefg” 和 k 2 将字符串分割成长度为 2k 4 的块。 对每个块中的前 k 2 个字符进行反转。 执行过程 1&#xff09;第一次循环&#xff08;i 0&#xff09; start 0 end Math.min(0…

MPT - 初识账户状态树(World State)

往期回顾 ETH网络中的账户ETH网络中的区块链 通过以上文章&#xff0c;我们了解到ETH网络中的World State是节点根据交易维护的&#xff0c;节点在维护Wrold State时为了方便操作会将用户状态构建成一颗树&#xff0c;称为账户状态树&#xff0c;采用一种叫做MPT的数据结构 MP…

如何申请到免费SSL证书

免费SSL证书主要通过权威CA机构如Lets Encrypt、JoySSL等签发&#xff0c;具备基础的服务器身份验证功能&#xff0c;能有效防止中间人攻击&#xff0c;确保信息从用户的浏览器到服务器间的全程加密传输&#xff0c;保护用户隐私数据不被窃取或篡改。 尽管免费SSL证书可能存在有…

腾讯云添加域名后不生效

问题原因 添加域名后不生效可能是因为没有加CDN域名解析 解决步骤