Закрыть процесс Когда мое консольное приложение закрыто [дубликат]

Аналогичное решение для @gcedo , но без необходимости добавления промежуточного содержимого, чтобы подтолкнуть нижний колонтитул. Мы можем просто добавить margin-top: auto в нижний колонтитул, и он будет сдвинут в нижнюю часть страницы независимо от его высоты или высоты содержимого выше.

body {дисплей: flex; flex-direction: column; min-height: 100vh; Маржа: 0; } .content {padding: 50px; фон: красный; } .footer {margin-top: auto; обивка: 10px; фон: зеленый; } & lt; div class = "content" & gt; некоторый контент здесь & lt; / div & gt; & lt; footer class = "footer" & gt; некоторый контент & lt; / footer & gt;

128
задан rahulroy9202 27 November 2014 в 13:09
поделиться

12 ответов

Из этот форум , кредит «Джошу».

Application.Quit() и Process.Kill() являются возможными решениями, но оказались ненадежными. Когда ваше основное приложение умирает, у вас все еще остаются дочерние процессы. Мы действительно хотим, чтобы дочерние процессы умирали, как только основной процесс умирает.

Решение заключается в использовании «объектов задания» http://msdn.microsoft.com/en- us / 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;

Ключевым моментом здесь является настройка объект задания должным образом. В конструкторе я устанавливаю «пределы» на 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);
145
ответ дан Mariusz Jamro 15 August 2018 в 18:29
поделиться

вызывать 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();
}
0
ответ дан Alexey 15 August 2018 в 18:29
поделиться
  • 1
    Вызов job.AddProcess после запуска процесса убьет цель использования этого объекта. – SerG 19 March 2014 в 12:02

Я вижу два варианта:

  1. Если вы точно знаете, какой дочерний процесс можно запустить, и вы уверены, что они начинаются только с вашего основного процесса, тогда вы можете просто искать их имя и убить их.
  2. Итерация через все процессы и уничтожение каждого процесса, который имеет ваш процесс как родителя (я думаю, вам нужно сначала убить дочерние процессы). Здесь объясняется, как вы можете получить идентификатор родительского процесса.
41
ответ дан Community 15 August 2018 в 18:29
поделиться
  • 1
    Это отличные дополнения к этой теме, спасибо! Я использовал каждый аспект этого ответа, включая ссылки. – Johnny Kauffman 11 August 2014 в 18:51

Один из способов - передать PID родительского процесса ребенку. Ребенок будет периодически опроса, если процесс с указанным pid существует или нет.

Вы также можете использовать метод Process.WaitForExit в методе child, чтобы получать уведомления о завершении родительского процесса, но он может не работать в случае диспетчера задач .

8
ответ дан Giorgi 15 August 2018 в 18:29
поделиться
  • 1
    Как передать родительский PID ребенку? Есть ли системное решение? Я не могу изменить двоичные файлы дочерних процессов. – SiberianGuy 27 July 2010 в 12:17
  • 2
    Если вы не можете изменить дочерний процесс, вы не сможете использовать мое решение, даже если вы передадите ему PID. – Giorgi 27 July 2010 в 12:19
  • 3
    @Idsa Вы можете передать его через командную строку: Process.Start(string fileName, string arguments) – Steve Taylor 17 March 2012 в 07:29
  • 4
    Вместо опроса вы можете подключиться к событию Exit в классе процессов. – RichardOD 4 September 2012 в 22:08
  • 5
    Пробовал, но родительский процесс не всегда выходит, если у него есть дети (по крайней мере, в моем случае Cobol- & gt; NET). Легко проверить просмотр иерархии процессов в Sysinternals ProcessExplorer. – Ivan Ferrer Villa 23 October 2012 в 13:03

Используйте обработчики событий, чтобы сделать крючки в нескольких сценариях выхода:

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(); };
2
ответ дан Justin Harris 15 August 2018 в 18:29
поделиться

Существует еще один подходящий метод, простой и эффективный, для завершения дочерних процессов при завершении программы. Вы можете реализовать и приложить отладчик к ним от родителя; когда заканчивается родительский процесс, дочерние процессы будут убиты ОС. Он может пойти в обоих направлениях с отладчиком от родителя от дочернего элемента (обратите внимание, что вы можете прикреплять только один отладчик одновременно). Здесь вы можете найти дополнительную информацию по этому вопросу . .

