2010-07-13 45 views
14

Tác vụ: Tự động giết tất cả các tiến trình con nếu quá trình cha mẹ chấm dứt. Các thủ tục gốc có thể được chấm dứt không chỉ theo cách chính xác, mà còn bằng cách giết chết trong ProcessExplorer, ví dụ. Tôi có thể làm như thế nào?Quy trình con chấm dứt khi quá trình cha mẹ chấm dứt trong C#

Câu hỏi tương tự trong С topic lời khuyên để sử dụng Đối tượng công việc. Làm thế nào để sử dụng nó trong C# mà không xuất DLL bên ngoài?


Tôi đã cố gắng sử dụng Đối tượng công việc. Nhưng mã này không hoạt động chính xác:

var job = PInvoke.CreateJobObject(null, null); 
    var jobli = new PInvoke.JOBOBJECT_BASIC_LIMIT_INFORMATION(); 

    jobli.LimitFlags = PInvoke.LimitFlags.JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE 
        | PInvoke.LimitFlags.JOB_OBJECT_LIMIT_PRIORITY_CLASS 
        | PInvoke.LimitFlags.JOB_OBJECT_LIMIT_JOB_TIME 
        | PInvoke.LimitFlags.JOB_OBJECT_LIMIT_DIE_ON_UNHANDLED_EXCEPTION 
        | PInvoke.LimitFlags.JOB_OBJECT_LIMIT_JOB_MEMORY; 

    var res = PInvoke.SetInformationJobObject(job, PInvoke.JOBOBJECTINFOCLASS.JobObjectBasicLimitInformation, jobli, 48); 

    if (!res) 
    { 
    int b = PInvoke.GetLastError(); 
    Console.WriteLine("Error " + b); 
    } 

    var Prc = Process.Start(...); 

    PInvoke.AssignProcessToJobObject(job, Prc.Handle); 

PInvoke.SetInformationJobObject trả về có lỗi. GetLastError trả về lỗi 24. Tuy nhiên, các công trình PInvoke.AssignProcessToJobObject và tiến trình con được thêm vào hàng đợi công việc (tôi có thể thấy nó trong ProcessExplorer). Nhưng, vì PInvoke.SetInformationJobObject không hoạt động - quá trình sinh sản vẫn còn sống khi tôi giết cha mẹ.

Tôi có gì không chính xác trong mã này?

+0

Câu hỏi khác có vẻ tốt với tôi, chỉ cần ghim các chức năng từ kernel32. http://www.pinvoke.net/default.aspx/kernel32.assignprocesstojobobject –

Trả lời

4

Bạn có thể vượt qua ProcessID của quy trình gốc làm đối số cho quá trình con. Và sau đó các quá trình con sẽ chịu trách nhiệm kiểm tra theo thời gian cho dù quá trình cha mẹ vẫn chạy. (Bằng cách gọi Process.GetProcessById.)

Một cách khác để theo dõi sự tồn tại của quy trình gốc là sử dụng Mutex nguyên thủy đồng bộ hóa. Ứng dụng gốc ban đầu sẽ tạo ra một mutex toàn cầu với tên được trẻ em biết đến. Trẻ em có thể kiểm tra theo thời gian cho dù mutex vẫn tồn tại và chấm dứt nếu không. (Sau khi quá trình cha mẹ được đóng, mutex sẽ bị hệ thống tự động phá hủy, bất kể cách nó được đóng lại như thế nào.)

+1

Cả hai lời khuyên không hữu ích - quy trình con không phải của tôi. Họ có thể là bất kỳ chương trình nào. – LionSoft

+2

@LionSoft: Bạn có thể có một quy trình con khác sẽ chịu trách nhiệm tạo các quy trình con đó không? Sau đó, quá trình đó có thể kiểm tra xem quá trình cha mẹ vẫn đang chạy và giết các trẻ khác nếu không. – Regent

+0

Nhưng phải làm gì nếu "quá trình con khác" sẽ bị buộc chấm dứt? – LionSoft

2

Windows không bắt buộc quá trình con đóng khi quá trình cha mẹ đóng. Khi bạn chọn "Kill Tree" trong một công cụ như Task Manager hoặc Process explorer, công cụ thực sự tìm thấy tất cả các tiến trình con và giết chúng từng cái một.

Nếu bạn muốn đảm bảo rằng các tiến trình con được làm sạch khi ứng dụng của bạn kết thúc, bạn có thể tạo một lớp ProcessManager thực hiện IDisposable mà thực sự tạo ra các quy trình, theo dõi trường hợp của chúng và các cuộc gọi Giết trên mỗi một trong số chúng trên Dispose, ví dụ

public class ProcessManager:IDisposable 
{ 
    List<Process> processes=new List<Process>(); 

    public Process Start(ProcessStartInfo info) 
    { 
     var newProcess = Process.Start(info); 
     newProcess.EnableRaisingEvents = true 
     processes.Add(newProcess); 
     newProcess.Exited += (sender, e) => processes.Remove(newProcess); 
     return newProcess; 
    } 

    ~ProcessManager() 
    { 
     Dispose(false); 
    } 

    public void Dispose() 
    { 
     Dispose(true); 
     GC.SuppressFinalize(this); 
    } 

    protected virtual void Dispose(bool disposing) 
    { 
     foreach (var process in processes) 
     { 
      try 
      { 
       if (!process.HasExited) 
        process.Kill(); 
      } 
      catch{}      
     } 
    } 
} 
+0

