Suspend Process in C# - c#

How do I suspend a whole process (like the Process Explorer does when I click Suspend) in C#.
I'm starting the Process with Process.Start, and on a certain event, I want to suspend the process to be able to do some investigation on a "snapshot" of it.

Here's my suggestion:
[Flags]
public enum ThreadAccess : int
{
TERMINATE = (0x0001),
SUSPEND_RESUME = (0x0002),
GET_CONTEXT = (0x0008),
SET_CONTEXT = (0x0010),
SET_INFORMATION = (0x0020),
QUERY_INFORMATION = (0x0040),
SET_THREAD_TOKEN = (0x0080),
IMPERSONATE = (0x0100),
DIRECT_IMPERSONATION = (0x0200)
}
[DllImport("kernel32.dll")]
static extern IntPtr OpenThread(ThreadAccess dwDesiredAccess, bool bInheritHandle, uint dwThreadId);
[DllImport("kernel32.dll")]
static extern uint SuspendThread(IntPtr hThread);
[DllImport("kernel32.dll")]
static extern int ResumeThread(IntPtr hThread);
[DllImport("kernel32", CharSet = CharSet.Auto,SetLastError = true)]
static extern bool CloseHandle(IntPtr handle);
private static void SuspendProcess(int pid)
{
var process = Process.GetProcessById(pid); // throws exception if process does not exist
foreach (ProcessThread pT in process.Threads)
{
IntPtr pOpenThread = OpenThread(ThreadAccess.SUSPEND_RESUME, false, (uint)pT.Id);
if (pOpenThread == IntPtr.Zero)
{
continue;
}
SuspendThread(pOpenThread);
CloseHandle(pOpenThread);
}
}
public static void ResumeProcess(int pid)
{
var process = Process.GetProcessById(pid);
if (process.ProcessName == string.Empty)
return;
foreach (ProcessThread pT in process.Threads)
{
IntPtr pOpenThread = OpenThread(ThreadAccess.SUSPEND_RESUME, false, (uint)pT.Id);
if (pOpenThread == IntPtr.Zero)
{
continue;
}
var suspendCount = 0;
do
{
suspendCount = ResumeThread(pOpenThread);
} while (suspendCount > 0);
CloseHandle(pOpenThread);
}
}

Thanks to Magnus
After including the Flags, I modified the code a bit to be an extension method in my project. I could now use
var process = Process.GetProcessById(param.PId);
process.Suspend();
Here is the code for those who might be interested.
public static class ProcessExtension
{
[DllImport("kernel32.dll")]
static extern IntPtr OpenThread(ThreadAccess dwDesiredAccess, bool bInheritHandle, uint dwThreadId);
[DllImport("kernel32.dll")]
static extern uint SuspendThread(IntPtr hThread);
[DllImport("kernel32.dll")]
static extern int ResumeThread(IntPtr hThread);
public static void Suspend(this Process process)
{
foreach (ProcessThread thread in process.Threads)
{
var pOpenThread = OpenThread(ThreadAccess.SUSPEND_RESUME, false, (uint)thread.Id);
if (pOpenThread == IntPtr.Zero)
{
break;
}
SuspendThread(pOpenThread);
}
}
public static void Resume(this Process process)
{
foreach (ProcessThread thread in process.Threads)
{
var pOpenThread = OpenThread(ThreadAccess.SUSPEND_RESUME, false, (uint)thread.Id);
if (pOpenThread == IntPtr.Zero)
{
break;
}
ResumeThread(pOpenThread);
}
}
}
I have a utility done which I use to generally suspend/kill/list a process. Full source is on Git

So really, what the other answer's are showing is suspending thread's in the process, there is no way to really suspend the process (i.e. in one call)....
A bit of a different solution would be to actually debug the target process which you are starting, see Mike Stall's blog for some advice how to implement this from a managed context.
If you implement a debugger, you will be able to scan memory or what other snap-shotting you would like.
However, I would like to point out, that technically, there is now way to really do this. Even if you do debugbreak a target debuggee process, another process on your system may inject a thread and will be given some ability to execute code regardless of the state of the target process (even let's say if it's hit a breakpoint due to an access violation), if you have all thread's suspended up to a super high suspend count, are currently at a break point in the main process thread and any other such presumed-frozen status, it is still possible for the system to inject another thread into that process and execute some instructions. You could also go through the trouble of modifying or replacing all of the entry point's the kernel usually calls and so on, but you've now entered the viscous arm's race of MALWARE ;)...
In any case, using the managed interfaces for debugging seems' a fair amount easier than p/invoke'ng a lot of native API call's which will do a poor job of emulating what you probably really want to be doing... using debug api's ;)

See this CodeProject article for the win32 basics : http://www.codeproject.com/KB/threads/pausep.aspx. This sample code makes use of the ToolHelp32 library from the SDK, so I would recommend turning this sample code into an unmanaged C++/CLI library with a simple interface like "SuspendProcess(uint processID).
Process.Start will return you a Process object, from which you can get the process id, and then pass this to your new library based on the above.
Dave

[DllImport("ntdll.dll", PreserveSig = false)]
public static extern void NtSuspendProcess(IntPtr processHandle);
static IntPtr handle;
string p = "";
foreach (Process item in Process.GetProcesses())
{
if (item.ProcessName == "GammaVPN")
{
p = item.ProcessName;
handle = item.Handle;
NtSuspendProcess(handle);
}
}
Console.WriteLine(p);
Console.WriteLine("done");

Related

.NET Runtime Error 80131506 - Passing Lambda to Native Function

