前言
多进程开发经常会遇到主进程关闭,子进程需要跟随主进程一同关闭。比如调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#子进程跟随主进程关闭的资料请关注代码网其它相关文章!
发表评论