Здесь у вас есть класс утилиты, который запускает новый процесс и присоединяет к нему отладчик. Он был адаптирован из этого поста Роджером Кнаппом. Единственное требование состоит в том, что оба процесса должны иметь одинаковую битту. Вы не можете отлаживать 32-битный процесс из 64-битного процесса или наоборот.

public class ProcessRunner
{
    #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

    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?
        }
    }
}

Использование:

    new ProcessRunner().StartProcess("c:\\Windows\\system32\\calc.exe");
7
ответ дан Marco Regueira 15 August 2018 в 18:29
поделиться

Вот альтернатива, которая может работать для некоторых, когда вы контролируете код, выполняемый дочерним процессом. Преимущество такого подхода заключается в том, что он не требует каких-либо собственных вызовов 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 s из стандартный поток ввода всегда будет возвращаться с по меньшей мере 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();
    }
}

Предостережения для этого подхода:

  1. фактический запуск дочернего .exe должен быть консольным приложением, поэтому он остается прикрепленным к stdin / out / err. Как и в приведенном выше примере, я легко адаптировал свое существующее приложение, которое использовало насос сообщений (но не отображало графический интерфейс), просто создав крошечный консольный проект, который ссылался на существующий проект, создавая экземпляр моего контекста приложения и вызывая Application.Run() внутри Main метод console.exe.
  2. Технически это просто сигнализирует о дочернем процессе, когда родительский элемент выходит из него, поэтому он будет работать, если родительский процесс завершился нормально или разбился, но все еще до дочерних процессов, чтобы выполнить свое собственное завершение работы. Это может быть или не быть тем, что вы хотите ...
15
ответ дан mbaynton 15 August 2018 в 18:29
поделиться
  • 1
    Я использовал этот трюк несколько раз. Очень хорошо работает – Esben Skov Pedersen 6 December 2012 в 11:26
  • 2
    Пример немного запутан, но стратегия прочная и не требует PInvoke crud. – piedar 1 February 2017 в 23:11

Я искал решение этой проблемы, которая не требовала неуправляемого кода. Я также не смог использовать стандартное перенаправление ввода / вывода, потому что это было приложение Windows Forms.

Моим решением было создать именованный канал в родительском процессе, а затем подключить дочерний процесс к тому же каналу. Если родительский процесс выходит из строя, тогда труба становится сломанной, и ребенок может обнаружить это.

Ниже приведен пример использования двух консольных приложений:

Родитель

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);
}
2
ответ дан Psychomatic Complexity 15 August 2018 в 18:29
поделиться

Этот ответ начался с превосходного ответа @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 требуется:

В моем случае мне не нужно было поддерживать Windows 7, поэтому у меня есть простая проверка наверху статического конструктора ниже.

/// <summary>
/// Allows processes to be automatically killed if this parent process unexpectedly quits.
/// This feature requires Windows 8 or greater. On Windows 7, nothing is done.</summary>
/// <remarks>References:
///  https://stackoverflow.com/a/4657392/386091
///  https://stackoverflow.com/a/9164742/386091 </remarks>
public static class ChildProcessTracker
{
    /// <summary>
    /// Add the process to be tracked. If our current process is killed, the child processes
    /// that we are tracking will be automatically killed, too. If the child process terminates
    /// first, that's fine, too.</summary>
    /// <param name="process"></param>
    public static void AddProcess(Process process)
    {
        if (s_jobHandle != IntPtr.Zero)
        {
            bool success = AssignProcessToJobObject(s_jobHandle, process.Handle);
            if (!success && !process.HasExited)
                throw new Win32Exception();
        }
    }

