前言
多进程开发经常会遇到主进程关闭,子进程需要跟随主进程一同关闭。比如调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多进程开发中比较重要的技术,因为大部分场景主进程退出后子进程应该跟随退出,正常流程中通过代码可以在退出时关闭所有子进程,但是异常崩溃时则不行,会出现遗留子进程。而本文的方法就很好的解决的这个问题,而且也不需要编写任何关闭子进程的相关代码,方便且省心。
以上就是c#如何实现子进程跟随主进程关闭的详细内容,更多关于c#子进程跟随主进程关闭的资料请关注代码网其它相关文章!
发表评论