Thật không may, khi tôi hầu như không giết quá trình trong ProcessExplorer quá trình này không có cơ hội để làm mã quyết toán. Vì vậy, exaple của bạn sẽ chỉ hoạt động khi quá trình cha mẹ chấm dứt chính xác. BTW, để chỉnh sửa ví dụ của bạn, bạn phải thêm dòng ** newProcess.EnableRaisingEvents = true; ** trước khi gán * Thoát * sự kiện. – LionSoft

+2

Như tôi đã nói, Windows không giết các tiến trình con khi quá trình cha mẹ chết. Không có cơ chế hệ điều hành nào để thực thi điều đó. Một đứa trẻ proces không thuộc về cha mẹ của nó. Nếu bạn muốn sinh ra các công việc xử lý được bảo đảm để được làm sạch khi một quá trình chết, bạn phải sử dụng các luồng. Bạn nói đúng về EnableRaisingEvents, đã sửa nó. –

+2

Có ít nhất hai cơ chế hệ điều hành để diệt các quá trình sinh sản: 1. Đính kèm quy trình con làm trình gỡ lỗi. 2. Sử dụng các đối tượng công việc với JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE cờ Nhưng tôi không thể làm việc cả hai phương pháp này cho đến nay. :( – LionSoft

3

Bạn có chú ý đến mã lỗi không? Lỗi 24 là ERROR_BAD_LENGTH, có nghĩa là 48 không phải là độ dài phù hợp của cấu trúc. Tôi nghĩ rằng đó là 44, nhưng bạn nên làm một sizeof để chắc chắn.

7

Để giết cây quá trình trên cửa sổ, chỉ được cung cấp quy trình gốc hoặc quá trình id, bạn sẽ cần phải đi bộ cây quá trình.

Để làm điều đó, bạn cần có cách để lấy id tiến trình gốc cho một quy trình nhất định.

using System; 
using System.Collections.Generic; 
using System.Text; 
using System.Threading; 
using System.Diagnostics; 
using System.Management; 

namespace KillProcessTree 
{ 

public static class MyExtensions 
{ 
    public static int GetParentProcessId(this Process p) 
    { 
     int parentId = 0; 
     try 
     { 
      ManagementObject mo = new ManagementObject("win32_process.handle='" + p.Id + "'"); 
      mo.Get(); 
      parentId = Convert.ToInt32(mo["ParentProcessId"]); 
     } 
     catch (Exception ex) 
     { 
      Console.WriteLine(ex.ToString()); 
      parentId = 0; 
     } 
     return parentId; 
    } 
} 

Một khi bạn có, thực sự giết cây không khó.

class Program 
{ 
    /// <summary> 
    /// Kill specified process and all child processes 
    /// </summary> 
    static void Main(string[] args) 
    { 
     if (args.Length < 1) 
     { 
      Console.WriteLine("Usage: KillProcessTree <pid>"); 
      return; 
     } 

     int pid = int.Parse(args[0]); 

     Process root = Process.GetProcessById(pid); 
     if (root != null) 
     { 
      Console.WriteLine("KillProcessTree " + pid); 

      var list = new List<Process>(); 
      GetProcessAndChildren(Process.GetProcesses(), root, list, 1); 

      // kill each process 
      foreach (Process p in list) 
      { 
       try 
       { 
        p.Kill(); 
       } 
       catch (Exception ex) 
       { 
        Console.WriteLine(ex.ToString()); 
       } 
      } 
     } 
     else 
     { 
      Console.WriteLine("Unknown process id: " + root); 
     } 
    } 

    /// <summary> 
    /// Get process and children 
    /// We use postorder (bottom up) traversal; good as any when you kill a process tree </summary> 
    /// </summary> 
    /// <param name="plist">Array of all processes</param> 
    /// <param name="parent">Parent process</param> 
    /// <param name="output">Output list</param> 
    /// <param name="indent">Indent level</param> 
    private static void GetProcessAndChildren(Process[] plist, Process parent, List<Process> output, int indent) 
    { 
     foreach (Process p in plist) 
     { 
      if (p.GetParentProcessId() == parent.Id) 
      { 
       GetProcessAndChildren(plist, p, output, indent + 1); 
      } 
     } 
     output.Add(parent); 
     Console.WriteLine(String.Format("{0," + indent*4 + "} {1}", parent.Id, parent.MainModule.ModuleName)); 
    } 
} 
} // namespace 
8

Tôi đã thử mã ở trên và thực sự, nó không hoạt động, phàn nàn về kích thước kém.Lý do cho điều này là cấu trúc được sử dụng thay đổi kích thước tùy thuộc vào nền tảng máy chủ; đoạn mã gốc (nhìn thấy trên một tá trang web) giả sử một ứng dụng 32 bit.

Chuyển cấu trúc này (lưu ý các thành viên thay đổi kích thước IntPtr) và nó sẽ hoạt động. Ít nhất là nó đã làm cho tôi.

[StructLayout(LayoutKind.Sequential)] 
struct JOBOBJECT_BASIC_LIMIT_INFORMATION 
{ 
    public Int64 PerProcessUserTimeLimit; 
    public Int64 PerJobUserTimeLimit; 
    public Int16 LimitFlags; 
    public UIntPtr MinimumWorkingSetSize; 
    public UIntPtr MaximumWorkingSetSize; 
    public Int16 ActiveProcessLimit; 
    public Int64 Affinity; 
    public Int16 PriorityClass; 
    public Int16 SchedulingClass; 
}