So I'm getting this error that looks as though it's a corrupted garbage collection:
Application Crashes With "Internal Error In The .NET Runtime"
The full error is:
The process was terminated due to an internal error in the .NET Runtime at IP 71C571C8 (71B20000) with exit code 80131506.
It's running on:
Framework Version: v4.0.30319
This occurs inconsistently when running this function repeatedly:
public static int GetMdiTitledChildWindows(IntPtr parentWindow)
{
IntPtr mdiClient = FindWindowEx(parentWindow, IntPtr.Zero, MdiClient, "");
List<IntPtr> handles = new List<IntPtr>();
EnumChildWindows(mdiClient, (hwnd, param) =>
{
handles.Add(hwnd);
return true;
}, IntPtr.Zero);
int counter = 0;
foreach (IntPtr handle in handles)
{
StringBuilder builder = new StringBuilder();
GetWindowText(handle, builder, GetWindowTextLength(handle)+1);
if (builder.Length > 0)
{
counter++;
}
}
return counter;
}
Where FindWindowEx(), EnumChildWindows() and GetWindowText() are all p/invoke signatures defined similarly to this:
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool EnumChildWindows(IntPtr hwndParent, EnumWindowsProc lpEnumFunc, IntPtr lParam);
The error only seems to occur after I've run the method many times, however, this doesn't happen consistently. Sometimes it works, sometimes it doesn't.
Any suggestions on how to fix this?
So I solved my issue with the help of a generous benefactor on Discord.
The problem was that I was passing a lambda to a p/invoke as a delegate:
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool EnumChildWindows(IntPtr hwndParent, EnumWindowsProc lpEnumFunc, IntPtr lParam);
So every time the unmanaged WinAPI call called-back into my delegate, the GC had the opportunity to run, if it did, it collected my lambda causing this crash. This wouldn't necessarily happen, hence why my method worked most of the time and crashed inconsistently.
The solution was to add a reference to the lambda that would prevent the GC from collecting it (although I went whole-hog and made it a local function because belt and braces):
public static int GetMdiTitledChildWindows(IntPtr parentWindow)
{
IntPtr mdiClient = FindWindowEx(parentWindow, IntPtr.Zero, MdiClient, "");
List<IntPtr> handles = new List<IntPtr>();
bool addToList(IntPtr hwnd, IntPtr param)
{
handles.Add(hwnd);
return true;
}
EnumWindowsProc gcHolder = addToList;
EnumChildWindows(mdiClient, gcHolder, IntPtr.Zero);
int counter = 0;
foreach (IntPtr handle in handles)
{
int textLength = GetWindowTextLength(handle) + 1;
StringBuilder builder = new StringBuilder(textLength);
GetWindowText(handle, builder, textLength);
if (builder.Length > 0)
{
counter++;
}
}
return counter;
}
The application now works as expected.

Is it possible to start background processes with Process.Start? [duplicate]