    static ChildProcessTracker()
    {
        // This feature requires Windows 8 or later. To support Windows 7 requires
        //  registry settings to be added if you are using Visual Studio plus an
        //  app.manifest change.
        //  https://stackoverflow.com/a/4232259/386091
        //  https://stackoverflow.com/a/9507862/386091
        if (Environment.OSVersion.Version < new Version(6, 2))
            return;

        // The job name is optional (and can be null) but it helps with diagnostics.
        //  If it's not null, it has to be unique. Use SysInternals' Handle command-line
        //  utility: handle -a ChildProcessTracker
        string jobName = "ChildProcessTracker" + Process.GetCurrentProcess().Id;
        s_jobHandle = CreateJobObject(IntPtr.Zero, jobName);

        var info = new JOBOBJECT_BASIC_LIMIT_INFORMATION();

        // This is the key flag. When our process is killed, Windows will automatically
        //  close the job handle, and when that happens, we want the child processes to
        //  be killed, too.
        info.LimitFlags = JOBOBJECTLIMIT.JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;

        var extendedInfo = new JOBOBJECT_EXTENDED_LIMIT_INFORMATION();
        extendedInfo.BasicLimitInformation = info;

        int length = Marshal.SizeOf(typeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION));
        IntPtr extendedInfoPtr = Marshal.AllocHGlobal(length);
        try
        {
            Marshal.StructureToPtr(extendedInfo, extendedInfoPtr, false);

            if (!SetInformationJobObject(s_jobHandle, JobObjectInfoType.ExtendedLimitInformation,
                extendedInfoPtr, (uint)length))
            {
                throw new Win32Exception();
            }
        }
        finally
        {
            Marshal.FreeHGlobal(extendedInfoPtr);
        }
    }

    [DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
    static extern IntPtr CreateJobObject(IntPtr lpJobAttributes, string name);

    [DllImport("kernel32.dll")]
    static extern bool SetInformationJobObject(IntPtr job, JobObjectInfoType infoType,
        IntPtr lpJobObjectInfo, uint cbJobObjectInfoLength);

    [DllImport("kernel32.dll", SetLastError = true)]
    static extern bool AssignProcessToJobObject(IntPtr job, IntPtr process);

    // Windows will automatically close any open job handles when our process terminates.
    //  This can be verified by using SysInternals' Handle utility. When the job handle
    //  is closed, the child processes will be killed.
    private static readonly IntPtr s_jobHandle;
}

public enum JobObjectInfoType
{
    AssociateCompletionPortInformation = 7,
    BasicLimitInformation = 2,
    BasicUIRestrictions = 4,
    EndOfJobTimeInformation = 6,
    ExtendedLimitInformation = 9,
    SecurityLimitInformation = 5,
    GroupInformation = 11
}

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

[Flags]
public enum JOBOBJECTLIMIT : uint
{
    JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE = 0x2000
}

[StructLayout(LayoutKind.Sequential)]
public struct IO_COUNTERS
{
    public UInt64 ReadOperationCount;
    public UInt64 WriteOperationCount;
    public UInt64 OtherOperationCount;
    public UInt64 ReadTransferCount;
    public UInt64 WriteTransferCount;
    public UInt64 OtherTransferCount;
}

[StructLayout(LayoutKind.Sequential)]
public 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;
}

Я тщательно тестировал 32-разрядную и 64-разрядную версии структур, программно сравнивая управляемые и родные версии друг с другом (общий размер, а также смещения для каждого члена).

Я тестировал этот код на Windows 7, 8 и 10.

28
ответ дан Ron 15 August 2018 в 18:29
поделиться
  • 1
    как насчет закрытия работы? – Frank Q. 3 November 2016 в 00:18
  • 2
    @FrankQ. Важно, чтобы Windows закрывала s_jobHandle для нас, когда наш процесс завершается, потому что наш процесс может неожиданно завершиться (например, сбой или пользователь использует диспетчер задач). Смотрите мой комментарий на s_jobHandle's. – Ron 10 November 2016 в 06:33
  • 3
  • 4

Я создал библиотеку управления дочерними процессами, где отслеживается родительский процесс и дочерний процесс из-за двунаправленного WCF-канала. Если либо дочерний процесс завершается, либо прерывается родительский процесс, он уведомляется. Существует также доступный помощник отладчика, который автоматически присоединяет отладчик VS к запущенному дочернему процессу

Сайт проекта:

http://www.crawler-lib.net/ дочерние процессы

Пакеты NuGet:

https://www.nuget.org/packages/ChildProcesses https: / /www.nuget.org/packages/ChildProcesses.VisualStudioDebug/

1
ответ дан Thomas Maierhofer 15 August 2018 в 18:29
поделиться
41
ответ дан Community 5 September 2018 в 17:38
поделиться
0
ответ дан Lenor 29 October 2018 в 01:48
поделиться
Другие вопросы по тегам:

Похожие вопросы: