পিতামাতার প্রক্রিয়াটি হত্যা করা হলে শিশু প্রক্রিয়াটি হত্যা করুন


156

আমি System.Diagnostics.Processআমার অ্যাপ্লিকেশন থেকে ক্লাস ব্যবহার করে নতুন প্রক্রিয়া তৈরি করছি ।

আমি চাই যখন / আমার অ্যাপ্লিকেশনটি ক্র্যাশ হয়ে গেছে তখন এই প্রক্রিয়াগুলি হত্যা করা হবে। তবে আমি যদি টাস্ক ম্যানেজারের কাছ থেকে আমার আবেদনটি হত্যা করি তবে শিশু প্রক্রিয়াগুলি হত্যা করা হয় না।

সন্তানের প্রক্রিয়াগুলি পিতামাতার প্রক্রিয়ার উপর নির্ভরশীল করার কোনও উপায় আছে কি?

উত্তর:


176

এই ফোরাম থেকে , 'জোশ' এর ক্রেডিট।

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

এমএসডিএন এই পতাকাটিকে এইভাবে সংজ্ঞায়িত করে:

কাজের শেষ হ্যান্ডেলটি বন্ধ হয়ে গেলে কাজের সাথে যুক্ত সমস্ত প্রক্রিয়া বন্ধ হয়ে যায়।

একবার এই ক্লাসটি সেটআপ হয়ে গেলে ... আপনাকে কেবলমাত্র প্রতিটি শিশু প্রক্রিয়াটি কাজের সাথে নিবন্ধিত করতে হবে। উদাহরণ স্বরূপ:

[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);


6
দুর্ভাগ্যক্রমে, আমি এটি 64 বিট মোডে চালাতে পারিনি। এখানে আমি একটি কাজের উদাহরণ পোস্ট করেছি, যা এর উপর ভিত্তি করে।
আলেকজান্ডার ইয়েজুতভ

2
@ ম্যাট হাওলস - কোথা থেকে Win32.CloseHandleউদ্ভূত? এটি কী কার্নেল 32.dll থেকে আমদানি করা হয়েছে? সেখানে একটি মিলে যাওয়া স্বাক্ষর রয়েছে, তবে আপনি অন্যান্য এপিআই ফাংশনের মতো এটি সুস্পষ্টভাবে আমদানি করছেন না।
এসোটেরিক পর্দার নাম

6
64 বিট মোডে অ্যাপসের জন্য -> stackoverflow.com/a/5976162 ভিস্তা / Win7 সমস্যার জন্য -> social.msdn.microsoft.com/forums/en-US/windowssecurity/thread/...
hB0

3
ঠিক আছে, এমএসডিএন বলেছেন: JOB_OBJECT_LIMIT_KILLटका_JOB_CLOSE পতাকাটির জন্য একটি JOBOBJECT_EXTENDED_LIMIT_INFORMATION কাঠামো ব্যবহার করা দরকার।
সার্জ

54

এই উত্তরটি @ ম্যাট হাওলসের দুর্দান্ত উত্তর প্লাস অন্যদের সাথে শুরু হয়েছিল (নীচের কোডের লিঙ্কগুলি দেখুন)। উন্নতি:

  • 32-বিট এবং 64-বিট সমর্থন করে।
  • @ ম্যাট হাওলসের উত্তরে কিছু সমস্যা সমাধান করা হয়েছে:
    1. ছোট স্মৃতি ফুটো extendedInfoPtr
    2. 'Win32' সংকলন ত্রুটি, এবং
    3. স্ট্যাক-ভারসাম্যহীন ব্যতিক্রম আমি কলটিতে পেয়েছি CreateJobObject(উইন্ডোজ 10, ভিজ্যুয়াল স্টুডিও 2015, 32-বিট ব্যবহার করে)।
  • কাজের নাম দিন, সুতরাং যদি আপনি সিসইন্টার্নাল ব্যবহার করেন তবে উদাহরণস্বরূপ, আপনি এটি সহজেই খুঁজে পেতে পারেন।
  • কিছুটা সহজ এপিআই এবং কম কোড রয়েছে।

এই কোডটি কীভাবে ব্যবহার করবেন তা এখানে:

// Get a Process object somehow.
Process process = Process.Start(exePath, args);
// Add the Process to ChildProcessTracker.
ChildProcessTracker.AddProcess(process);

উইন্ডোজ 7 সমর্থন করার জন্য প্রয়োজন:

আমার ক্ষেত্রে, আমার উইন্ডোজ 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-বিট উভয় সংস্করণকে প্রোগ্রামগতভাবে একে অপরের সাথে পরিচালিত এবং নেটিভ সংস্করণগুলির (সামগ্রিক আকারের পাশাপাশি প্রতিটি সদস্যের অফসেট) তুলনা করে পরীক্ষা করেছি।

আমি উইন্ডোজ 7, ​​8 এবং 10 এ এই কোডটি পরীক্ষা করেছি।


জব হ্যান্ডেল বন্ধ করার কি ??
ফ্র্যাঙ্ক কি।

@FrankQ। আমাদের প্রক্রিয়াটি শেষ হয়ে গেলে উইন্ডোজকে আমাদের জন্য_জব্যান্ডকে বন্ধ করে দেওয়া গুরুত্বপূর্ণ, কারণ আমাদের প্রক্রিয়াটি অপ্রত্যাশিতভাবে শেষ হতে পারে (যেমন ক্র্যাশ করে বা ব্যবহারকারী যদি টাস্ক ম্যানেজার ব্যবহার করে)। S_jobHandle এর উপর আমার মন্তব্য দেখুন।
রন

এটা আমার জন্য কাজ করছে। আমি জিজ্ঞাসা করতে পারি যে JOB_OBJECT_LIMIT_BREAKAWAY_OK পতাকা ব্যবহার সম্পর্কে আপনার মতামত কী? docs.microsoft.com/en-us/windows/desktop/api/winnt/...
Yiping

1
@ আইপিং এটিকে মনে হচ্ছে CREATE_BREAKAWAY_FROM_JOB আপনার সন্তানের প্রক্রিয়াটিকে এমন একটি প্রক্রিয়া তৈরি করতে দেয় যা আপনার মূল প্রক্রিয়াটিকে ছাপিয়ে যেতে পারে। ওপি যা চেয়েছিল তার চেয়ে এটি আলাদা প্রয়োজন। পরিবর্তে, যদি আপনি নিজের আসল প্রক্রিয়াটিকে দীর্ঘস্থায়ী প্রক্রিয়া (এবং চাইল্ডপ্রসেসট্র্যাকার ব্যবহার না করেন) তৈরি করতে পারেন তবে তা সহজ ler
রন

@ রন আপনি কি একটি ওভারলোড যোগ করতে পারেন, এটি কেবল প্রক্রিয়া হ্যান্ডেলটি গ্রহণ করে পুরো প্রক্রিয়াটি নয়?
জান্নিক

47

এই পোস্টটি @ ম্যাট হাওলসের জবাব বাড়ানোর উদ্দেশ্যে করা হয়েছে, বিশেষত যারা ভিস্তা বা উইন 7 এর অধীনে জব অবজেক্টগুলি ব্যবহার করে সমস্যায় পড়েছেন তাদের জন্য , বিশেষত যদি আপনি AssignProcessToJobObject কল করার সময় অ্যাক্সেস অস্বীকার ত্রুটি ('5') পেয়ে থাকেন।

TL; ড

ভিস্তা এবং উইন 7 এর সাথে সামঞ্জস্যতা নিশ্চিত করতে, .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>

মনে রাখবেন যে আপনি যখন ভিজ্যুয়াল স্টুডিও 2012-এ নতুন ম্যানিফেস্ট যুক্ত করবেন এটিতে উপরের স্নিপেটটি ইতিমধ্যে থাকবে সুতরাং আপনার এটি শ্রবণ থেকে অনুলিপি করার দরকার নেই। এটি উইন্ডোজ 8 এর জন্য একটি নোডও অন্তর্ভুক্ত করবে।

সম্পূর্ণ ব্যাখ্যা

আপনি যে প্রক্রিয়াটি শুরু করছেন তা ইতিমধ্যে অন্য কোনও কাজের সাথে যুক্ত থাকলে আপনার চাকরি সমিতি কোনও অ্যাক্সেস প্রত্যাখ্যান ত্রুটির সাথে ব্যর্থ হবে। প্রোগ্রামের সামঞ্জস্য সহকারী প্রবেশ করুন, যা উইন্ডোজ ভিস্তার মধ্যে শুরু করে, তার নিজস্ব কাজগুলিতে সমস্ত ধরণের প্রক্রিয়া বরাদ্দ করবে।

ভিস্তার মধ্যে আপনি কেবল অ্যাপ্লিকেশন ম্যানিফেস্টিটি অন্তর্ভুক্ত করে আপনার অ্যাপ্লিকেশনটিকে পিসিএ থেকে বাদ দেওয়ার জন্য চিহ্নিত করতে পারেন। ভিজ্যুয়াল স্টুডিওগুলি NET অ্যাপ্লিকেশনগুলির জন্য স্বয়ংক্রিয়ভাবে এটি করছে বলে মনে হয় আপনি সেখানে ভাল আছেন।

একটি সাধারণ ম্যানিফেস্ট এটি আর উইন 7 এ কাটবে না। [1] সেখানে, আপনাকে নির্দিষ্টভাবে উল্লেখ করতে হবে যে আপনি উইনি 7 এর সাথে আপনার ম্যানিফেস্টে থাকা ট্যাগের সাথে সামঞ্জস্যপূর্ণ। [2]

এটি আমাকে উইন্ডোজ ৮ সম্পর্কে চিন্তিত করতে পরিচালিত করেছিল আমাকে কী আবার আমার ম্যানিফেস্টে পরিবর্তন করতে হবে? স্পষ্টতই মেঘগুলিতে একটি বিরতি রয়েছে, উইন্ডোজ 8 এখন এক প্রক্রিয়াটিকে একাধিক কাজের সাথে যুক্ত করতে দেয়। [3] সুতরাং আমি এটি এখনও পরীক্ষা করে দেখিনি, তবে আমি কল্পনা করি যে আপনি যদি সমর্থিতOS তথ্যের সাথে কেবল একটি প্রকাশ অন্তর্ভুক্ত করেন তবে এই উন্মাদনাটি এখনই শেষ হয়ে যাবে।

টিপ ঘ : আপনি যদি ভিজুয়াল স্টুডিওর সাথে একটি নেট অ্যাপ বিকাশ করছেন তবে আমি যেমন ছিলাম, [4] আপনার অ্যাপ্লিকেশন ম্যানিফেস্টকে কীভাবে কাস্টমাইজ করতে হয় তার জন্য কয়েকটি দুর্দান্ত নির্দেশনা।

টিপ 2 : ভিজ্যুয়াল স্টুডিও থেকে আপনার অ্যাপ্লিকেশন চালু করার বিষয়ে সতর্কতা অবলম্বন করুন। আমি দেখতে পেলাম যে যথাযথ মেনিফেস্ট যুক্ত করার পরে, ভিজুয়াল স্টুডিও থেকে লঞ্চ করার সময় আমার পিসিএ নিয়ে সমস্যা ছিল, এমনকি আমি ডিবাগিং ছাড়াই স্টার্ট ব্যবহার করি না কেন। এক্সপ্লোরার থেকে আমার অ্যাপ্লিকেশন চালু করা কাজ করেছে। ম্যানুয়ালি রেজিস্ট্রি ব্যবহার করে পিসিএ থেকে বাদ দেওয়ার জন্য দেভেনভ যুক্ত করার পরে, ভিএস থেকে জব অবজেক্ট ব্যবহার করা অ্যাপ্লিকেশনগুলিও কাজ শুরু করে। [5]

টিপ 3 : আপনি যদি পিসিএর সমস্যা কিনা তা জানতে চান, কমান্ড লাইন থেকে আপনার অ্যাপ্লিকেশন চালু করার চেষ্টা করুন, বা প্রোগ্রামটি একটি নেটওয়ার্ক ড্রাইভে অনুলিপি করুন এবং সেখান থেকে চালনা করুন। পিসিএ স্বয়ংক্রিয়ভাবে সেই সমস্ত প্রসঙ্গে অক্ষম।

[1] http://blogs.msdn.com/b/cjacks/archive/2009/06/18/pca-changes-for-windows-7-how-to-tell-us-you-are-not-an -installer-নিতে-2 কারণ-We-পরিবর্তিত-নিয়ম-অন-you.aspx

[২] http://ayende.com/blog/4360/how-to-opt-out-of-program-compatibility-assistance

[3] http://msdn.microsoft.com/en-us/library/windows/desktop/ms681949(v=vs.85).aspx : "উইন্ডোজ 8 এ একাধিক কাজের সাথে একটি প্রক্রিয়া যুক্ত হতে পারে"

[4] আমি কীভাবে কোনও অ্যাপ্লিকেশনটিতে ভিএস ২০০8 ব্যবহার করে একটি অ্যাপ্লিকেশন ম্যানিফেস্ট এম্বেড করতে পারি?

[5] কীভাবে ভিজুয়াল স্টুডিও ডিবাগারটি কোনও কাজের উদ্দেশ্যে আমার প্রক্রিয়া শুরু করবেন?


2
এগুলি বিষয়টিতে দুর্দান্ত সংযোজন, ধন্যবাদ! আমি লিঙ্কগুলি সহ এই উত্তরের প্রতিটি দিক ব্যবহার করেছি।
জনি কফম্যান

16

এখানে একটি বিকল্প রয়েছে যা আপনার সন্তানের প্রক্রিয়াটি চালিত কোডের নিয়ন্ত্রণে থাকলে কারও জন্য কাজ করতে পারে। এই পদ্ধতির সুবিধাটি হ'ল এটির জন্য কোনও দেশীয় উইন্ডোজ কল প্রয়োজন হয় না।

মূল ধারণাটি হ'ল সন্তানের স্ট্যান্ডার্ড ইনপুটটি এমন একটি স্ট্রিমে পুনর্নির্দেশ করা যার অন্য প্রান্তটি পিতামাতার সাথে সংযুক্ত থাকে এবং পিতা-মাতার চলে যাওয়ার পরে সনাক্ত করতে সেই স্ট্রিমটি ব্যবহার করুন। আপনি যখন 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();
    }
}

এই পদ্ধতির গুহাত:

  1. প্রকৃত শিশু .exe যে প্রবর্তন করা উচিত তা অবশ্যই কনসোল অ্যাপ্লিকেশন হতে হবে যাতে এটি স্টিডিন / আউট / এররের সাথে সংযুক্ত থাকে। উপরোক্ত উদাহরণ হিসাবে, আমি সহজেই আমার বিদ্যমান অ্যাপ্লিকেশনটি অভিযোজিত করেছি যা একটি বার্তা পাম্প ব্যবহার করেছে (তবে একটি জিইউআই প্রদর্শন করে না) কেবলমাত্র একটি ছোট কনসোল প্রকল্প তৈরি করে যা আমার বিদ্যমান প্রজেক্টটি রেফারেন্স করে, আমার অ্যাপ্লিকেশন প্রসঙ্গে ইনস্ট্যান্ট করে এবং পদ্ধতিটির Application.Run()ভিতরে কল Mainকরে কনসোল .exe।

  2. প্রযুক্তিগতভাবে, পিতা-মাতার বাইরে চলে যাওয়ার পরে এটি কেবল শিশু প্রক্রিয়াটিকেই ইঙ্গিত দেয়, তাই পিতামাতার প্রক্রিয়াটি স্বাভাবিকভাবে উপস্থিত হয়েছিল বা ক্র্যাশ হয়েছে কিনা তা কাজ করবে তবে শিশু শাটডাউনটি সম্পাদন করার জন্য এটি এখনও শিশু প্রক্রিয়াগুলিতে নির্ভর করবে। এটি আপনি যা চান তা হতে পারে বা নাও হতে পারে ...


11

একটি উপায় হ'ল সন্তানের কাছে পিতামাতার প্রক্রিয়াটির পিআইডি পাস করা। নির্দিষ্ট পিড সহ প্রক্রিয়াটি বিদ্যমান থাকলে বা না থাকলে শিশু পর্যায়ক্রমে পলিং করবে। তা না হলে কেবল সরে যাবে।

আপনি প্যারেন্ট প্রসেসটি শেষ হওয়ার পরে শিশু পদ্ধতিতে প্রসেস.ওয়েটফরএক্সিট পদ্ধতিটিও ব্যবহার করতে পারেন তবে টাস্ক ম্যানেজারের ক্ষেত্রে এটি কাজ নাও করতে পারে।


আমি কীভাবে সন্তানের কাছে প্যারেন্ট পিআইডি পাস করতে পারি? কোন সিস্টেম সমাধান আছে? আমি শিশু প্রক্রিয়া বাইনারিগুলি সংশোধন করতে পারি না।
সাইবেরিয়ানগুই

1
ঠিক আছে, আপনি যদি শিশু প্রক্রিয়াটি পরিবর্তন করতে না পারেন তবে আপনি আমার পিআইডি পাস করার পরেও আমার সমাধানটি ব্যবহার করতে পারবেন না।
জিওরগি

@Idsa আপনি এটা কম্যান্ড লাইনের মাধ্যমে প্রেরণ করতে পারেন:Process.Start(string fileName, string arguments)
Distortum

2
পোলের পরিবর্তে আপনি প্রসেস ক্লাসে প্রস্থান ইভেন্টে প্রবেশ করতে পারেন।
রিচার্ডড

এটি ব্যবহার করে চেষ্টা করা হয়েছে তবে পিতামাতার প্রক্রিয়াটি যদি শিশুদের জীবিত থাকে তবে সর্বদা বের হয় না (কমপক্ষে আমার ক্ষেত্রে কোবল-> নেট)। সিসইন্টার্নালস প্রসেসএক্সপ্লোরার প্রক্রিয়াক্রমিক ক্রিয়াকলাপ পর্যবেক্ষণ করা সহজ।
ইভান ফেরের ভিলা

8

প্রোগ্রাম সমাপ্তিতে শিশু প্রক্রিয়াগুলি শেষ করার জন্য আরও একটি প্রাসঙ্গিক পদ্ধতি, সহজ এবং কার্যকর is আপনি তাদের পিতামাতার কাছ থেকে একটি ডিবাগার প্রয়োগ এবং সংযুক্ত করতে পারেন ; যখন পিতামাতার প্রক্রিয়াটি শেষ হয়, তখন শিশু প্রসেসগুলি ওএস দ্বারা মেরে ফেলা হবে। এটি সন্তানের কাছ থেকে পিতা-মাতার সাথে কোনও ডিবাগার সংযুক্ত করার উভয় পথে যেতে পারে (নোট করুন যে আপনি একবারে কেবল একটি ডিবাগার সংযুক্ত করতে পারেন)। আপনি এখানে বিষয় সম্পর্কে আরও তথ্য পেতে পারেন

এখানে আপনার কাছে একটি ইউটিলিটি ক্লাস রয়েছে যা একটি নতুন প্রক্রিয়া আরম্ভ করে এবং এটিতে একটি ডিবাগার সংযুক্ত করে। এটা তোলে থেকে অভিযোজিত হয়েছে এই পোস্টে রজার নাপ নামে এক মহিলাকে দ্বারা। একমাত্র প্রয়োজন উভয় প্রক্রিয়া একই সাক্ষ্য ভাগ করা প্রয়োজন। আপনি 64 বিট প্রক্রিয়া থেকে বিপরীতভাবে 32 বিট প্রক্রিয়াটি ডিবাগ করতে পারবেন না।

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");

8

আমি এই সমস্যার সমাধান সমাধানের সন্ধান করছিলাম যার জন্য পরিচালনাবিহীন কোডের প্রয়োজন নেই। আমি স্ট্যান্ডার্ড ইনপুট / আউটপুট পুনর্নির্দেশটিও ব্যবহার করতে পারিনি কারণ এটি একটি উইন্ডোজ ফর্ম অ্যাপ্লিকেশন।

আমার সমাধানটি ছিল পিতামাতার প্রসেসে একটি নামযুক্ত পাইপ তৈরি করা এবং তারপরে সন্তানের প্রক্রিয়াটিকে একই পাইপের সাথে সংযুক্ত করা। যদি পিতামাতার প্রক্রিয়াটি প্রস্থান করে তবে পাইপটি ভাঙ্গা হয়ে যায় এবং শিশু এটি সনাক্ত করতে পারে।

নীচে দুটি কনসোল অ্যাপ্লিকেশন ব্যবহার করে একটি উদাহরণ দেওয়া হল:

মাতা

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);
}

3

কয়েকটি প্রস্থান পরিস্থিতিতে হুক তৈরি করতে ইভেন্ট হ্যান্ডলারগুলি ব্যবহার করুন :

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

2

শুধু আমার 2018 সংস্করণ। এটি আপনার মেইন () পদ্ধতিটি বাদ দিয়ে দিন।

    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);
    }

4
আমি সন্দেহ করি যখন পিতামাতার প্রক্রিয়াটি হত্যা করা হয় তখন এটি কার্যকর হয় ।
বসন্তী 76

1