I'm creating new processes using System.Diagnostics.Process class from my application. I want this processes to be killed when/if my application has crashed. But if I kill my application from Task Manager, child processes are not killed. Is there any way to make child processes dependent on parent process?
From this forum, credit to 'Josh'.
Application.Quit() and Process.Kill() are possible solutions, but have proven to be unreliable. When your main application dies, you are still left with child processes running. What we really want is for the child processes to die as soon as the main process dies.
The solution is to use "job objects" http://msdn.microsoft.com/en-us/library/ms682409(VS.85).aspx.
The idea is to create a "job object" for your main application, and register your child processes with the job object. If the main process dies, the OS will take care of terminating the child processes.
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);
}
}
Looking at the constructor ...
JOBOBJECT_BASIC_LIMIT_INFORMATION info = new JOBOBJECT_BASIC_LIMIT_INFORMATION();
info.LimitFlags = 0x2000;
The key here is to setup the job object properly. In the constructor I'm setting the "limits" to 0x2000, which is the numeric value for JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE.
MSDN defines this flag as:
Causes all processes associated with
the job to terminate when the last
handle to the job is closed.
Once this class is setup...you just have to register each child process with the job. For example:
[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);
This answer started with #Matt Howells' excellent answer plus others (see links in the code below). Improvements:
Supports 32-bit and 64-bit.
Fixes some problems in #Matt Howells' answer:
The small memory leak of extendedInfoPtr
The 'Win32' compile error, and
A stack-unbalanced exception I got in the call to CreateJobObject (using Windows 10, Visual Studio 2015, 32-bit).
Names the Job, so if you use SysInternals, for example, you can easily find it.
Has a somewhat simpler API and less code.
Here's how to use this code:
// Get a Process object somehow.
Process process = Process.Start(exePath, args);
// Add the Process to ChildProcessTracker.
ChildProcessTracker.AddProcess(process);
To support Windows 7 requires:
A simple app.manifest change as #adam smith describes.
Registry settings to be added if you are using Visual Studio.
In my case, I didn't need to support Windows 7, so I have a simple check at the
top of the static constructor below.
/// <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;
}
I carefully tested both the 32-bit and 64-bit versions of the structs by programmatically comparing the managed and native versions to each other (the overall size as well as the offsets for each member).
I've tested this code on Windows 7, 8, and 10.
This post is intended as an extension to #Matt Howells' answer, specifically for those who run into problems with using Job Objects under Vista or Win7, especially if you get an access denied error ('5') when calling AssignProcessToJobObject.
tl;dr
To ensure compatibility with Vista and Win7, add the following manifest to the .NET parent process:
<?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>
Note that when you add new manifest in Visual Studio 2012 it will contain the above snippet already so you do not need to copy it from hear. It will also include a node for Windows 8.
full explanation
Your job association will fail with an access denied error if the process you're starting is already associated with another job. Enter Program Compatibility Assistant, which, starting in Windows Vista, will assign all kinds of processes to its own jobs.
In Vista you can mark your application to be excluded from PCA by simply including an application manifest. Visual Studio seems to do this for .NET apps automatically, so you're fine there.
A simple manifest no longer cuts it in Win7. [1] There, you have to specifically specify that you're compatible with Win7 with the tag in your manifest. [2]
This led me to worry about Windows 8. Will I have to change my manifest once again? Apparently there's a break in the clouds, as Windows 8 now allows a process to belong to multiple jobs. [3] So I haven't tested it yet, but I imagine that this madness will be over now if you simply include a manifest with the supportedOS information.
Tip 1: If you're developing a .NET app with Visual Studio, as I was, here [4] are some nice instructions on how to customize your application manifest.
Tip 2: Be careful with launching your application from Visual Studio. I found that, after adding the appropriate manifest, I still had problems with PCA when launching from Visual Studio, even if I used Start without Debugging. Launching my application from Explorer worked, however. After manually adding devenv for exclusion from PCA using the registry, starting applications that used Job Objects from VS started working as well. [5]
Tip 3: If you ever want to know if PCA is your problem, try launching your application from the command line, or copy the program to a network drive and run it from there. PCA is automatically disabled in those contexts.
[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-take-2-because-we-changed-the-rules-on-you.aspx
[2] http://ayende.com/blog/4360/how-to-opt-out-of-program-compatibility-assistant
[3] http://msdn.microsoft.com/en-us/library/windows/desktop/ms681949(v=vs.85).aspx:
"A process can be associated with more than one job in Windows 8"
[4] How can I embed an application manifest into an application using VS2008?
[5] How to stop the Visual Studio debugger starting my process in a job object?
Here's an alternative that may work for some when you have control of the code the child process runs. The benefit of this approach is it doesn't require any native Windows calls.
The basic idea is to redirect the child's standard input to a stream whose other end is connected to the parent, and use that stream to detect when the parent has gone away. When you use System.Diagnostics.Process to start the child, it's easy to ensure its standard input is redirected:
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
And then, on the child process, take advantage of the fact that Reads from the standard input stream will always return with at least 1 byte until the stream is closed, when they will start returning 0 bytes. An outline of the way I ended up doing this is below; my way also uses a message pump to keep the main thread available for things other than watching standard in, but this general approach could be used without message pumps too.
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();
}
}
Caveats to this approach:
the actual child .exe that is launched must be a console application so it remains attached to stdin/out/err. As in the above example, I easily adapted my existing application that used a message pump (but didn't show a GUI) by just creating a tiny console project that referenced the existing project, instantiating my application context and calling Application.Run() inside the Main method of the console .exe.
Technically, this merely signals the child process when the parent exits, so it will work whether the parent process exited normally or crashed, but its still up to the child processes to perform its own shutdown. This may or may not be what you want...
There is another relevant method, easy and effective, to finish child processes on program termination. You can implement and attach a debugger to them from the parent; when the parent process ends, child processes will be killed by the OS. It can go both ways attaching a debugger to the parent from the child (note that you can only attach one debugger at a time). You can find more info on the subject here.
Here you have an utility class that launches a new process and attaches a debugger to it. It has been adapted from this post by Roger Knapp. The only requirement is that both processes need to share the same bitness. You cannot debug a 32bit process from a 64bit process or vice versa.
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
}
Usage:
new ProcessRunner().StartProcess("c:\\Windows\\system32\\calc.exe");
One way is to pass PID of parent process to the child. The child will periodically poll if the process with the specified pid exists or not. If not it will just quit.
You can also use Process.WaitForExit method in child method to be notified when the parent process ends but it might not work in case of Task Manager.
I was looking for a solution to this problem that did not require unmanaged code. I was also not able to use standard input/output redirection because it was a Windows Forms application.
My solution was to create a named pipe in the parent process and then connect the child process to the same pipe. If the parent process exits then the pipe becomes broken and the child can detect this.
Below is an example using two console applications:
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();
}
}
Child
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);
}
Use event handlers to make hooks on a few exit scenarios:
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(); };
Yet another addition to the abundant richness of solutions proposed so far....
The problem with many of them is that they rely upon the parent and child process to shut down in an orderly manner, which isn't always true when development is underway. I found that my child process was often being orphaned whenever I terminated the parent process in the debugger, which required me to kill the orphaned process(es) with Task Manager in order to rebuild my solution.
The solution: Pass the parent process ID in on the commandline (or even less invasive, in the environment variables) of the child process.
In the parent process, the process ID is available as:
Process.CurrentProcess().Id;
In the child process:
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();
};
I am of two minds as to whether this is acceptable practice in production code. On the one hand, this should never happen. But on the other hand, it may mean the difference between restarting a process, and rebooting a production server. And what should never happen often does.
And it sure is useful while debugging orderly shutdown problems.
Solution that worked for me:
When creating process add tag 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();
I see two options:
If you know exactly what child process could be started and you are sure they are only started from your main process, then you could consider simply searching for them by name and kill them.
Iterate through all processes and kill every process that has your process as a parent (I guess you need to kill the child processes first). Here is explained how you can get the parent process id.
I've made a child process management library where the parent process and the child process are monitored due a bidirectional WCF pipe. If either the child process terminates or the parent process terminates each other is notified.
There is also a debugger helper available which automatically attaches the VS debugger to the started child process
Project site:
http://www.crawler-lib.net/child-processes
NuGet Packages:
https://www.nuget.org/packages/ChildProcesses
https://www.nuget.org/packages/ChildProcesses.VisualStudioDebug/
Just my 2018 version.
Use it aside your Main() method.
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);
}
call job.AddProcess better to do after start of the process:
prc.Start();
job.AddProcess(prc.Handle);
When calling AddProcess before the terminate, child processes are not killed. (Windows 7 SP1)
private void KillProcess(Process proc)
{
var job = new Job();
job.AddProcess(proc.Handle);
job.Close();
}
I had the same problem. I was creating child processes that never got killed if my main app crashed. I had to destroy manually child processes when debugging. I found that there was no need to make the children somewhat depend on parent. In my main, I added a try catch to do a CleanUp() of child processes on exit.
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();
}
}
}
If you can catch situation, when your processes tree should be killed, since .NET 5.0 (.NET Core 3.0) you could use Process.Kill(bool entireProcessTree)

How does Thread.Abort() work?

We usually throw exception when invalid input is passed to a method or when a object is about to enter invalid state. Let's consider the following example
private void SomeMethod(string value)
{
if(value == null)
throw new ArgumentNullException("value");
//Method logic goes here
}
In the above example I inserted a throw statement which throws ArgumentNullException. My question is how does runtime manages to throw ThreadAbortException. Obviously it is not possible to use a throw statement in all the methods, even runtime manages to throw ThreadAbortException in our custom methods too.
I was wondering how do they do it?
I was curious to know what is happening behind the scenes, I opened a reflector to open Thread.Abort and end up with this
[MethodImplAttribute(MethodImplOptions.InternalCall)]
private extern void AbortInternal();//Implemented in CLR
Then I googled and found this How does ThreadAbortException really work. This link says that runtime posts APC through QueueUserAPC function and that's how they do the trick.
I wasn't aware of QueueUserAPC method I just gave a try to see whether it is possible with some code. Following code shows my try.
[DllImport("kernel32.dll")]
static extern uint QueueUserAPC(ApcDelegate pfnAPC, IntPtr hThread, UIntPtr dwData);
delegate void ApcDelegate(UIntPtr dwParam);
Thread t = new Thread(Threadproc);
t.Start();
//wait for thread to start
uint result = QueueUserAPC(APC, new IntPtr(nativeId), (UIntPtr)0);//returns zero(fails)
int error = Marshal.GetLastWin32Error();// error also zero
private static void APC(UIntPtr data)
{
Console.WriteLine("Callback invoked");
}
private static void Threadproc()
{
//some infinite loop with a sleep
}
If am doing something wrong forgive me, I have no idea how to do it. Again back to question, Can somebody with knowledge about this or part of CLR team explain how it works internally?
If APC is the trick runtime follows what am doing wrong here?
Are you sure you read the page you were pointing to? In the end it boils down to:
The call to Thread.Abort boils down to .NET setting a flag on a thread to be aborted and then checking that flag during certain points in the thread’s lifetime, throwing the exception if the flag is set.
To get your APC callback to work, you need a thread handle (which is not the same as the thread ID). I've also updated the attributes on the PInvokes.
Also keep in mind that the thread needs to be in an "alert-able" wait state in order for the APC to be called (which Thread.Sleep will give us). So if the thread is busy doing stuff, it may not be called.
[DllImport("kernel32.dll", EntryPoint = "GetCurrentThread", CallingConvention = CallingConvention.StdCall)]
public static extern IntPtr GetCurrentThread();
[DllImport("kernel32.dll", EntryPoint = "QueueUserAPC", CallingConvention = CallingConvention.StdCall, SetLastError = true)]
public static extern uint QueueUserAPC(ApcDelegate pfnAPC, IntPtr hThread, UIntPtr dwData);
[UnmanagedFunctionPointerAttribute(CallingConvention.StdCall)]
public delegate void ApcDelegate(UIntPtr dwParam);
[DllImport("kernel32.dll", EntryPoint = "DuplicateHandle", CallingConvention = CallingConvention.StdCall, SetLastError = true)]
public static extern bool DuplicateHandle([In] System.IntPtr hSourceProcessHandle, [In] System.IntPtr hSourceHandle, [In] System.IntPtr hTargetProcessHandle, out System.IntPtr lpTargetHandle, uint dwDesiredAccess, [MarshalAsAttribute(UnmanagedType.Bool)] bool bInheritHandle, uint dwOptions);
[DllImport("kernel32.dll", EntryPoint = "GetCurrentProcess", CallingConvention = CallingConvention.StdCall, SetLastError = true)]
public static extern IntPtr GetCurrentProcess();
static IntPtr hThread;
public static void SomeMethod(object value)
{
DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), GetCurrentProcess(), out hThread, 0, false, 2);
while (true)
{
Console.WriteLine(".");
Thread.Sleep(1000);
}
}
private static void APC(UIntPtr data)
{
Console.WriteLine("Callback invoked");
}
static void Main(string[] args)
{
Console.WriteLine("in Main\n");
Thread t = new Thread(Program.SomeMethod);
t.Start();
Thread.Sleep(1000); // wait until the thread fills out the hThread member -- don't do this at home, this isn't a good way to synchronize threads...
uint result = QueueUserAPC(APC, hThread, (UIntPtr)0);
Console.ReadLine();
}
Edit:
How the CLR injects the exception
Given this loop for the thread function:
while (true)
{
i = ((i + 7) * 3 ^ 0x73234) & 0xFFFF;
}
I then .Aborted the thread and looked at the native stack trace
...
ntdll!KiUserExceptionDispatcher
KERNELBASE!RaiseException
clr!RaiseComPlusException
clr!RedirectForThrowControl2
clr!RedirectForThrowControl_RspAligned
clr!RedirectForThrowControl_FixRsp
csTest.Program.SomeMethod(System.Object)
...
Looking at the return address of the RedirectForThrowControl_FixRsp call, it is pointing into the middle of my loop, for which there are no jumps or calls:
nop
mov eax,dword ptr [rbp+8]
add eax,7 // code flow would return to execute this line
lea eax,[rax+rax*2]
xor eax,73234h
and eax,0FFFFh
mov dword ptr [rbp+8],eax
nop
mov byte ptr [rbp+18h],1
jmp 000007fe`95ba02da // loop back to the top
So apparently the CLR is actually modifying the instruction pointer of the thread in question to physically yank control from the normal flow. They obviously needed to supply several wrappers to fixup and restore all the stack registers to make it work correctly (thus the aptly named _FixRsp and _RspAligned APIs.
In a separate test, I just had Console.Write() calls within my thread loop, and there it looked like the CLR injected a test just before the physical call out to WriteFile:
KERNELBASE!RaiseException
clr!RaiseTheExceptionInternalOnly
clr! ?? ::FNODOBFM::`string'
clr!HelperMethodFrame::PushSlowHelper
clr!JIT_RareDisableHelper
mscorlib_ni!DomainNeutralILStubClass.IL_STUB_PInvoke(Microsoft.Win32.SafeHandles.SafeFileHandle, Byte*, Int32, Int32 ByRef, IntPtr)
mscorlib_ni!System.IO.__ConsoleStream.WriteFileNative(Microsoft.Win32.SafeHandles.SafeFileHandle, Byte[], Int32, Int32, Boolean)
To get the QueueUserAPC to work you have to do two things.
Acquire the target thread handle. Note that this is not the same thing as the native thread id.
Allow the target thread to go into an alertable state.
Here is a complete program that demonstrates this.
class Program
{
[DllImport("kernel32.dll", EntryPoint = "DuplicateHandle", CallingConvention = CallingConvention.StdCall, SetLastError = true)]
public static extern bool DuplicateHandle([In] System.IntPtr hSourceProcessHandle, [In] System.IntPtr hSourceHandle, [In] System.IntPtr hTargetProcessHandle, out System.IntPtr lpTargetHandle, uint dwDesiredAccess, [MarshalAsAttribute(UnmanagedType.Bool)] bool bInheritHandle, uint dwOptions);
[DllImport("kernel32.dll", EntryPoint = "GetCurrentProcess", CallingConvention = CallingConvention.StdCall, SetLastError = true)]
public static extern IntPtr GetCurrentProcess();
[DllImport("kernel32.dll")]
private static extern IntPtr GetCurrentThread();
[DllImport("kernel32.dll")]
private static extern uint QueueUserAPC(ApcMethod pfnAPC, IntPtr hThread, UIntPtr dwData);
private delegate void ApcMethod(UIntPtr dwParam);
static void Main(string[] args)
{
Console.WriteLine("Main: " + Thread.CurrentThread.ManagedThreadId);
IntPtr threadHandle = IntPtr.Zero;
var threadHandleSet = new ManualResetEvent(false);
var apcSet = new ManualResetEvent(false);
var thread = new Thread(
() =>
{
Console.WriteLine("thread started");
threadHandle = GetCurrentThread();
DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), GetCurrentProcess(), out threadHandle, 0, false, 2);
threadHandleSet.Set();
apcSet.WaitOne();
for (int i = 0; i < 10; i++)
{
Console.WriteLine("thread waiting");
Thread.Sleep(1000);
Console.WriteLine("thread running");
}
Console.WriteLine("thread finished");
});
thread.Start();
threadHandleSet.WaitOne();
uint result = QueueUserAPC(DoApcCallback, threadHandle, UIntPtr.Zero);
apcSet.Set();
Console.ReadLine();
}
private static void DoApcCallback(UIntPtr dwParam)
{
Console.WriteLine("DoApcCallback: " + Thread.CurrentThread.ManagedThreadId);
}
}
This essentially allows a developer to inject the execution of a method into any arbitrary thread. The target thread does not have to have a message pump like would be necessary for the traditional approach. One problem with this approach though is that the target thread has to be in an alertable state. So basically the thread must call one of the canned .NET blocking calls like Thread.Sleep, WaitHandle.WaitOne, etc. for the APC queue to execute.
I downloaded the SSCLI code and started poking around. The code is difficult for me to follow (mostly because I am not a C++ or ASM expert), but I do see a lot of hooks where the aborts are injected semi-synchronously.
try/catch/finally/fault block flow control processing
GC activations (allocating memory)
proxied through soft interrupts (like with Thread.Interrupt) when in an alertable state
virtual call intercepts
JIT tail call preparations
unmanaged to managed transitions
That is just to name a few. What I wanted to know was how asynchronous aborts were injected. The general idea of hijacking the instruction pointer is part of how it happens. However, it is far more complex than what I described above. It does not appear that a Suspend-Modify-Resume idiom is always used. From the SSCLI code I can see that it does suspend and resume the thread in certain scenarios to prepare for the hijack, but this is not always the case. It looks to me that the hijack can occur while the thread is running full bore as well.
The article you linked to mentions that an abort flag is set on the target thread. This is technically correct. The flag is called TS_AbortRequested and there is a lot of logic that controls how this flag is set. There are checks for determining if a constrained execution region exists and whether the thread is currently in a try-catch-finally-fault block. Some of this work involves a stack crawl which means the thread must be suspended and resumed. However, how the change of the flag is detected is where the real magic happens. The article does not explain that very well.
I already mentioned several semi-synchronous injection points in the list above. Those should be pretty trivial to understand. But, how does the asynchronous injection happen exactly? Well, it appears to me that the JIT is the wizard behind by the curtain here. There is some kind of polling mechanism built into the JIT/GC that periodically determines if a collection should occur. This also provides an opportunity to check to see if any of the managed threads have changed state (like having the abort flag set). If TS_AbortRequested is set then the hijack happens then and there.
If you are looking at the SSCLI code here are some good functions to look at.
HandleThreadAbort
CommonTripThread
JIT_PollGC
JIT_TailCallHelper
COMPlusCheckForAbort
ThrowForFlowControl
JIT_RareDisableHelper
There are many other clues. Keep in mind that this is the SSCLI so the method names may not match exactly with call stacks observed in production (like what Josh Poley discovered), but there will be similarities. Also, a lot of the thread hijacking is done with assembly code so it is hard to follow at times. I highlighted JIT_PollGC because I believe this is where the interesting stuff happens. This is the hook that I believe the JIT will dynamically and strategically place into the executing thread. This is basically the mechanism for how those tight loops can still receive the abort injections. The target thread really is essentially polling for the abort request, but as part of a larger strategy to invoke the GC1
So clearly the JIT, GC, and thread aborts are intimately related. It is obvious when you look at the SSCLI code. As an example, the method used to determine the safe points for thread aborts is the same as the one used to determine if the GC is allowed to run.
1Shared Source CLI Essentials, David Stutz, 2003, pg. 249-250
It's easy, the underlying OS does it. If the thread is in any state except 'running on another core', there is no problem - it's state is set to 'never run again'. If the thread is runing on another core, the OS hardware-interrupts the other core via. it's interprocessor driver and so exterminates the thread.
Any mention of 'time-slice', 'quantum' etc. is just.....

