هل هناك طريقة للتأكد من إنهاء عملية الخلفية التي أنشأها برنامجي عند انتهاء العملية؟

StackOverflow https://stackoverflow.com/questions/632148

  •  08-07-2019
  •  | 
  •  

سؤال

في الأساس، تعمل العملية الفرعية إلى أجل غير مسمى حتى يتم إيقافها في الخلفية، وأريد تنظيفها عندما ينتهي برنامجي لأي سبب من الأسباب، على سبيل المثال.عبر مدير المهام

لدي حاليًا فترة من الوقت (Process.GetProcessesByName("ParentProcess").Count() > 0) للتكرار والخروج إذا لم تكن العملية الأصلية قيد التشغيل، ولكنها تبدو هشة جدًا، وإذا أردت أن تعمل تحت مصحح الأخطاء في Visual الاستوديو يجب أن أضيف "ParentProcess.vshost" أو شيء من هذا القبيل.

هل هناك أي طريقة للتأكد من انتهاء العملية الفرعية دون مطالبة العملية الفرعية بمعرفة العملية الأصلية؟أفضّل الحل في التعليمات البرمجية المُدارة، ولكن إذا لم يكن هناك حل، فيمكنني استدعاء PInvoc.

يحرر:يبدو تمرير PID حلاً أكثر قوة، ولكن من أجل الفضول، ماذا لو لم تكن العملية الفرعية هي الكود الخاص بي ولكن بعض ملفات exe التي ليس لدي سيطرة عليها؟هل هناك طريقة للحماية من احتمال إنشاء عمليات فرعية معزولة؟

هل كانت مفيدة؟

المحلول

إذا كانت العملية الفرعية عبارة عن رمز خاص بك، فيمكنك تمرير معرف عملية العملية الأصلية إليها عند تشغيلها.يمكن للعملية الفرعية بعد ذلك جلب العملية باستخدام Process.GetProcessById والاشتراك فيها Exited حدث مع معالج يقوم بإيقاف بقية العملية (الفرعية) بأمان.لاحظ أنك بحاجة إلى ضبط EnableRaisingEvents الممتلكات في عملية true.

نصائح أخرى

إذا عملية طفل لا التعليمات البرمجية الخاصة بك يمكنك استخدام هذا الرمز لإيجاد وقتل جميع العمليات التابعة:

using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Runtime.InteropServices;

namespace Util {
    public static class ProcessExtensions {
        public static void KillDescendants(this Process processToNotKillYet) {
            foreach (var eachProcess in Process.GetProcesses()) {
                if (eachProcess.ParentPid() == processToNotKillYet.Id) {
                    eachProcess.KillTree();
                }
            }
        }

        public static void KillTree(this Process processToKill) {
            processToKill.KillDescendants();
            processToKill.Kill();
        }

        public static PROCESS_BASIC_INFORMATION Info(this Process process) {
            var processInfo = new PROCESS_BASIC_INFORMATION();
            try {
                uint bytesWritten;
                NtQueryInformationProcess(process.Handle,
                                          0,
                                          ref processInfo,
                                          (uint)Marshal.SizeOf(processInfo),
                                          out bytesWritten); // == 0 is OK
            }
            catch (Win32Exception e) {
                if (!e.Message.Equals("Access is denied")) throw;
            }

            return processInfo;
        }

        public static int ParentPid(this Process process) {
            return process.Info().ParentPid;
        }

        [DllImport("ntdll.dll")]
        private static extern int NtQueryInformationProcess(
            IntPtr hProcess,
            int processInformationClass /* 0 */,
            ref PROCESS_BASIC_INFORMATION processBasicInformation,
            uint processInformationLength,
            out uint returnLength);

        [StructLayout(LayoutKind.Sequential)]
        public struct PROCESS_BASIC_INFORMATION {
            public int ExitStatus;
            public int PebBaseAddress;
            public int AffinityMask;
            public int BasePriority;
            public int Pid;
            public int ParentPid;
        }
    }
}

وهذا المصطلح الشائع لمثل هذه العملية الطفل في عملية اليتيم . راجع مقالة مرتبطة لبعض الحلول الممكنة.

وهنا رمز المصدر لتطبيق أداة صغيرة بنيت (هو على أساس حل آلان هينسيل، التي وجدتها مفيدة جدا).

ويطلق عليه ChildrenProcessKiller وأنه هو مراقب التي تمكن من قتل جميع عملية المتحدرين من عملية الأصل بالنظر عندما يتم إنهاء عملية الأصل (حتى إذا تعطل عملية الأصل)

والاستعمال:

ChildrenProcessKiller.exe parentProcessId

تحذير: يتم توفير هذا الرمز "كما هي"، وأنه قد قتل أطفال الصغار ؛-)

وChildrenProcessKiller.cs

using System;
using System.Collections.Generic;
using System.Diagnostics;

