System.Diagnostics.Process
类创建新进程。我希望当我的应用程序崩溃时,该进程被终止。但是,如果我从任务管理器中杀死我的应用程序,则不会杀死子进程。有什么方法可以使子进程依赖于父进程吗?#1 楼
从此论坛,您可以将其归功于'Josh'。Application.Quit()
和Process.Kill()
是可能的解决方案,但事实证明是不可靠的。当主应用程序死亡时,子进程仍处于运行状态。我们真正想要的是子进程在主进程死亡后立即死亡。解决方案是使用“作业对象” http://msdn.microsoft.com/zh-cn/ library / ms682409(VS.85).aspx。
其目的是为您的主应用程序创建一个“作业对象”,并向该作业对象注册子进程。如果主进程死亡,则操作系统将负责终止子进程。
public enum JobObjectInfoType
{
AssociateCompletionPortInformation = 7,
BasicLimitInformation = 2,
BasicUIRestrictions = 4,
EndOfJobTimeInformation = 6,
ExtendedLimitInformation = 9,
SecurityLimitInformation = 5,
GroupInformation = 11
}
[StructLayout(LayoutKind.Sequential)]
public struct SECURITY_ATTRIBUTES
{
public int nLength;
public IntPtr lpSecurityDescriptor;
public int bInheritHandle;
}
[StructLayout(LayoutKind.Sequential)]
struct JOBOBJECT_BASIC_LIMIT_INFORMATION
{
public Int64 PerProcessUserTimeLimit;
public Int64 PerJobUserTimeLimit;
public Int16 LimitFlags;
public UInt32 MinimumWorkingSetSize;
public UInt32 MaximumWorkingSetSize;
public Int16 ActiveProcessLimit;
public Int64 Affinity;
public Int16 PriorityClass;
public Int16 SchedulingClass;
}
[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_EXTENDED_LIMIT_INFORMATION
{
public JOBOBJECT_BASIC_LIMIT_INFORMATION BasicLimitInformation;
public IO_COUNTERS IoInfo;
public UInt32 ProcessMemoryLimit;
public UInt32 JobMemoryLimit;
public UInt32 PeakProcessMemoryUsed;
public UInt32 PeakJobMemoryUsed;
}
public class Job : IDisposable
{
[DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
static extern IntPtr CreateJobObject(object a, string lpName);
[DllImport("kernel32.dll")]
static extern bool SetInformationJobObject(IntPtr hJob, JobObjectInfoType infoType, IntPtr lpJobObjectInfo, uint cbJobObjectInfoLength);
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool AssignProcessToJobObject(IntPtr job, IntPtr process);
private IntPtr m_handle;
private bool m_disposed = false;
public Job()
{
m_handle = CreateJobObject(null, null);
JOBOBJECT_BASIC_LIMIT_INFORMATION info = new JOBOBJECT_BASIC_LIMIT_INFORMATION();
info.LimitFlags = 0x2000;
JOBOBJECT_EXTENDED_LIMIT_INFORMATION extendedInfo = new JOBOBJECT_EXTENDED_LIMIT_INFORMATION();
extendedInfo.BasicLimitInformation = info;
int length = Marshal.SizeOf(typeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION));
IntPtr extendedInfoPtr = Marshal.AllocHGlobal(length);
Marshal.StructureToPtr(extendedInfo, extendedInfoPtr, false);
if (!SetInformationJobObject(m_handle, JobObjectInfoType.ExtendedLimitInformation, extendedInfoPtr, (uint)length))
throw new Exception(string.Format("Unable to set information. Error: {0}", Marshal.GetLastWin32Error()));
}
#region IDisposable Members
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
#endregion
private void Dispose(bool disposing)
{
if (m_disposed)
return;
if (disposing) {}
Close();
m_disposed = true;
}
public void Close()
{
Win32.CloseHandle(m_handle);
m_handle = IntPtr.Zero;
}
public bool AddProcess(IntPtr handle)
{
return AssignProcessToJobObject(m_handle, handle);
}
}
看构造函数...
JOBOBJECT_BASIC_LIMIT_INFORMATION info = new JOBOBJECT_BASIC_LIMIT_INFORMATION();
info.LimitFlags = 0x2000;
这里的关键是正确设置作业对象。在构造函数中,我将“ limits”设置为0x2000,这是
JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE
的数值。MSDN将此标志定义为:
与
关联的进程在关闭作业的最后一个
句柄时终止。
一旦设置了此类,您只需注册每个子进程都有工作。例如:
[DllImport("user32.dll", SetLastError = true)]
public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
Excel.Application app = new Excel.ApplicationClass();
uint pid = 0;
Win32.GetWindowThreadProcessId(new IntPtr(app.Hwnd), out pid);
job.AddProcess(Process.GetProcessById((int)pid).Handle);
#2 楼
这个答案从@Matt Howells的出色答案以及其他答案开始(请参见下面的代码链接)。改进:支持32位和64位。
解决了@Matt Howells答案中的一些问题:
extendedInfoPtr
的小内存泄漏'Win32'编译错误和
我调用
CreateJobObject
时遇到堆栈不平衡的异常(使用Windows 10,Visual Studio 2015、32位) 。为作业命名,因此,例如,如果使用SysInternals,就可以轻松找到它。
API稍微简单一些,代码更少。
这里是如何使用此代码的方法:
// Get a Process object somehow.
Process process = Process.Start(exePath, args);
// Add the Process to ChildProcessTracker.
ChildProcessTracker.AddProcess(process);
要支持Windows 7,需要:
一个简单的app.manifest按照@adam smith的描述进行更改。
如果使用Visual Studio,则要添加注册表设置。
就我而言,我不需要支持Windows 7,因此可以在下面的静态构造函数的
顶部进行简单的检查。
<
我通过编程方式比较了托管结构,仔细测试了结构的32位和64位版本和彼此的本机版本(整体大小以及每个成员的偏移量)。
我已经在Windows 7、8和10上测试了此代码。
评论
关闭作业句柄怎么办?
–坦率的问:
16年11月2日在23:18
@FrankQ。让Windows在我们的进程终止时为我们关闭s_jobHandle是很重要的,因为我们的进程可能会意外终止(例如崩溃或用户使用任务管理器)。请参阅我对s_jobHandle的评论。
–罗恩
16年10月10日在5:33
它为我工作。请问您对使用JOB_OBJECT_LIMIT_BREAKAWAY_OK标志有何看法? docs.microsoft.com/zh-CN/windows/desktop/api/winnt/…
–叶平
18-10-22在6:06
@yiping听起来像CREATE_BREAKAWAY_FROM_JOB允许您的子进程生成可以超过原始进程寿命的进程。这是与OP要求不同的要求。相反,如果您可以使原始进程产生一个寿命较长的进程(而不使用ChildProcessTracker),那会更简单。
–罗恩
18-10-23在18:20
@Ron您可以添加一个仅接受过程句柄而不接受整个过程的重载吗?
– Jannik
19年4月9日在8:40
#3 楼
这篇文章旨在作为@Matt Howells答案的扩展,专门针对那些在Vista或Win7下使用Job Objects遇到问题的人,尤其是如果在调用AssignProcessToJobObject时遇到访问被拒绝错误('5')的情况。 >; dr
为了确保与Vista和Win7兼容,请将以下清单添加到.NET父进程中:
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<v3:trustInfo xmlns:v3="urn:schemas-microsoft-com:asm.v3">
<v3:security>
<v3:requestedPrivileges>
<v3:requestedExecutionLevel level="asInvoker" uiAccess="false" />
</v3:requestedPrivileges>
</v3:security>
</v3:trustInfo>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<!-- We specify these, in addition to the UAC above, so we avoid Program Compatibility Assistant in Vista and Win7 -->
<!-- We try to avoid PCA so we can use Windows Job Objects -->
<!-- See https://stackoverflow.com/questions/3342941/kill-child-process-when-parent-process-is-killed -->
<application>
<!--The ID below indicates application support for Windows Vista -->
<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>
<!--The ID below indicates application support for Windows 7 -->
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
</application>
</compatibility>
</assembly>
请注意,当您在Visual Studio 2012中添加新清单时,它将已经包含上面的代码段,因此您无需从听众那里复制它。它还将包含Windows 8的节点。
完整说明
如果您正在启动的进程已经与另一个进程关联,则您的作业关联将失败,并显示访问被拒绝错误。工作。输入程序兼容性助手,从Windows Vista开始,它将为自己的作业分配各种进程。
在Vista中,您可以通过简单地包括应用程序清单来标记要从PCA中排除的应用程序。 Visual Studio似乎可以自动为.NET应用程序执行此操作,因此您就可以了。
在Win7中不再需要简单的清单。 [1]在这里,您必须使用清单中的标记专门指定与Win7兼容。 [2]
这使我担心Windows8。是否需要再次更改清单?显然,云已经中断了,因为Windows 8现在允许一个进程属于多个作业。 [3]所以我还没有测试过,但是我想如果您只包含带有受支持的OS信息的清单,这种疯狂的现象就会过去了。
提示1:如果您正在开发一个就像我以前使用Visual Studio的.NET应用程序一样,这里[4]是有关如何自定义应用程序清单的一些很好的说明。
提示2:从Visual Studio启动应用程序时要小心。我发现,添加适当的清单后,即使我使用“开始而不调试”,从Visual Studio启动时,PCA仍然存在问题。但是,从资源管理器启动我的应用程序是可行的。使用注册表手动添加devenv以将其从PCA中排除后,启动使用VS中的Job Objects的应用程序也开始工作。 [5]
技巧3:如果您想知道PCA是否是您的问题,请尝试从命令行启动应用程序,或将程序复制到网络驱动器并从那里运行。在这些情况下会自动禁用PCA。
[1] http://blogs.msdn.com/b/cjacks/archive/2009/06/18/pca-changes-for-windows-7如何告诉我们您不是安装人员的2,因为我们已更改了you.aspx的规则。
[2] http ://ayende.com/blog/4360/how-to-opt-out-of-program-compatibility-assistant
[3] http://msdn.microsoft.com/zh-cn /library/windows/desktop/ms681949(v=vs.85).aspx:
“一个进程可以与Windows 8中的多个作业相关联”“
[4]我将应用程序清单嵌入到使用VS2008的应用程序中吗?
[5]如何停止Visual Studio调试器在作业对象中启动我的进程?
评论
这些都是该主题的重要补充,谢谢!我利用了该答案的各个方面,包括链接。
–约翰尼·考夫曼(Johnny Kauffman)
2014年8月11日在17:51
#4 楼
当您控制子进程运行的代码时,这可能对某些人有用。这种方法的好处是它不需要任何Windows本地调用。基本思想是将子级的标准输入重定向到另一端连接到父级的流,并使用该流。检测父母何时离开。使用
System.Diagnostics.Process
启动子级时,很容易确保将其标准输入重定向:Process childProcess = new Process();
childProcess.StartInfo = new ProcessStartInfo("pathToConsoleModeApp.exe");
childProcess.StartInfo.RedirectStandardInput = true;
childProcess.StartInfo.CreateNoWindow = true; // no sense showing an empty black console window which the user can't input into
然后在子级进程中利用以下事实:来自标准输入流的
Read
总是返回至少1个字节,直到关闭流为止,此时它们将开始返回0个字节。我结束操作的方式概述如下:我的方式还使用消息泵来保持主线程可用于观看标准以外的内容,但是这种通用方法也可以在没有消息泵的情况下使用。using System;
using System.IO;
using System.Threading;
using System.Windows.Forms;
static int Main()
{
Application.Run(new MyApplicationContext());
return 0;
}
public class MyApplicationContext : ApplicationContext
{
private SynchronizationContext _mainThreadMessageQueue = null;
private Stream _stdInput;
public MyApplicationContext()
{
_stdInput = Console.OpenStandardInput();
// feel free to use a better way to post to the message loop from here if you know one ;)
System.Windows.Forms.Timer handoffToMessageLoopTimer = new System.Windows.Forms.Timer();
handoffToMessageLoopTimer.Interval = 1;
handoffToMessageLoopTimer.Tick += new EventHandler((obj, eArgs) => { PostMessageLoopInitialization(handoffToMessageLoopTimer); });
handoffToMessageLoopTimer.Start();
}
private void PostMessageLoopInitialization(System.Windows.Forms.Timer t)
{
if (_mainThreadMessageQueue == null)
{
t.Stop();
_mainThreadMessageQueue = SynchronizationContext.Current;
}
// constantly monitor standard input on a background thread that will
// signal the main thread when stuff happens.
BeginMonitoringStdIn(null);
// start up your application's real work here
}
private void BeginMonitoringStdIn(object state)
{
if (SynchronizationContext.Current == _mainThreadMessageQueue)
{
// we're already running on the main thread - proceed.
var buffer = new byte[128];
_stdInput.BeginRead(buffer, 0, buffer.Length, (asyncResult) =>
{
int amtRead = _stdInput.EndRead(asyncResult);
if (amtRead == 0)
{
_mainThreadMessageQueue.Post(new SendOrPostCallback(ApplicationTeardown), null);
}
else
{
BeginMonitoringStdIn(null);
}
}, null);
}
else
{
// not invoked from the main thread - dispatch another call to this method on the main thread and return
_mainThreadMessageQueue.Post(new SendOrPostCallback(BeginMonitoringStdIn), null);
}
}
private void ApplicationTeardown(object state)
{
// tear down your application gracefully here
_stdInput.Close();
this.ExitThread();
}
}
注意此方法:
启动的实际子.exe必须是控制台应用程序,因此它仍附加到stdin / out / err。如上例所示,我只需创建一个引用现有项目的小型控制台项目,实例化我的应用程序上下文并在
Application.Run()
方法内调用Main
,就可以轻松适应使用消息泵(但未显示GUI)的现有应用程序从技术上讲,这仅在父进程退出时向子进程发出信号,因此无论父进程正常退出还是崩溃,它都可以工作,但仍由子进程自行执行关闭操作。这可能不是您想要的...
#5 楼
一种方法是将父进程的PID传递给子进程。子进程将定期轮询具有指定pid的进程是否存在。如果不是这样,它只会退出。您还可以在子方法中使用Process.WaitForExit方法,以在父进程结束时收到通知,但在任务管理器中可能不起作用。
评论
如何将父级PID传递给子级?有什么系统解决方案?我无法修改子进程二进制文件。
–西伯利亚人
10年7月27日在11:17
好吧,如果您不能修改子进程,即使将PID传递给它,也无法使用我的解决方案。
– Giorgi
10 Jul 27 '11:19
@Idsa您可以通过命令行传递它:Process.Start(字符串fileName,字符串参数)
–扭曲
2012年3月17日下午5:29
除了轮询之外,您还可以插入流程类的Exit事件。
–RichardOD
2012年9月4日在21:08
尝试过,但如果子进程还活着(至少在我的情况下为Cobol-> NET),父进程并不总是退出。在Sysinternals ProcessExplorer中易于检查流程层次结构。
–伊凡·费雷尔·维拉(Ivan Ferrer Villa)
2012年10月23日在12:03
#6 楼
还有另一种简单有效的相关方法,可以在程序终止时完成子进程。您可以从父级实现调试器并将其附加到调试器。当父进程结束时,子进程将被操作系统杀死。将调试器从子级附加到父级有两种方法(请注意,一次只能附加一个调试器)。您可以在此处找到有关此主题的更多信息。这里有一个实用程序类,它可以启动新进程并将调试器附加到该进程。它已根据Roger Knapp的帖子改编。唯一的要求是两个进程需要共享相同的位。您不能从64位进程调试32位进程,反之亦然。
public class ProcessRunner
{
// see http://csharptest.net/1051/managed-anti-debugging-how-to-prevent-users-from-attaching-a-debugger/
// see https://stackoverflow.com/a/24012744/2982757
public Process ChildProcess { get; set; }
public bool StartProcess(string fileName)
{
var processStartInfo = new ProcessStartInfo(fileName)
{
UseShellExecute = false,
WindowStyle = ProcessWindowStyle.Normal,
ErrorDialog = false
};
this.ChildProcess = Process.Start(processStartInfo);
if (ChildProcess == null)
return false;
new Thread(NullDebugger) {IsBackground = true}.Start(ChildProcess.Id);
return true;
}
private void NullDebugger(object arg)
{
// Attach to the process we provided the thread as an argument
if (DebugActiveProcess((int) arg))
{
var debugEvent = new DEBUG_EVENT {bytes = new byte[1024]};
while (!this.ChildProcess.HasExited)
{
if (WaitForDebugEvent(out debugEvent, 1000))
{
// return DBG_CONTINUE for all events but the exception type
var continueFlag = DBG_CONTINUE;
if (debugEvent.dwDebugEventCode == DebugEventType.EXCEPTION_DEBUG_EVENT)
continueFlag = DBG_EXCEPTION_NOT_HANDLED;
ContinueDebugEvent(debugEvent.dwProcessId, debugEvent.dwThreadId, continueFlag);
}
}
}
else
{
//we were not able to attach the debugger
//do the processes have the same bitness?
//throw ApplicationException("Unable to attach debugger") // Kill child? // Send Event? // Ignore?
}
}
#region "API imports"
private const int DBG_CONTINUE = 0x00010002;
private const int DBG_EXCEPTION_NOT_HANDLED = unchecked((int) 0x80010001);
private enum DebugEventType : int
{
CREATE_PROCESS_DEBUG_EVENT = 3,
//Reports a create-process debugging event. The value of u.CreateProcessInfo specifies a CREATE_PROCESS_DEBUG_INFO structure.
CREATE_THREAD_DEBUG_EVENT = 2,
//Reports a create-thread debugging event. The value of u.CreateThread specifies a CREATE_THREAD_DEBUG_INFO structure.
EXCEPTION_DEBUG_EVENT = 1,
//Reports an exception debugging event. The value of u.Exception specifies an EXCEPTION_DEBUG_INFO structure.
EXIT_PROCESS_DEBUG_EVENT = 5,
//Reports an exit-process debugging event. The value of u.ExitProcess specifies an EXIT_PROCESS_DEBUG_INFO structure.
EXIT_THREAD_DEBUG_EVENT = 4,
//Reports an exit-thread debugging event. The value of u.ExitThread specifies an EXIT_THREAD_DEBUG_INFO structure.
LOAD_DLL_DEBUG_EVENT = 6,
//Reports a load-dynamic-link-library (DLL) debugging event. The value of u.LoadDll specifies a LOAD_DLL_DEBUG_INFO structure.
OUTPUT_DEBUG_STRING_EVENT = 8,
//Reports an output-debugging-string debugging event. The value of u.DebugString specifies an OUTPUT_DEBUG_STRING_INFO structure.
RIP_EVENT = 9,
//Reports a RIP-debugging event (system debugging error). The value of u.RipInfo specifies a RIP_INFO structure.
UNLOAD_DLL_DEBUG_EVENT = 7,
//Reports an unload-DLL debugging event. The value of u.UnloadDll specifies an UNLOAD_DLL_DEBUG_INFO structure.
}
[StructLayout(LayoutKind.Sequential)]
private struct DEBUG_EVENT
{
[MarshalAs(UnmanagedType.I4)] public DebugEventType dwDebugEventCode;
public int dwProcessId;
public int dwThreadId;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 1024)] public byte[] bytes;
}
[DllImport("Kernel32.dll", SetLastError = true)]
private static extern bool DebugActiveProcess(int dwProcessId);
[DllImport("Kernel32.dll", SetLastError = true)]
private static extern bool WaitForDebugEvent([Out] out DEBUG_EVENT lpDebugEvent, int dwMilliseconds);
[DllImport("Kernel32.dll", SetLastError = true)]
private static extern bool ContinueDebugEvent(int dwProcessId, int dwThreadId, int dwContinueStatus);
[DllImport("Kernel32.dll", SetLastError = true)]
public static extern bool IsDebuggerPresent();
#endregion
}
new ProcessRunner().StartProcess("c:\Windows\system32\calc.exe");
#7 楼
我一直在寻找不需要非托管代码的解决方案。我还不能使用标准的输入/输出重定向,因为它是Windows Forms应用程序。我的解决方案是在父进程中创建一个命名管道,然后将子进程连接到同一管道。如果父进程退出,则管道将断开并且子进程可以检测到此情况。
下面是使用两个控制台应用程序的示例:
Parent
private const string PipeName = "471450d6-70db-49dc-94af-09d3f3eba529";
public static void Main(string[] args)
{
Console.WriteLine("Main program running");
using (NamedPipeServerStream pipe = new NamedPipeServerStream(PipeName, PipeDirection.Out))
{
Process.Start("child.exe");
Console.WriteLine("Press any key to exit");
Console.ReadKey();
}
}
孩子
private const string PipeName = "471450d6-70db-49dc-94af-09d3f3eba529"; // same as parent
public static void Main(string[] args)
{
Console.WriteLine("Child process running");
using (NamedPipeClientStream pipe = new NamedPipeClientStream(".", PipeName, PipeDirection.In))
{
pipe.Connect();
pipe.BeginRead(new byte[1], 0, 1, PipeBrokenCallback, pipe);
Console.WriteLine("Press any key to exit");
Console.ReadKey();
}
}
private static void PipeBrokenCallback(IAsyncResult ar)
{
// the pipe was closed (parent process died), so exit the child process too
try
{
NamedPipeClientStream pipe = (NamedPipeClientStream)ar.AsyncState;
pipe.EndRead(ar);
}
catch (IOException) { }
Environment.Exit(1);
}
#8 楼
使用事件处理程序在一些退出方案上建立钩子:var process = Process.Start("program.exe");
AppDomain.CurrentDomain.DomainUnload += (s, e) => { process.Kill(); process.WaitForExit(); };
AppDomain.CurrentDomain.ProcessExit += (s, e) => { process.Kill(); process.WaitForExit(); };
AppDomain.CurrentDomain.UnhandledException += (s, e) => { process.Kill(); process.WaitForExit(); };
评论
如此简单却如此有效。
–uncommon_name
5月15日20:48
从任务管理器中终止父进程时,似乎未调用这些事件
–阿伦
9月7日晚上8:23
#9 楼
只是我的2018年版本。在Main()方法中使用它。
using System.Management;
using System.Diagnostics;
...
// Called when the Main Window is closed
protected override void OnClosed(EventArgs EventArgs)
{
string query = "Select * From Win32_Process Where ParentProcessId = " + Process.GetCurrentProcess().Id;
ManagementObjectSearcher searcher = new ManagementObjectSearcher(query);
ManagementObjectCollection processList = searcher.Get();
foreach (var obj in processList)
{
object data = obj.Properties["processid"].Value;
if (data != null)
{
// retrieve the process
var childId = Convert.ToInt32(data);
var childProcess = Process.GetProcessById(childId);
// ensure the current process is still live
if (childProcess != null) childProcess.Kill();
}
}
Environment.Exit(0);
}
评论
我怀疑在父进程被杀死时会执行此操作。
– springy76
18-10-30在9:11
#10 楼
我看到两个选项:如果您确切知道可以启动哪个子进程,并且确定它们仅从主进程启动,那么您可以考虑简单地按名称搜索它们杀死他们
遍历所有进程并杀死将您的进程作为父进程的每个进程(我想您需要先杀死子进程)。这里说明了如何获取父进程ID。
#11 楼
我已经制作了一个子流程管理库,其中通过双向WCF管道监视父流程和子流程。如果子进程终止或父进程终止,则得到通知。还有一个调试器帮助程序可自动将VS调试器附加到已启动的子进程
项目站点:
http://www.crawler-lib.net/child-processes
NuGet软件包:
https://www.nuget.org / packages / ChildProcesses
https://www.nuget.org/packages/ChildProcesses.VisualStudioDebug/
#12 楼
最好在进程启动后再调用job.AddProcess:prc.Start();
job.AddProcess(prc.Handle);
在终止之前调用AddProcess时,子进程不会被杀死。 (Windows 7 SP1)
private void KillProcess(Process proc)
{
var job = new Job();
job.AddProcess(proc.Handle);
job.Close();
}
评论
在该过程开始后调用job.AddProcess会终止使用此对象的目的。
– SerG
2014年3月19日10:02
#13 楼
到目前为止,提出的解决方案还提供了丰富的解决方案。...许多解决方案的问题在于,它们依赖于父级和子级进程有序地关闭,这并不总是正确的。当开发正在进行中时。我发现,每当我在调试器中终止父进程时,我的子进程就经常被孤立,这要求我使用任务管理器杀死孤立的进程,以重建我的解决方案。
解决方案:通过父进程的ID在子进程的命令行上(甚至在环境变量中侵入性更小)。
在父进程中,该进程ID可以通过以下方式获得:
Process.CurrentProcess.Id;
在子进程:
Process parentProcess = Process.GetProcessById(parentProcessId);
parentProcess.Exited += (s, e) =>
{
// clean up what you can.
this.Dispose();
// maybe log an error
....
// And terminate with prejudice!
//(since something has already gone terribly wrong)
Process.GetCurrentProcess().Kill();
};
我对这在生产代码中是否可接受的做法有两种想法。一方面,这永远都不会发生。但是,另一方面,这可能意味着重新启动进程与重新启动生产服务器之间的区别。而且永远不应该发生的事情。
在调试有序关闭问题时,它肯定很有用。
#14 楼
适用于我的解决方案:创建过程时,添加标签
process.EnableRaisingEvents = true;
: csc = new Process();
csc.StartInfo.UseShellExecute = false;
csc.StartInfo.CreateNoWindow = true;
csc.StartInfo.FileName = Path.Combine(HLib.path_dataFolder, "csc.exe");
csc.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
csc.StartInfo.ErrorDialog = false;
csc.StartInfo.RedirectStandardInput = true;
csc.StartInfo.RedirectStandardOutput = true;
csc.EnableRaisingEvents = true;
csc.Start();
#15 楼
我有同样的问题。我正在创建子进程,如果我的主应用程序崩溃了,它将永远不会被杀死。我必须在调试时手动销毁子进程。我发现没有必要让孩子在某种程度上依赖父母。在我的主程序中,我添加了一个try catch来在退出时对子进程执行CleanUp()。 static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
try
{
Application.Run(new frmMonitorSensors());
}
catch(Exception ex)
{
CleanUp();
ErrorLogging.Add(ex.ToString());
}
}
static private void CleanUp()
{
List<string> processesToKill = new List<string>() { "Process1", "Process2" };
foreach (string toKill in processesToKill)
{
Process[] processes = Process.GetProcessesByName(toKill);
foreach (Process p in processes)
{
p.Kill();
}
}
}
评论
我将添加一个链接到CloseHandle
–奥斯汀·萨洛宁(Austin Salonen)
2011-3-16在20:34
不幸的是,我无法在64位模式下运行它。我在这里发布了一个基于此的工作示例。
–亚历山大·叶祖托夫(Alexander Yezutov)
2012年2月6日17:55
@Matt Howells-Win32.CloseHandle起源于何处?这是从kernel32.dll导入的吗?那里有一个匹配的签名,但是您没有像其他API函数那样显式地导入它。
–深奥的屏幕名称
2012年8月7日在20:56
对于64位模式的应用程序-> stackoverflow.com/a/5976162对于Vista / Win7问题->social.msdn.microsoft.com/forums/zh-CN/windowssecurity/thread/…
– hB0
2012年8月23日在9:46
好的,MSDN说:JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE标志要求使用JOBOBJECT_EXTENDED_LIMIT_INFORMATION结构。
– SerG
2014年3月19日上午10:17