Ensure that the process i opened will close if my application will crash [duplicate]

I'm creating new processes using System.Diagnostics.Process class from my application. I want this processes to be killed when/if my application has crashed. But if I kill my application from Task Manager, child processes are not killed. Is there any way to make child processes dependent on parent process?
From this forum, credit to 'Josh'.
Application.Quit() and Process.Kill() are possible solutions, but have proven to be unreliable. When your main application dies, you are still left with child processes running. What we really want is for the child processes to die as soon as the main process dies.
The solution is to use "job objects" http://msdn.microsoft.com/en-us/library/ms682409(VS.85).aspx.
The idea is to create a "job object" for your main application, and register your child processes with the job object. If the main process dies, the OS will take care of terminating the child processes.
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);
}
}
Looking at the constructor ...
JOBOBJECT_BASIC_LIMIT_INFORMATION info = new JOBOBJECT_BASIC_LIMIT_INFORMATION();
info.LimitFlags = 0x2000;
The key here is to setup the job object properly. In the constructor I'm setting the "limits" to 0x2000, which is the numeric value for JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE.
MSDN defines this flag as:
Causes all processes associated with
the job to terminate when the last
handle to the job is closed.
Once this class is setup...you just have to register each child process with the job. For example:
[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);
This answer started with #Matt Howells' excellent answer plus others (see links in the code below). Improvements:
Supports 32-bit and 64-bit.
Fixes some problems in #Matt Howells' answer:
The small memory leak of extendedInfoPtr
The 'Win32' compile error, and
A stack-unbalanced exception I got in the call to CreateJobObject (using Windows 10, Visual Studio 2015, 32-bit).
Names the Job, so if you use SysInternals, for example, you can easily find it.
Has a somewhat simpler API and less code.
Here's how to use this code:
// Get a Process object somehow.
Process process = Process.Start(exePath, args);
// Add the Process to ChildProcessTracker.
ChildProcessTracker.AddProcess(process);
To support Windows 7 requires:
A simple app.manifest change as #adam smith describes.
Registry settings to be added if you are using Visual Studio.
In my case, I didn't need to support Windows 7, so I have a simple check at the
top of the static constructor below.
/// <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;
}
I carefully tested both the 32-bit and 64-bit versions of the structs by programmatically comparing the managed and native versions to each other (the overall size as well as the offsets for each member).
I've tested this code on Windows 7, 8, and 10.
This post is intended as an extension to #Matt Howells' answer, specifically for those who run into problems with using Job Objects under Vista or Win7, especially if you get an access denied error ('5') when calling AssignProcessToJobObject.
tl;dr
To ensure compatibility with Vista and Win7, add the following manifest to the .NET parent process:
<?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>
Note that when you add new manifest in Visual Studio 2012 it will contain the above snippet already so you do not need to copy it from hear. It will also include a node for Windows 8.
full explanation
Your job association will fail with an access denied error if the process you're starting is already associated with another job. Enter Program Compatibility Assistant, which, starting in Windows Vista, will assign all kinds of processes to its own jobs.
In Vista you can mark your application to be excluded from PCA by simply including an application manifest. Visual Studio seems to do this for .NET apps automatically, so you're fine there.
A simple manifest no longer cuts it in Win7. [1] There, you have to specifically specify that you're compatible with Win7 with the tag in your manifest. [2]
This led me to worry about Windows 8. Will I have to change my manifest once again? Apparently there's a break in the clouds, as Windows 8 now allows a process to belong to multiple jobs. [3] So I haven't tested it yet, but I imagine that this madness will be over now if you simply include a manifest with the supportedOS information.
Tip 1: If you're developing a .NET app with Visual Studio, as I was, here [4] are some nice instructions on how to customize your application manifest.
Tip 2: Be careful with launching your application from Visual Studio. I found that, after adding the appropriate manifest, I still had problems with PCA when launching from Visual Studio, even if I used Start without Debugging. Launching my application from Explorer worked, however. After manually adding devenv for exclusion from PCA using the registry, starting applications that used Job Objects from VS started working as well. [5]
Tip 3: If you ever want to know if PCA is your problem, try launching your application from the command line, or copy the program to a network drive and run it from there. PCA is automatically disabled in those contexts.
[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-take-2-because-we-changed-the-rules-on-you.aspx
[2] http://ayende.com/blog/4360/how-to-opt-out-of-program-compatibility-assistant
[3] http://msdn.microsoft.com/en-us/library/windows/desktop/ms681949(v=vs.85).aspx:
"A process can be associated with more than one job in Windows 8"
[4] How can I embed an application manifest into an application using VS2008?
[5] How to stop the Visual Studio debugger starting my process in a job object?
Here's an alternative that may work for some when you have control of the code the child process runs. The benefit of this approach is it doesn't require any native Windows calls.
The basic idea is to redirect the child's standard input to a stream whose other end is connected to the parent, and use that stream to detect when the parent has gone away. When you use System.Diagnostics.Process to start the child, it's easy to ensure its standard input is redirected:
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
And then, on the child process, take advantage of the fact that Reads from the standard input stream will always return with at least 1 byte until the stream is closed, when they will start returning 0 bytes. An outline of the way I ended up doing this is below; my way also uses a message pump to keep the main thread available for things other than watching standard in, but this general approach could be used without message pumps too.
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();
}
}
Caveats to this approach:
the actual child .exe that is launched must be a console application so it remains attached to stdin/out/err. As in the above example, I easily adapted my existing application that used a message pump (but didn't show a GUI) by just creating a tiny console project that referenced the existing project, instantiating my application context and calling Application.Run() inside the Main method of the console .exe.
Technically, this merely signals the child process when the parent exits, so it will work whether the parent process exited normally or crashed, but its still up to the child processes to perform its own shutdown. This may or may not be what you want...
There is another relevant method, easy and effective, to finish child processes on program termination. You can implement and attach a debugger to them from the parent; when the parent process ends, child processes will be killed by the OS. It can go both ways attaching a debugger to the parent from the child (note that you can only attach one debugger at a time). You can find more info on the subject here.
Here you have an utility class that launches a new process and attaches a debugger to it. It has been adapted from this post by Roger Knapp. The only requirement is that both processes need to share the same bitness. You cannot debug a 32bit process from a 64bit process or vice versa.
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
}
Usage:
new ProcessRunner().StartProcess("c:\\Windows\\system32\\calc.exe");
One way is to pass PID of parent process to the child. The child will periodically poll if the process with the specified pid exists or not. If not it will just quit.
You can also use Process.WaitForExit method in child method to be notified when the parent process ends but it might not work in case of Task Manager.
I was looking for a solution to this problem that did not require unmanaged code. I was also not able to use standard input/output redirection because it was a Windows Forms application.
My solution was to create a named pipe in the parent process and then connect the child process to the same pipe. If the parent process exits then the pipe becomes broken and the child can detect this.
Below is an example using two console applications:
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();
}
}
Child
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);
}
Use event handlers to make hooks on a few exit scenarios:
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(); };
Yet another addition to the abundant richness of solutions proposed so far....
The problem with many of them is that they rely upon the parent and child process to shut down in an orderly manner, which isn't always true when development is underway. I found that my child process was often being orphaned whenever I terminated the parent process in the debugger, which required me to kill the orphaned process(es) with Task Manager in order to rebuild my solution.
The solution: Pass the parent process ID in on the commandline (or even less invasive, in the environment variables) of the child process.
In the parent process, the process ID is available as:
Process.CurrentProcess().Id;
In the child process:
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();
};
I am of two minds as to whether this is acceptable practice in production code. On the one hand, this should never happen. But on the other hand, it may mean the difference between restarting a process, and rebooting a production server. And what should never happen often does.
And it sure is useful while debugging orderly shutdown problems.
Solution that worked for me:
When creating process add tag 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();
I see two options:
If you know exactly what child process could be started and you are sure they are only started from your main process, then you could consider simply searching for them by name and kill them.
Iterate through all processes and kill every process that has your process as a parent (I guess you need to kill the child processes first). Here is explained how you can get the parent process id.
I've made a child process management library where the parent process and the child process are monitored due a bidirectional WCF pipe. If either the child process terminates or the parent process terminates each other is notified.
There is also a debugger helper available which automatically attaches the VS debugger to the started child process
Project site:
http://www.crawler-lib.net/child-processes
NuGet Packages:
https://www.nuget.org/packages/ChildProcesses
https://www.nuget.org/packages/ChildProcesses.VisualStudioDebug/
Just my 2018 version.
Use it aside your Main() method.
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);
}
call job.AddProcess better to do after start of the process:
prc.Start();
job.AddProcess(prc.Handle);
When calling AddProcess before the terminate, child processes are not killed. (Windows 7 SP1)
private void KillProcess(Process proc)
{
var job = new Job();
job.AddProcess(proc.Handle);
job.Close();
}
I had the same problem. I was creating child processes that never got killed if my main app crashed. I had to destroy manually child processes when debugging. I found that there was no need to make the children somewhat depend on parent. In my main, I added a try catch to do a CleanUp() of child processes on exit.
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();
}
}
}
If you can catch situation, when your processes tree should be killed, since .NET 5.0 (.NET Core 3.0) you could use Process.Kill(bool entireProcessTree)