আমি দুটি বিকল্প দেখতে পাচ্ছি:

  1. কোন শিশু প্রক্রিয়াটি সঠিকভাবে শুরু করা যেতে পারে এবং আপনি যদি নিশ্চিত হন যে সেগুলি কেবলমাত্র আপনার মূল প্রক্রিয়া থেকেই শুরু হয়েছে, তবে আপনি কেবল নামেই তাদের অনুসন্ধান এবং তাদের হত্যা করার বিষয়ে বিবেচনা করতে পারেন।
  2. সমস্ত প্রক্রিয়াগুলির মাধ্যমে পর্যালোচনা করুন এবং পিতামাতার হিসাবে আপনার প্রক্রিয়া রয়েছে এমন প্রতিটি প্রক্রিয়াটি মেরে ফেলুন (আমার ধারণা আপনার প্রথমে শিশু প্রক্রিয়াটি হত্যা করা দরকার)। আপনি কীভাবে পিতামাতার প্রক্রিয়া আইডি পেতে পারেন তা এখানে ব্যাখ্যা করা হয়েছে।

1

আমি একটি শিশু প্রক্রিয়া পরিচালনা গ্রন্থাগার তৈরি করেছি যেখানে দ্বি-নির্দেশমূলক ডাব্লুসিএফ পাইপের কারণে পিতামাতার প্রক্রিয়া এবং শিশু প্রক্রিয়া পর্যবেক্ষণ করা হয়। যদি শিশু প্রক্রিয়াটি সমাপ্ত হয় বা পিতামাতার প্রক্রিয়া একে অপরকে সমাপ্ত করে তবে অবহিত হয়। একটি ডিবাগার সহায়তাও উপলব্ধ রয়েছে যা স্বয়ংক্রিয়ভাবে শুরু হওয়া শিশু প্রক্রিয়াতে ভিএস ডিবাগারকে সংযুক্ত করে

প্রকল্প এলাকা:

http://www.crawler-lib.net/child-processes

নিউগেট প্যাকেজগুলি:

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


0

জব কল করুন। প্রক্রিয়া শুরুর পরে আরও ভাল করার জন্য প্রসেস:

prc.Start();
job.AddProcess(prc.Handle);

সমাপ্তির আগে অ্যাডপ্রোসেস কল করার সময়, শিশু প্রক্রিয়াগুলি হত্যা করা হয় না। (উইন্ডোজ 7 এসপি 1)

private void KillProcess(Process proc)
{
    var job = new Job();
    job.AddProcess(proc.Handle);
    job.Close();
}

কলিং জব। প্রক্রিয়া শুরুর পরে অ্যাডপ্রোসেসটি এই অবজেক্টটি ব্যবহারের উদ্দেশ্যকে হত্যা করবে।
সার্জ

0

এখনও পর্যন্ত প্রস্তাবিত প্রচুর richশ্বর্যের আরও একটি সংযোজন ....

তাদের মধ্যে অনেকের সমস্যা হ'ল তারা নিয়মিত পদ্ধতিতে বন্ধ করার জন্য পিতামাতার এবং সন্তানের প্রক্রিয়া নির্ভর করে, যা বিকাশ চলাকালীন সর্বদা সত্য হয় না। আমি দেখতে পেয়েছি যে আমি যখনই ডিবাগারে পিতামাতার প্রক্রিয়াটি শেষ করি তখনই আমার শিশু প্রক্রিয়াটি প্রায়শই অনাথ হয়ে পড়েছিল, যার ফলে আমার সমাধানটি পুনর্নির্মাণের জন্য আমাকে টাস্ক ম্যানেজারের সাথে অনাথ প্রক্রিয়া (এস) হত্যা করতে হবে।

সমাধান: শিশু প্রক্রিয়াটির কমান্ডলাইনে (বা এমনকি কম আক্রমণাত্মক, পরিবেশের ভেরিয়েবলগুলিতে) প্যারেন্ট প্রসেস আইডিটি পাস করুন।

মূল প্রক্রিয়াতে, প্রক্রিয়া আইডি নিম্নলিখিত হিসাবে উপলব্ধ:

  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();
}

উত্পাদনের কোডে এটি গ্রহণযোগ্য অনুশীলন কিনা তা নিয়ে আমি দুজনের মনে। একদিকে, এটি কখনই ঘটবে না। তবে অন্যদিকে, এর অর্থ একটি প্রক্রিয়া পুনরায় আরম্ভ এবং একটি উত্পাদন সার্ভার পুনরায় বুট করার মধ্যে পার্থক্য হতে পারে। এবং যা কখনই ঘটে না তা প্রায়ই ঘটে।

এবং নিয়মিত শাটডাউন সমস্যাগুলি ডিবাগ করার সময় এটি অবশ্যই কার্যকর।

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.