In C# code want to terminate the specified process AND any child processes which were started by it, similar to what can be accomplished from the commandline with taskkill.exe (and similarly with windows 2000 era resource kit file rkill.exe):
taskkill.exe /PID 1234 /F /T
The same kind of thing can be done with the Task Manager taskmgr.exe in which one can right click on a process in the Processes tab and choose "End Process Tree" instead of "End Process"
There is a function NtQueryInformationProcess in the NTDLL.dll with the struct PROCESSINFOCLASS that can be called in such a way as to get InheritedFromUniqueProcessId from the PROCESS_BASIC_INFORMATION
http://www.codeguru.com/Cpp/W-P/win32/article.php/c1437/
That gives a "Parent" process ID that can then be used to walk through all processes and kill a tree of processes, that is the information we are looking for that would enable us to locate all child processes and terminate those as well.
Is that the only way to obtain the information under Windows (2000, XP, 2003)?
Any idea how to make such calls from C#? We are able to make basic calls but calls such as this one that have OUT parameters we have trouble getting to work in C#.
Thanks,
Frank
Thanks for your post. NtQueryInformationProcess is supported in Windows
XP/2000/2003. Based on my experience and research, I did not find other
method to achieve this.
In C#, you can use P/Invoke to call unmanaged APIs. Please refer to the
following MSDN article:
Consuming Unmanaged DLL Functions
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguide/htm
l/cpconConsumingUnmanagedDLLFunctions.asp
The article below give several samples on passing structures as In/Out
parameters in P/Invoke:
Marshaling Classes, Structures, and Unions
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguide/htm
l/cpconmarshalingclassesstructuresunions.asp
If you have any problems, please feel free to let me know.
Have a nice day!
Regards,
HuangTM
Microsoft Online Partner Support
MCSE/MCSD
Get Secure! -- www.microsoft.com/security
This posting is provided "as is" with no warranties and confers no rights.
The code download includes a wrapper class, wrapper.cs.
The Wrapper.cs contains examples of calling NtQueryInformationProcess
to get InheritedFromUniqueProcessId which is equivalent to the Parent
Process ID.
Basically the calling code from .NET would look like this:
[DllImport("KERNEL32.DLL")] private static extern int
OpenProcess(eDesiredAccess dwDesiredAccess, bool bInheritHandle, int
dwProcessId);
[DllImport("KERNEL32.DLL")] private static extern int CloseHandle(int
hObject);
[DllImport("NTDLL.DLL")] private static extern int
NtQueryInformationProcess(int hProcess, PROCESSINFOCLASS pic, ref
PROCESS_BASIC_INFORMATION pbi, int cb, ref int pSize);
enum PROCESSINFOCLASS : int
{
ProcessBasicInformation = 0,
ProcessQuotaLimits,
ProcessIoCounters,
ProcessVmCounters,
ProcessTimes,
ProcessBasePriority,
ProcessRaisePriority,
ProcessDebugPort,
ProcessExceptionPort,
ProcessAccessToken,
ProcessLdtInformation,
ProcessLdtSize,
ProcessDefaultHardErrorMode,
ProcessIoPortHandlers, // Note: this is kernel mode only
ProcessPooledUsageAndLimits,
ProcessWorkingSetWatch,
ProcessUserModeIOPL,
ProcessEnableAlignmentFaultFixup,
ProcessPriorityClass,
ProcessWx86Information,
ProcessHandleCount,
ProcessAffinityMask,
ProcessPriorityBoost,
MaxProcessInfoClass
};
[StructLayout(LayoutKind.Sequential)]
public struct PROCESS_BASIC_INFORMATION
{
public int ExitStatus;
public int PebBaseAddress;
public int AffinityMask;
public int BasePriority;
public int UniqueProcessId;
public int InheritedFromUniqueProcessId;
public int Size
{
get { return(6*4); }
}
};
enum eDesiredAccess : int
{
DELETE = 0x00010000,
READ_CONTROL = 0x00020000,
WRITE_DAC = 0x00040000,
WRITE_OWNER = 0x00080000,
SYNCHRONIZE = 0x00100000,
STANDARD_RIGHTS_ALL = 0x001F0000,
PROCESS_TERMINATE = 0x0001,
PROCESS_CREATE_THREAD = 0x0002,
PROCESS_SET_SESSIONID = 0x0004,
PROCESS_VM_OPERATION = 0x0008,
PROCESS_VM_READ = 0x0010,
PROCESS_VM_WRITE = 0x0020,
PROCESS_DUP_HANDLE = 0x0040,
PROCESS_CREATE_PROCESS = 0x0080,
PROCESS_SET_QUOTA = 0x0100,
PROCESS_SET_INFORMATION = 0x0200,
PROCESS_QUERY_INFORMATION = 0x0400,
PROCESS_ALL_ACCESS = SYNCHRONIZE | 0xFFF
}
public static int GetParentProcessId(int PID)
{
int ParentID = 0;
try
{
int hProcess =
OpenProcess(eDesiredAccess.PROCESS_QUERY_INFORMATION, false, PID);
if (hProcess != 0)
{
PROCESS_BASIC_INFORMATION pbi = new
PROCESS_BASIC_INFORMATION();
int pSize = 0;
if (-1 != NtQueryInformationProcess(hProcess,
PROCESSINFOCLASS.ProcessBasicInformation, ref pbi, pbi.Size, ref
pSize))
{
ParentID = pbi.InheritedFromUniqueProcessId;
}
CloseHandle(hProcess);
}
}
catch(Exception e)
{
Trace.WriteLine("Error in GetParentProcessId(): {0}", e.Message);
}
return(ParentID);
}
----------------------
using System.Collections;
using System.Diagnostics;
using System.Runtime.InteropServices;
/// <summary>
/// Summary description for ProcessTreeKillable.
/// </summary>
public class ProcessUtility
{
public static void KillTree(int processToKillId)
{
// Kill each child process
foreach (int childProcessId in GetChildProcessIds(processToKillId))
{
using (Process child = Process.GetProcessById(childProcessId))
{
child.Kill();
}
}
// Then kill this process
using (Process thisProcess = Process.GetProcessById(processToKillId))
{
thisProcess.Kill();
}
}
public static int GetParentProcessId(int processId)
{
int ParentID = 0;
int hProcess = OpenProcess(eDesiredAccess.PROCESS_QUERY_INFORMATION,
false, processId);
if (hProcess != 0)
{
try
{
PROCESS_BASIC_INFORMATION pbi = new PROCESS_BASIC_INFORMATION();
int pSize = 0;
if (-1 != NtQueryInformationProcess(hProcess,
PROCESSINFOCLASS.ProcessBasicInformation, ref pbi, pbi.Size, ref
pSize))
{
ParentID = pbi.InheritedFromUniqueProcessId;
}
}
finally
{
CloseHandle(hProcess);
}
}
return (ParentID);
}
public static int[] GetChildProcessIds(int parentProcessId)
{
ArrayList myChildren = new ArrayList();
foreach (Process proc in Process.GetProcesses())
{
int currentProcessId = proc.Id;
proc.Dispose();
if (parentProcessId == GetParentProcessId(currentProcessId))
{
// Add this one
myChildren.Add(currentProcessId);
// Add any of its children
myChildren.AddRange(GetChildProcessIds(currentProcessId));
}
}
return (int[]) myChildren.ToArray(typeof (int));
}
#region PInvokes
[DllImport("KERNEL32.DLL")]
private static extern int OpenProcess(eDesiredAccess dwDesiredAccess,
bool bInheritHandle, int dwProcessId);
[DllImport("KERNEL32.DLL")]
private static extern int CloseHandle(int hObject);
[DllImport("NTDLL.DLL")]
private static extern int NtQueryInformationProcess(int hProcess,
PROCESSINFOCLASS pic, ref PROCESS_BASIC_INFORMATION pbi, int cb, ref
int pSize);
private enum PROCESSINFOCLASS : int
[StructLayout(LayoutKind.Sequential)]
private struct PROCESS_BASIC_INFORMATION
{
public int ExitStatus;
public int PebBaseAddress;
public int AffinityMask;
public int BasePriority;
public int UniqueProcessId;
public int InheritedFromUniqueProcessId;
public int Size
{
get { return (6*4); }
}
} ;
private enum eDesiredAccess : int
{
DELETE = 0x00010000,
READ_CONTROL = 0x00020000,
WRITE_DAC = 0x00040000,
WRITE_OWNER = 0x00080000,
SYNCHRONIZE = 0x00100000,
STANDARD_RIGHTS_ALL = 0x001F0000,
PROCESS_TERMINATE = 0x0001,
PROCESS_CREATE_THREAD = 0x0002,
PROCESS_SET_SESSIONID = 0x0004,
PROCESS_VM_OPERATION = 0x0008,
PROCESS_VM_READ = 0x0010,
PROCESS_VM_WRITE = 0x0020,
PROCESS_DUP_HANDLE = 0x0040,
PROCESS_CREATE_PROCESS = 0x0080,
PROCESS_SET_QUOTA = 0x0100,
PROCESS_SET_INFORMATION = 0x0200,
PROCESS_QUERY_INFORMATION = 0x0400,
PROCESS_ALL_ACCESS = SYNCHRONIZE | 0xFFF
}
#endregion
}