How to hook an application?

I'm trying to hook the creation of a windows in my C# app.
static IntPtr hhook = IntPtr.Zero;
static NativeMethods.HookProc hhookProc;
static void Main(string[] args)
{
// Dummy.exe is a form with a button that opens a MessageBox when clicking on it.
Process dummy = Process.Start(#"Dummy.exe");
try
{
hhookProc = new NativeMethods.HookProc(Hook);
IntPtr hwndMod = NativeMethods.GetModuleHandle(Process.GetCurrentProcess().MainModule.ModuleName);
hhook = NativeMethods.SetWindowsHookEx(HookType.WH_CBT, hhookProc, hwndMod, (uint)AppDomain.GetCurrentThreadId());
Console.WriteLine("hhook valid? {0}", hhook != IntPtr.Zero);
while (!dummy.HasExited)
dummy.WaitForExit(500);
}
finally
{
if(hhook != IntPtr.Zero)
NativeMethods.UnhookWindowsHookEx(hhook);
}
}
static int Hook(int nCode, IntPtr wParam, IntPtr lParam)
{
Console.WriteLine("Hook()");
return NativeMethods.CallNextHookEx(hhook, nCode, wParam, lParam);
}
Problem is, when clicking on my button (in Dummy.exe), I never enter in my Hook, what am I doing wrong?
Thanks.
EDIT
Program.cs
using System;
using System.Diagnostics;
namespace Hooker
{
class Program
{
static IntPtr hhook = IntPtr.Zero;
static NativeMethods.HookProc hhookProc;
static void Main(string[] args)
{
Process dummy = Process.Start(#"Dummy.exe");
try
{
hhookProc = new NativeMethods.HookProc(Hook);
hhook = NativeMethods.SetWindowsHookEx(HookType.WH_CBT, hhookProc, IntPtr.Zero, 0);
Console.WriteLine("hhook valid? {0}", hhook != IntPtr.Zero);
while (!dummy.HasExited)
dummy.WaitForExit(500);
}
finally
{
if(hhook != IntPtr.Zero)
NativeMethods.UnhookWindowsHookEx(hhook);
}
}
static int Hook(int nCode, IntPtr wParam, IntPtr lParam)
{
Console.WriteLine("Hook()");
return NativeMethods.CallNextHookEx(IntPtr.Zero, nCode, wParam, lParam);
}
}
}
NativeMethods.cs
namespace Hooker
{
using System;
using System.Runtime.InteropServices;
internal static class NativeMethods
{
public delegate int HookProc(int code, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr SetWindowsHookEx(HookType hookType, HookProc lpfn, IntPtr hMod, int dwThreadId);
[DllImport("user32.dll")]
public static extern int CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool UnhookWindowsHookEx(IntPtr hhk);
[DllImport("user32.dll", SetLastError = true)]
public static extern int GetWindowThreadProcessId(IntPtr hwnd, ref int pid);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr GetModuleHandle(string lpModuleName);
}
}
For dummy, do a new Form with :
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
MessageBox.Show("CONTENT", "TITLE");
}
Like most SetWindowsHookEx hooks, WH_CBT hooks require that the hook callback lives in a separate Win32 DLL that will get loaded into the target process. This essentially requires that the hook is written in C/C++, C# won't work here.
(Low-level mouse and keyboard hooks are two exceptions to this rule. It might also be possible to use other hooks in C# but only if you are hooking one of your own threads, so dwThreadId is the id of a thread in the current process, not 0. I haven't confirmed this, though. And you need to make sure you're using the Win32 threadid, so using GetCurrentThreadId is probably the best bet here.)
If you want to watch for new windows appearing from another app, an alternative C#-friendly approach is to use the SetWinEventHook API instead, specify WINEVENT_OUTOFCONTEXT (which is the magic flag that gets the events delivered to your own process, removing the need for a DLL and making C# usable here) and hook for the EVENT_OBJECT_CREATE and EVENT_OBJECT_SHOW events. You can listen to either your own process/thread's events, or to all processes/threads on the current desktop.
This will get you all sorts of "create" and show notifications, including those for child HWNDs within dialog, and even items within listboxes and similar; so you'll need to filter to extract only those for top-level HWNDs: eg. check that idObject==OBJID_WINDOW and idChild==0; that hwnd is visible (IsVisible()) and is top-level.
Note that using WinEvents requires that the thread that calls SetWinEventHook is pumping messages - which is typically the case anyway if it's a thread with UI. If not, you may need to add a message loop (GetMessage/TranslateMessage) manually. And you'll also want to use GC.KeepAlive() with the callback here also to prevent it from getting collected until after you call UnhookWinEvents.
One issue with your code is that hhookProc can be garbage collected while your native code still needs it. Use GC.KeepAlive or put in in a static variable.
The hMod param should probably be null, since you specified a thread in your own process:
hMod [in]
Type: HINSTANCE
A handle to the DLL containing the hook procedure pointed to by the lpfn parameter. The hMod parameter must be set to NULL if the dwThreadId parameter specifies a thread created by the current process and if the hook procedure is within the code associated with the current process.
But I think this only works for windows on the thread you specify.
In theory you can specify threads in other applications or even a global hook. The specified callback then gets called on the corresponding thread, even if that thread is in another process, in which case your dll gets injected into that process(that's the reason you need to specify the module handle in the first place).
But I believe that's not possible with .net code, because the mechanism for injecting into other processes and calling the hook method over there doesn't work with JIT compiled code.
This won't work in C#
Scope: Thread
If the application installs a hook procedure for a thread of a different application, the procedure must be in a DLL.
(Documentation of SetWindowsHookEx)
Scope: Global
To install a global hook, a hook must have a native DLL export to inject itself in another process that requires a valid, consistent function to call into. This behavior requires a DLL export. The .NET Framework does not support DLL exports.
(Source)
I'm not familiar with the NativeMethod class you are referencing, but I'll make some assumptions and try to make some ground.
My guess this has to do with what handle you are hooking. The
dummy.MainWindowHandle
represents the front most window's handle, which is usually what you are looking for. However, in this case, you are opening a MessageBox.Show() which likely has a different handle than the one you have hooked to.
I'd assume that
IntPtr hwndMod = NativeMethods.GetModuleHandle(Process.GetCurrentProcess().MainModule.ModuleName);
is probably going to return the same result as
dummy.Refresh();
IntPtr hwndMod = dummy.MainWindowHandle;
So I think it's safe to say that they might be giving you the handle you aren't looking for.
Perhaps try to make a test WinForm application. That way you can grab the proper handle. Just make sure to use
dummy.WaitForInputIdle();
dummy.Refresh();
before grabbing the handle to make sure you are grabbing the right handle at launch time.
I see it is a console application , so console application doesn't enter a windows messages loop.
simple solution is to include system.windows.forms
and simply type application.start() in your main
and things will be fine :)

Categories

Resources