namespace ChildrenProcessKiller
{
  static class ChildrenProcessKiller
  {
    [STAThread]
    static void Main(string[] args)
    {
      Application.EnableVisualStyles();
      Application.SetCompatibleTextRenderingDefault(false);

      string message = "This is a watcher that enables to kill all descendants process of a given parent process\n";
      message += "when the parent process exits (even if the parent process crashes) \n\n";
      message += "Usage : " + Application.ExecutablePath + " parentProcessId";

      if (args.Length != 1)
      {
        MessageBox.Show(message);
        System.Environment.Exit(1);
      }

      int parentProcessId;
      if (!Int32.TryParse(args[0], out parentProcessId))
      {
        MessageBox.Show(message);
        System.Environment.Exit(1);
      }

      try
      {
        mParentProcess = Process.GetProcessById(parentProcessId);
      }
      catch (System.ArgumentException ex)
      {
        //Parent process cannot be found!
        System.Environment.Exit(2);
      }
      Run();
    }

    private static List<Process> mChildrenProcesses;
    private static Process mParentProcess;

    private static void Run()
    {
      int thisProcessId = Process.GetCurrentProcess().Id;
      while ( ! mParentProcess.HasExited )
      {
        RefreshChildrenProcesses();
        System.Threading.Thread.Sleep(1000);
      }

      foreach (Process childProcess in mChildrenProcesses)
      {
        if ((!childProcess.HasExited) && (childProcess.Id != thisProcessId))
        {
          KillGracefullyThenViolently(childProcess);
        }
      }
    }

    private static void KillGracefullyThenViolently(Process process)
    {
      if (process.HasExited)
        return;

      try
      {
        process.CloseMainWindow();
      }
      catch (PlatformNotSupportedException)
      {}
      catch (InvalidOperationException)
      {}//do nothing : this app is meant to be "unstoppable", unless the parent process has exited

      for (int i = 0; i < 15; i++)
      {
        System.Threading.Thread.Sleep(100);
        if (process.HasExited)
          return;
      }

      try
      {
        process.Kill();
      }
      catch (System.ComponentModel.Win32Exception)
      {}
      catch(NotSupportedException)
      {}
      catch(InvalidOperationException)
      {} //same comment here
    }

    private static void RefreshChildrenProcesses()
    {
      if (mParentProcess.HasExited)
        return;
      List<Process> newChildren;
      try
      {
        newChildren = Utils.ProcessTree.GetProcessDescendants(mParentProcess);
        mChildrenProcesses = newChildren;
      }
      catch (System.Exception ex)
      {
        ; 
      }
    }


  }
}

وProcessTree.cs

using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Collections.Generic;
using System.IO;
using System.Windows.Forms;

namespace Utils
{
  public static class ProcessTree
  {

    public static List<Process> GetProcessDescendants(Process process)
    {
      List<Process> result = new List<Process>();
      foreach (Process eachProcess in Process.GetProcesses())
      {
        if (ParentPid(eachProcess) == process.Id)
        {
          result.Add(eachProcess);
        }
      }
      return result;
    }

    public static void KillDescendants(Process processToNotKillYet) 
    {
      foreach (Process eachProcess in Process.GetProcesses()) 
      {
        if (ParentPid(eachProcess) == processToNotKillYet.Id) 
        {
          if (eachProcess.Id != Process.GetCurrentProcess().Id)
            KillTree(eachProcess);
        }
      }
    }

    public static void KillTree(Process processToKill) 
    {
      KillDescendants(processToKill);
      processToKill.Kill();
    }

    public static PROCESS_BASIC_INFORMATION Info(Process process) 
    {
      PROCESS_BASIC_INFORMATION processInfo = new PROCESS_BASIC_INFORMATION();
      try
      {
        uint bytesWritten;
        NtQueryInformationProcess(process.Handle,
                        0,
                        ref processInfo,
                        (uint)Marshal.SizeOf(processInfo),
                        out bytesWritten); // == 0 is OK
      }
      catch (Win32Exception e) 
      {
        if (!e.Message.Equals("Access is denied")) throw;
      }

      return processInfo;
    }

    public static int ParentPid(Process process) 
    {
      return Info(process).ParentPid;
    }

    [DllImport("ntdll.dll")]
    private static extern int NtQueryInformationProcess(
      IntPtr hProcess,
      int processInformationClass /* 0 */,
      ref PROCESS_BASIC_INFORMATION processBasicInformation,
      uint processInformationLength,
      out uint returnLength);

    [StructLayout(LayoutKind.Sequential)]
    public struct PROCESS_BASIC_INFORMATION 
    {
      public int ExitStatus;
      public int PebBaseAddress;
      public int AffinityMask;
      public int BasePriority;
      public int Pid;
      public int ParentPid;
    }
  }
}

وتمرير معرف العملية الأم كمعلمة سطر الأوامر لعملية طفل.

في عملية استخدام الأطفال الحصول على عملية عن الهوية والاشتراك في انها حالة خروج أو إنشاء موضوع والدعوة إلى Process.WaitForExit

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top