Code:
/ Dotnetfx_Win7_3.5.1 / Dotnetfx_Win7_3.5.1 / 3.5.1 / DEVDIV / depot / DevDiv / releases / whidbey / NetFXspW7 / ndp / fx / src / Services / Monitoring / system / Diagnosticts / ProcessManager.cs / 1 / ProcessManager.cs
#if !FEATURE_PAL
namespace System.Diagnostics {
using System.Text;
using System.Threading;
using System.Runtime.InteropServices;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Diagnostics;
using System;
using System.Collections;
using System.IO;
using Microsoft.Win32;
using Microsoft.Win32.SafeHandles;
using System.Collections.Specialized;
using System.Globalization;
using System.Security;
using System.Security.Permissions;
using System.Security.Principal;
using System.Runtime.Versioning;
///
/// This class finds the main window of a process. It needs to be
/// class because we need to store state while searching the set
/// of windows.
///
///
internal class MainWindowFinder {
IntPtr bestHandle;
int processId;
public IntPtr FindMainWindow(int processId) {
bestHandle = (IntPtr)0;
this.processId = processId;
NativeMethods.EnumThreadWindowsCallback callback = new NativeMethods.EnumThreadWindowsCallback(this.EnumWindowsCallback);
NativeMethods.EnumWindows(callback, IntPtr.Zero);
GC.KeepAlive(callback);
return bestHandle;
}
bool IsMainWindow(IntPtr handle) {
if (NativeMethods.GetWindow(new HandleRef(this, handle), NativeMethods.GW_OWNER) != (IntPtr)0 || !NativeMethods.IsWindowVisible(new HandleRef(this, handle)))
return false;
// [....]: should we use no window title to mean not a main window? (task man does)
/*
int length = NativeMethods.GetWindowTextLength(handle) * 2;
StringBuilder builder = new StringBuilder(length);
if (NativeMethods.GetWindowText(handle, builder, builder.Capacity) == 0)
return false;
if (builder.ToString() == string.Empty)
return false;
*/
return true;
}
bool EnumWindowsCallback(IntPtr handle, IntPtr extraParameter) {
int processId;
NativeMethods.GetWindowThreadProcessId(new HandleRef(this, handle), out processId);
if (processId == this.processId) {
if (IsMainWindow(handle)) {
bestHandle = handle;
return false;
}
}
return true;
}
}
///
/// This static class is a platform independent Api for querying information
/// about processes, threads and modules. It delegates to the platform
/// specific classes WinProcessManager for Win9x and NtProcessManager
/// for WinNt.
///
///
internal static class ProcessManager {
static ProcessManager() {
// In order to query information (OpenProcess) on some protected processes
// like csrss, we need SeDebugPrivilege privilege.
// After removing the depenecy on Performance Counter, we don't have a chance
// to run the code in CLR performance counter to ask for this privilege.
// So we will try to get the privilege here.
// We could fail if the user account doesn't have right to do this, but that's fair.
NativeMethods.LUID luid = new NativeMethods.LUID();
if (!NativeMethods.LookupPrivilegeValue(null, "SeDebugPrivilege", out luid)) {
return;
}
IntPtr tokenHandle = IntPtr.Zero;
try {
if( !NativeMethods.OpenProcessToken(
new HandleRef(null, NativeMethods.GetCurrentProcess()),
(int)TokenAccessLevels.AdjustPrivileges,
out tokenHandle)) {
return;
}
NativeMethods.TokenPrivileges tp = new NativeMethods.TokenPrivileges();
tp.PrivilegeCount = 1;
tp.Luid = luid;
tp.Attributes = NativeMethods.SE_PRIVILEGE_ENABLED;
// AdjustTokenPrivileges can return true even if it didn't succeed (when ERROR_NOT_ALL_ASSIGNED is returned).
NativeMethods.AdjustTokenPrivileges(new HandleRef(null,tokenHandle), false, tp, 0, IntPtr.Zero, IntPtr.Zero);
}
finally {
if( tokenHandle != IntPtr.Zero) {
SafeNativeMethods.CloseHandle(new HandleRef(null,tokenHandle));
}
}
}
public static bool IsNt {
get {
return Environment.OSVersion.Platform == PlatformID.Win32NT;
}
}
public static bool IsOSOlderThanXP {
get {
return Environment.OSVersion.Version.Major < 5 ||
(Environment.OSVersion.Version.Major == 5 && Environment.OSVersion.Version.Minor == 0);
}
}
public static ProcessInfo[] GetProcessInfos(string machineName) {
bool isRemoteMachine = IsRemoteMachine(machineName);
if (IsNt) {
// Do not use performance counter for local machine with Win2000 and above
if( !isRemoteMachine &&
(Environment.OSVersion.Version.Major >= 5 )) {
return NtProcessInfoHelper.GetProcessInfos();
}
return NtProcessManager.GetProcessInfos(machineName, isRemoteMachine);
}
else {
if (isRemoteMachine)
throw new PlatformNotSupportedException(SR.GetString(SR.WinNTRequiredForRemote));
return WinProcessManager.GetProcessInfos();
}
}
public static int[] GetProcessIds() {
if (IsNt)
return NtProcessManager.GetProcessIds();
else {
return WinProcessManager.GetProcessIds();
}
}
public static int[] GetProcessIds(string machineName) {
if (IsRemoteMachine(machineName)) {
if (IsNt) {
return NtProcessManager.GetProcessIds(machineName, true);
}
else {
throw new PlatformNotSupportedException(SR.GetString(SR.WinNTRequiredForRemote));
}
}
else {
return GetProcessIds();
}
}
public static bool IsProcessRunning(int processId, string machineName) {
return IsProcessRunning(processId, GetProcessIds(machineName));
}
public static bool IsProcessRunning(int processId) {
return IsProcessRunning(processId, GetProcessIds());
}
static bool IsProcessRunning(int processId, int[] processIds) {
for (int i = 0; i < processIds.Length; i++)
if (processIds[i] == processId)
return true;
return false;
}
public static int GetProcessIdFromHandle(SafeProcessHandle processHandle) {
if (IsNt)
return NtProcessManager.GetProcessIdFromHandle(processHandle);
else
throw new PlatformNotSupportedException(SR.GetString(SR.WinNTRequired));
}
public static IntPtr GetMainWindowHandle(ProcessInfo processInfo) {
MainWindowFinder finder = new MainWindowFinder();
return finder.FindMainWindow(processInfo.processId);
}
public static ModuleInfo[] GetModuleInfos(int processId) {
if (IsNt)
return NtProcessManager.GetModuleInfos(processId);
else
return WinProcessManager.GetModuleInfos(processId);
}
public static SafeProcessHandle OpenProcess(int processId, int access, bool throwIfExited) {
SafeProcessHandle processHandle = NativeMethods.OpenProcess(access, false, processId);
int result = Marshal.GetLastWin32Error();
if (!processHandle.IsInvalid) {
return processHandle;
}
if (processId == 0) {
throw new Win32Exception(5);
}
// If the handle is invalid because the process has exited, only throw an exception if throwIfExited is true.
if (!IsProcessRunning(processId)) {
if (throwIfExited) {
throw new InvalidOperationException(SR.GetString(SR.ProcessHasExited, processId.ToString(CultureInfo.CurrentCulture)));
}
else {
return SafeProcessHandle.InvalidHandle;
}
}
throw new Win32Exception(result);
}
public static SafeThreadHandle OpenThread(int threadId, int access) {
try {
SafeThreadHandle threadHandle = NativeMethods.OpenThread(access, false, threadId);
int result = Marshal.GetLastWin32Error();
if (threadHandle.IsInvalid) {
if (result == NativeMethods.ERROR_INVALID_PARAMETER)
throw new InvalidOperationException(SR.GetString(SR.ThreadExited, threadId.ToString(CultureInfo.CurrentCulture)));
throw new Win32Exception(result);
}
return threadHandle;
}
catch (EntryPointNotFoundException x) {
throw new PlatformNotSupportedException(SR.GetString(SR.Win2000Required), x);
}
}
public static bool IsRemoteMachine(string machineName) {
if (machineName == null)
throw new ArgumentNullException("machineName");
if (machineName.Length == 0)
throw new ArgumentException(SR.GetString(SR.InvalidParameter, "machineName", machineName));
string baseName;
if (machineName.StartsWith("\\", StringComparison.Ordinal))
baseName = machineName.Substring(2);
else
baseName = machineName;
if (baseName.Equals(".")) return false;
StringBuilder sb = new StringBuilder(256);
SafeNativeMethods.GetComputerName(sb, new int[] {sb.Capacity});
string computerName = sb.ToString();
if (String.Compare(computerName, baseName, StringComparison.OrdinalIgnoreCase) == 0) return false;
return true;
}
}
///
/// This static class provides the process api for the Win9x platform.
/// We use the toolhelp32 api to query process, thread and module information.
///
///
internal static class WinProcessManager {
// This is expensive. We should specialize getprocessinfos and only get
// the ids instead of getting all the info and then copying the ids out.
public static int[] GetProcessIds() {
ProcessInfo[] infos = GetProcessInfos();
int[] ids = new int[infos.Length];
for (int i = 0; i < infos.Length; i++) {
ids[i] = infos[i].processId;
}
return ids;
}
public static ProcessInfo[] GetProcessInfos() {
IntPtr handle = (IntPtr)(-1);
GCHandle bufferHandle = new GCHandle();
ArrayList threadInfos = new ArrayList();
Hashtable processInfos = new Hashtable();
try {
handle = NativeMethods.CreateToolhelp32Snapshot(NativeMethods.TH32CS_SNAPPROCESS | NativeMethods.TH32CS_SNAPTHREAD, 0);
if (handle == (IntPtr)(-1)) throw new Win32Exception();
int entrySize = (int)Marshal.SizeOf(typeof(NativeMethods.WinProcessEntry));
int bufferSize = entrySize + NativeMethods.WinProcessEntry.sizeofFileName;
int[] buffer = new int[bufferSize / 4];
bufferHandle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
IntPtr bufferPtr = bufferHandle.AddrOfPinnedObject();
Marshal.WriteInt32(bufferPtr, bufferSize);
HandleRef handleRef = new HandleRef(null, handle);
if (NativeMethods.Process32First(handleRef, bufferPtr)) {
do {
NativeMethods.WinProcessEntry process = new NativeMethods.WinProcessEntry();
Marshal.PtrToStructure(bufferPtr, process);
ProcessInfo processInfo = new ProcessInfo();
String name = Marshal.PtrToStringAnsi((IntPtr)((long)bufferPtr + entrySize));
processInfo.processName = Path.ChangeExtension(Path.GetFileName(name), null);
processInfo.handleCount = process.cntUsage;
processInfo.processId = process.th32ProcessID;
processInfo.basePriority = process.pcPriClassBase;
processInfo.mainModuleId = process.th32ModuleID;
processInfos.Add(processInfo.processId, processInfo);
Marshal.WriteInt32(bufferPtr, bufferSize);
}
while (NativeMethods.Process32Next(handleRef, bufferPtr));
}
NativeMethods.WinThreadEntry thread = new NativeMethods.WinThreadEntry();
thread.dwSize = Marshal.SizeOf(thread);
if (NativeMethods.Thread32First(handleRef, thread)) {
do {
ThreadInfo threadInfo = new ThreadInfo();
threadInfo.threadId = thread.th32ThreadID;
threadInfo.processId = thread.th32OwnerProcessID;
threadInfo.basePriority = thread.tpBasePri;
threadInfo.currentPriority = thread.tpBasePri + thread.tpDeltaPri;
threadInfos.Add(threadInfo);
}
while (NativeMethods.Thread32Next(handleRef, thread));
}
for (int i = 0; i < threadInfos.Count; i++) {
ThreadInfo threadInfo = (ThreadInfo)threadInfos[i];
ProcessInfo processInfo = (ProcessInfo)processInfos[threadInfo.processId];
if (processInfo != null)
processInfo.threadInfoList.Add(threadInfo);
//else
// throw new InvalidOperationException(SR.GetString(SR.ProcessNotFound, threadInfo.threadId.ToString(), threadInfo.processId.ToString()));
}
}
finally {
if (bufferHandle.IsAllocated) bufferHandle.Free();
Debug.WriteLineIf(Process.processTracing.TraceVerbose, "Process - CloseHandle(toolhelp32 snapshot handle)");
if (handle != (IntPtr)(-1)) SafeNativeMethods.CloseHandle(new HandleRef(null, handle));
}
ProcessInfo[] temp = new ProcessInfo[processInfos.Values.Count];
processInfos.Values.CopyTo(temp, 0);
return temp;
}
public static ModuleInfo[] GetModuleInfos(int processId) {
IntPtr handle = (IntPtr)(-1);
GCHandle bufferHandle = new GCHandle();
ArrayList moduleInfos = new ArrayList();
try {
handle = NativeMethods.CreateToolhelp32Snapshot(NativeMethods.TH32CS_SNAPMODULE, processId);
if (handle == (IntPtr)(-1)) throw new Win32Exception();
int entrySize = Marshal.SizeOf(typeof(NativeMethods.WinModuleEntry));
int bufferSize = entrySize + NativeMethods.WinModuleEntry.sizeofFileName + NativeMethods.WinModuleEntry.sizeofModuleName;
int[] buffer = new int[bufferSize / 4];
bufferHandle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
IntPtr bufferPtr = bufferHandle.AddrOfPinnedObject();
Marshal.WriteInt32(bufferPtr, bufferSize);
HandleRef handleRef = new HandleRef(null, handle);
if (NativeMethods.Module32First(handleRef, bufferPtr)) {
do {
NativeMethods.WinModuleEntry module = new NativeMethods.WinModuleEntry();
Marshal.PtrToStructure(bufferPtr, module);
ModuleInfo moduleInfo = new ModuleInfo();
moduleInfo.baseName = Marshal.PtrToStringAnsi((IntPtr)((long)bufferPtr + entrySize));
moduleInfo.fileName = Marshal.PtrToStringAnsi((IntPtr)((long)bufferPtr + entrySize + NativeMethods.WinModuleEntry.sizeofModuleName));
moduleInfo.baseOfDll = module.modBaseAddr;
moduleInfo.sizeOfImage = module.modBaseSize;
moduleInfo.Id = module.th32ModuleID;
moduleInfos.Add(moduleInfo);
Marshal.WriteInt32(bufferPtr, bufferSize);
}
while (NativeMethods.Module32Next(handleRef, bufferPtr));
}
}
finally {
if (bufferHandle.IsAllocated) bufferHandle.Free();
Debug.WriteLineIf(Process.processTracing.TraceVerbose, "Process - CloseHandle(toolhelp32 snapshot handle)");
if (handle != (IntPtr)(-1)) SafeNativeMethods.CloseHandle(new HandleRef(null, handle));
}
ModuleInfo[] temp = new ModuleInfo[moduleInfos.Count];
moduleInfos.CopyTo(temp, 0);
return temp;
}
}
///
/// This static class provides the process api for the WinNt platform.
/// We use the performance counter api to query process and thread
/// information. Module information is obtained using PSAPI.
///
///
internal static class NtProcessManager {
private const int ProcessPerfCounterId = 230;
private const int ThreadPerfCounterId = 232;
private const string PerfCounterQueryString = "230 232";
internal const int IdleProcessID = 0;
static Hashtable valueIds;
static NtProcessManager() {
valueIds = new Hashtable();
valueIds.Add("Handle Count", ValueId.HandleCount);
valueIds.Add("Pool Paged Bytes", ValueId.PoolPagedBytes);
valueIds.Add("Pool Nonpaged Bytes", ValueId.PoolNonpagedBytes);
valueIds.Add("Elapsed Time", ValueId.ElapsedTime);
valueIds.Add("Virtual Bytes Peak", ValueId.VirtualBytesPeak);
valueIds.Add("Virtual Bytes", ValueId.VirtualBytes);
valueIds.Add("Private Bytes", ValueId.PrivateBytes);
valueIds.Add("Page File Bytes", ValueId.PageFileBytes);
valueIds.Add("Page File Bytes Peak", ValueId.PageFileBytesPeak);
valueIds.Add("Working Set Peak", ValueId.WorkingSetPeak);
valueIds.Add("Working Set", ValueId.WorkingSet);
valueIds.Add("ID Thread", ValueId.ThreadId);
valueIds.Add("ID Process", ValueId.ProcessId);
valueIds.Add("Priority Base", ValueId.BasePriority);
valueIds.Add("Priority Current", ValueId.CurrentPriority);
valueIds.Add("% User Time", ValueId.UserTime);
valueIds.Add("% Privileged Time", ValueId.PrivilegedTime);
valueIds.Add("Start Address", ValueId.StartAddress);
valueIds.Add("Thread State", ValueId.ThreadState);
valueIds.Add("Thread Wait Reason", ValueId.ThreadWaitReason);
}
internal static int SystemProcessID {
get {
const int systemProcessIDOnXP = 4;
const int systemProcessIDOn2K = 8;
if( ProcessManager.IsOSOlderThanXP) {
return systemProcessIDOn2K;
}
else {
return systemProcessIDOnXP;
}
}
}
[ResourceExposure(ResourceScope.Machine)]
[ResourceConsumption(ResourceScope.Machine)]
public static int[] GetProcessIds(string machineName, bool isRemoteMachine) {
ProcessInfo[] infos = GetProcessInfos(machineName, isRemoteMachine);
int[] ids = new int[infos.Length];
for (int i = 0; i < infos.Length; i++)
ids[i] = infos[i].processId;
return ids;
}
[ResourceExposure(ResourceScope.Machine)]
[ResourceConsumption(ResourceScope.Machine)]
public static int[] GetProcessIds() {
int[] processIds = new int[256];
int size;
for (;;) {
if (!NativeMethods.EnumProcesses(processIds, processIds.Length * 4, out size))
throw new Win32Exception();
if (size == processIds.Length * 4) {
processIds = new int[processIds.Length * 2];
continue;
}
break;
}
int[] ids = new int[size / 4];
Array.Copy(processIds, ids, ids.Length);
return ids;
}
[ResourceExposure(ResourceScope.Machine)]
[ResourceConsumption(ResourceScope.Machine)]
public static ModuleInfo[] GetModuleInfos(int processId) {
return GetModuleInfos(processId, false);
}
[ResourceExposure(ResourceScope.Machine)]
[ResourceConsumption(ResourceScope.Machine)]
public static ModuleInfo GetFirstModuleInfo(int processId) {
ModuleInfo[] moduleInfos = GetModuleInfos(processId, true);
if( moduleInfos.Length == 0) {
return null;
}
else {
return moduleInfos[0];
}
}
[ResourceExposure(ResourceScope.Machine)]
[ResourceConsumption(ResourceScope.Machine)]
private static ModuleInfo[] GetModuleInfos(int processId, bool firstModuleOnly) {
// preserving Everett behavior.
if( processId == SystemProcessID || processId == IdleProcessID) {
// system process and idle process doesn't have any modules
throw new Win32Exception(HResults.EFail,SR.GetString(SR.EnumProcessModuleFailed));
}
SafeProcessHandle processHandle = SafeProcessHandle.InvalidHandle;
try {
processHandle = ProcessManager.OpenProcess(processId, NativeMethods.PROCESS_QUERY_INFORMATION | NativeMethods.PROCESS_VM_READ, true);
IntPtr[] moduleHandles = new IntPtr[64];
GCHandle moduleHandlesArrayHandle = new GCHandle();
int moduleCount = 0;
for (;;) {
bool enumResult = false;
try {
moduleHandlesArrayHandle = GCHandle.Alloc(moduleHandles, GCHandleType.Pinned);
//
// EnumProcessModules is not a reliable method to get the modules for a process.
// If OS loader is touching module information, this method might fail and copy part of the data.
// This is no easy solution to this problem. The only reliable way to fix this is to
// suspend all the threads in target process. Of course we don't want to do this in Process class.
// So we just to try avoid the ---- by calling the same method 10 (an arbitary number) times.
//
for( int i = 0; i < 10; i++) {
enumResult = NativeMethods.EnumProcessModules(processHandle, moduleHandlesArrayHandle.AddrOfPinnedObject(), moduleHandles.Length * IntPtr.Size, ref moduleCount);
if( enumResult) {
break;
}
Thread.Sleep(1);
}
}
finally {
moduleHandlesArrayHandle.Free();
}
if (!enumResult) {
throw new Win32Exception();
}
moduleCount /= IntPtr.Size;
if (moduleCount <= moduleHandles.Length) break;
moduleHandles = new IntPtr[moduleHandles.Length * 2];
}
ArrayList moduleInfos = new ArrayList();
int ret;
for (int i = 0; i < moduleCount; i++) {
ModuleInfo moduleInfo = new ModuleInfo();
IntPtr moduleHandle = moduleHandles[i];
NativeMethods.NtModuleInfo ntModuleInfo = new NativeMethods.NtModuleInfo();
if (!NativeMethods.GetModuleInformation(processHandle, new HandleRef(null, moduleHandle), ntModuleInfo, Marshal.SizeOf(ntModuleInfo)))
throw new Win32Exception();
moduleInfo.sizeOfImage = ntModuleInfo.SizeOfImage;
moduleInfo.entryPoint = ntModuleInfo.EntryPoint;
moduleInfo.baseOfDll = ntModuleInfo.BaseOfDll;
StringBuilder baseName = new StringBuilder(1024);
ret = NativeMethods.GetModuleBaseName(processHandle, new HandleRef(null, moduleHandle), baseName, baseName.Capacity * 2);
if (ret == 0) throw new Win32Exception();
moduleInfo.baseName = baseName.ToString();
StringBuilder fileName = new StringBuilder(1024);
ret = NativeMethods.GetModuleFileNameEx(processHandle, new HandleRef(null, moduleHandle), fileName, fileName.Capacity * 2);
if (ret == 0) throw new Win32Exception();
moduleInfo.fileName = fileName.ToString();
// smss.exe is started before the win32 subsystem so it get this funny "\systemroot\.." path.
// We change this to the actual path by appending "smss.exe" to GetSystemDirectory()
if (string.Compare(moduleInfo.fileName, "\\SystemRoot\\System32\\smss.exe", StringComparison.OrdinalIgnoreCase) == 0) {
moduleInfo.fileName = Path.Combine(Environment.SystemDirectory, "smss.exe");
}
// Avoid returning Unicode-style long string paths. IO methods cannot handle them.
if (moduleInfo.fileName != null
&& moduleInfo.fileName.Length >= 4
&& moduleInfo.fileName.StartsWith(@"\\?\", StringComparison.Ordinal)) {
moduleInfo.fileName = moduleInfo.fileName.Substring(4);
}
moduleInfos.Add(moduleInfo);
//
// If the user is only interested in the main module, break now.
// This avoid some waste of time. In addition, if the application unloads a DLL
// we will not get an exception.
//
if( firstModuleOnly) { break; }
}
ModuleInfo[] temp = new ModuleInfo[moduleInfos.Count];
moduleInfos.CopyTo(temp, 0);
return temp;
}
finally {
Debug.WriteLineIf(Process.processTracing.TraceVerbose, "Process - CloseHandle(process)");
if (!processHandle.IsInvalid ) {
processHandle.Close();
}
}
}
[ResourceExposure(ResourceScope.Process)]
[ResourceConsumption(ResourceScope.Process)]
public static int GetProcessIdFromHandle(SafeProcessHandle processHandle) {
NativeMethods.NtProcessBasicInfo info = new NativeMethods.NtProcessBasicInfo();
int status = NativeMethods.NtQueryInformationProcess(processHandle, NativeMethods.NtQueryProcessBasicInfo, info, (int)Marshal.SizeOf(info), null);
if (status != 0) {
throw new InvalidOperationException(SR.GetString(SR.CantGetProcessId), new Win32Exception(status));
}
// We should change the signature of this function and ID property in process class.
return info.UniqueProcessId.ToInt32();
}
public static ProcessInfo[] GetProcessInfos(string machineName, bool isRemoteMachine) {
// We demand unmanaged code here because PerformanceCounterLib doesn't demand
// anything. This is the only place we do GetPerformanceCounterLib, and it isn't cached.
new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Demand();
PerformanceCounterLib library = null;
try {
library = PerformanceCounterLib.GetPerformanceCounterLib(machineName, new CultureInfo(0x009));
return GetProcessInfos(library);
}
catch(Exception e) {
if( isRemoteMachine) {
throw new InvalidOperationException(SR.GetString(SR.CouldntConnectToRemoteMachine), e);
}
else {
throw e;
}
}
catch {
if(isRemoteMachine) {
throw new InvalidOperationException(SR.GetString(SR.CouldntConnectToRemoteMachine));
}
else {
throw;
}
}
// We don't want to call library.Close() here because that would cause us to unload all of the perflibs.
// On the next call to GetProcessInfos, we'd have to load them all up again, which is SLOW!
}
static ProcessInfo[] GetProcessInfos(PerformanceCounterLib library) {
ProcessInfo[] processInfos = new ProcessInfo[0] ;
byte[] dataPtr = null;
int retryCount = 5;
while (processInfos.Length == 0 && retryCount != 0) {
try {
dataPtr = library.GetPerformanceData(PerfCounterQueryString);
processInfos = GetProcessInfos(library, ProcessPerfCounterId, ThreadPerfCounterId, dataPtr);
}
catch (Exception e) {
throw new InvalidOperationException(SR.GetString(SR.CouldntGetProcessInfos), e);
}
catch {
throw new InvalidOperationException(SR.GetString(SR.CouldntGetProcessInfos));
}
--retryCount;
}
if (processInfos.Length == 0)
throw new InvalidOperationException(SR.GetString(SR.ProcessDisabled));
return processInfos;
}
unsafe static ProcessInfo[] GetProcessInfos(PerformanceCounterLib library, int processIndex, int threadIndex, byte[] data) {
Debug.WriteLineIf(Process.processTracing.TraceVerbose, "GetProcessInfos()");
Hashtable processInfos = new Hashtable();
ArrayList threadInfos = new ArrayList();
fixed (byte* dataPtr = data) {
IntPtr dataBlockPtr = new IntPtr( (void*) dataPtr);
NativeMethods.PERF_DATA_BLOCK dataBlock = new NativeMethods.PERF_DATA_BLOCK();
Marshal.PtrToStructure(dataBlockPtr, dataBlock);
IntPtr typePtr = (IntPtr)((long)dataBlockPtr + dataBlock.HeaderLength);
NativeMethods.PERF_INSTANCE_DEFINITION instance = new NativeMethods.PERF_INSTANCE_DEFINITION();
NativeMethods.PERF_COUNTER_BLOCK counterBlock = new NativeMethods.PERF_COUNTER_BLOCK();
for (int i = 0; i < dataBlock.NumObjectTypes; i++) {
NativeMethods.PERF_OBJECT_TYPE type = new NativeMethods.PERF_OBJECT_TYPE();
Marshal.PtrToStructure(typePtr, type);
IntPtr instancePtr = (IntPtr)((long)typePtr + type.DefinitionLength);
IntPtr counterPtr = (IntPtr)((long)typePtr + type.HeaderLength);
ArrayList counterList = new ArrayList();
for (int j = 0; j < type.NumCounters; j++) {
NativeMethods.PERF_COUNTER_DEFINITION counter = new NativeMethods.PERF_COUNTER_DEFINITION();
Marshal.PtrToStructure(counterPtr, counter);
string counterName = library.GetCounterName(counter.CounterNameTitleIndex);
if (type.ObjectNameTitleIndex == processIndex)
counter.CounterNameTitlePtr = (int)GetValueId(counterName);
else if (type.ObjectNameTitleIndex == threadIndex)
counter.CounterNameTitlePtr = (int)GetValueId(counterName);
counterList.Add(counter);
counterPtr = (IntPtr)((long)counterPtr + counter.ByteLength);
}
NativeMethods.PERF_COUNTER_DEFINITION[] counters = new NativeMethods.PERF_COUNTER_DEFINITION[counterList.Count];
counterList.CopyTo(counters, 0);
for (int j = 0; j < type.NumInstances; j++) {
Marshal.PtrToStructure(instancePtr, instance);
IntPtr namePtr = (IntPtr)((long)instancePtr + instance.NameOffset);
string instanceName = Marshal.PtrToStringUni(namePtr);
if (instanceName.Equals("_Total")) continue;
IntPtr counterBlockPtr = (IntPtr)((long)instancePtr + instance.ByteLength);
Marshal.PtrToStructure(counterBlockPtr, counterBlock);
if (type.ObjectNameTitleIndex == processIndex) {
ProcessInfo processInfo = GetProcessInfo(type, (IntPtr)((long)instancePtr + instance.ByteLength), counters);
if (processInfo.processId == 0 && string.Compare(instanceName, "Idle", StringComparison.OrdinalIgnoreCase) != 0) {
// Sometimes we'll get a process structure that is not completely filled in.
// We can catch some of these by looking for non-"idle" processes that have id 0
// and ignoring those.
Debug.WriteLineIf(Process.processTracing.TraceVerbose, "GetProcessInfos() - found a non-idle process with id 0; ignoring.");
}
else {
if (processInfos[processInfo.processId] != null) {
// We've found two entries in the perfcounters that claim to be the
// same process. We throw an exception. Is this really going to be
// helpfull to the user? Should we just ignore?
Debug.WriteLineIf(Process.processTracing.TraceVerbose, "GetProcessInfos() - found a duplicate process id");
}
else {
// the performance counters keep a 15 character prefix of the exe name, and then delete the ".exe",
// if it's in the first 15. The problem is that sometimes that will leave us with part of ".exe"
// at the end. If instanceName ends in ".", ".e", or ".ex" we remove it.
string processName = instanceName;
if (processName.Length == 15) {
if (instanceName.EndsWith(".", StringComparison.Ordinal )) processName = instanceName.Substring(0, 14);
else if (instanceName.EndsWith(".e", StringComparison.Ordinal )) processName = instanceName.Substring(0, 13);
else if (instanceName.EndsWith(".ex", StringComparison.Ordinal)) processName = instanceName.Substring(0, 12);
}
processInfo.processName = processName;
processInfos.Add(processInfo.processId, processInfo);
}
}
}
else if (type.ObjectNameTitleIndex == threadIndex) {
ThreadInfo threadInfo = GetThreadInfo(type, (IntPtr)((long)instancePtr + instance.ByteLength), counters);
if (threadInfo.threadId != 0) threadInfos.Add(threadInfo);
}
instancePtr = (IntPtr)((long)instancePtr + instance.ByteLength + counterBlock.ByteLength);
}
typePtr = (IntPtr)((long)typePtr + type.TotalByteLength);
}
}
for (int i = 0; i < threadInfos.Count; i++) {
ThreadInfo threadInfo = (ThreadInfo)threadInfos[i];
ProcessInfo processInfo = (ProcessInfo)processInfos[threadInfo.processId];
if (processInfo != null) {
processInfo.threadInfoList.Add(threadInfo);
}
//else {
// throw new InvalidOperationException(SR.GetString(SR.ProcessNotFound, threadInfo.threadId.ToString(), threadInfo.processId.ToString()));
//}
}
ProcessInfo[] temp = new ProcessInfo[processInfos.Values.Count];
processInfos.Values.CopyTo(temp, 0);
return temp;
}
static ThreadInfo GetThreadInfo(NativeMethods.PERF_OBJECT_TYPE type, IntPtr instancePtr, NativeMethods.PERF_COUNTER_DEFINITION[] counters) {
ThreadInfo threadInfo = new ThreadInfo();
for (int i = 0; i < counters.Length; i++) {
NativeMethods.PERF_COUNTER_DEFINITION counter = counters[i];
long value = ReadCounterValue(counter.CounterType, (IntPtr)((long)instancePtr + counter.CounterOffset));
switch ((ValueId)counter.CounterNameTitlePtr) {
case ValueId.ProcessId:
threadInfo.processId = (int)value;
break;
case ValueId.ThreadId:
threadInfo.threadId = (int)value;
break;
case ValueId.BasePriority:
threadInfo.basePriority = (int)value;
break;
case ValueId.CurrentPriority:
threadInfo.currentPriority = (int)value;
break;
case ValueId.StartAddress:
threadInfo.startAddress = (IntPtr)value;
break;
case ValueId.ThreadState:
threadInfo.threadState = (ThreadState)value;
break;
case ValueId.ThreadWaitReason:
threadInfo.threadWaitReason = GetThreadWaitReason((int)value);
break;
}
}
return threadInfo;
}
internal static ThreadWaitReason GetThreadWaitReason(int value) {
switch (value) {
case 0:
case 7: return ThreadWaitReason.Executive;
case 1:
case 8: return ThreadWaitReason.FreePage;
case 2:
case 9: return ThreadWaitReason.PageIn;
case 3:
case 10: return ThreadWaitReason.SystemAllocation;
case 4:
case 11: return ThreadWaitReason.ExecutionDelay;
case 5:
case 12: return ThreadWaitReason.Suspended;
case 6:
case 13: return ThreadWaitReason.UserRequest;
case 14: return ThreadWaitReason.EventPairHigh;;
case 15: return ThreadWaitReason.EventPairLow;
case 16: return ThreadWaitReason.LpcReceive;
case 17: return ThreadWaitReason.LpcReply;
case 18: return ThreadWaitReason.VirtualMemory;
case 19: return ThreadWaitReason.PageOut;
default: return ThreadWaitReason.Unknown;
}
}
static ProcessInfo GetProcessInfo(NativeMethods.PERF_OBJECT_TYPE type, IntPtr instancePtr, NativeMethods.PERF_COUNTER_DEFINITION[] counters) {
ProcessInfo processInfo = new ProcessInfo();
for (int i = 0; i < counters.Length; i++) {
NativeMethods.PERF_COUNTER_DEFINITION counter = counters[i];
long value = ReadCounterValue(counter.CounterType, (IntPtr)((long)instancePtr + counter.CounterOffset));
switch ((ValueId)counter.CounterNameTitlePtr) {
case ValueId.ProcessId:
processInfo.processId = (int)value;
break;
case ValueId.HandleCount:
processInfo.handleCount = (int)value;
break;
case ValueId.PoolPagedBytes:
processInfo.poolPagedBytes = (int)value;
break;
case ValueId.PoolNonpagedBytes:
processInfo.poolNonpagedBytes = (int)value;
break;
case ValueId.VirtualBytes:
processInfo.virtualBytes = (int)value;
break;
case ValueId.VirtualBytesPeak:
processInfo.virtualBytesPeak = (int)value;
break;
case ValueId.WorkingSetPeak:
processInfo.workingSetPeak = (int)value;
break;
case ValueId.WorkingSet:
processInfo.workingSet = (int)value;
break;
case ValueId.PageFileBytesPeak:
processInfo.pageFileBytesPeak = (int)value;
break;
case ValueId.PageFileBytes:
processInfo.pageFileBytes = (int)value;
break;
case ValueId.PrivateBytes:
processInfo.privateBytes = (int)value;
break;
case ValueId.BasePriority:
processInfo.basePriority = (int)value;
break;
}
}
return processInfo;
}
static ValueId GetValueId(string counterName) {
if (counterName != null) {
object id = valueIds[counterName];
if (id != null)
return(ValueId)id;
}
return ValueId.Unknown;
}
static long ReadCounterValue(int counterType, IntPtr dataPtr) {
if ((counterType & NativeMethods.NtPerfCounterSizeLarge) != 0)
return Marshal.ReadInt64(dataPtr);
else
return(long)Marshal.ReadInt32(dataPtr);
}
enum ValueId {
Unknown = -1,
HandleCount,
PoolPagedBytes,
PoolNonpagedBytes,
ElapsedTime,
VirtualBytesPeak,
VirtualBytes,
PrivateBytes,
PageFileBytes,
PageFileBytesPeak,
WorkingSetPeak,
WorkingSet,
ThreadId,
ProcessId,
BasePriority,
CurrentPriority,
UserTime,
PrivilegedTime,
StartAddress,
ThreadState,
ThreadWaitReason
}
}
internal static class NtProcessInfoHelper {
private static int GetNewBufferSize(int existingBufferSize, int requiredSize) {
if( requiredSize == 0) {
//
// On some old OS like win2000, requiredSize will not be set if the buffer
// passed to NtQuerySystemInformation is not enough.
//
int newSize = existingBufferSize * 2;
if ( newSize < existingBufferSize ) {
// In reality, we should never overflow.
// Adding the code here just in case it happens.
throw new OutOfMemoryException();
}
return newSize;
}
else {
// allocating a few more kilo bytes just in case there are some new process
// kicked in since new call to NtQuerySystemInformation
return requiredSize + 1024 * 10;
}
}
#pragma warning disable 169
public static ProcessInfo[] GetProcessInfos() {
// On a normal machine, 30k to 40K will be enough.
// We use 128K here to tolerate mutilple connections to a machine.
int bufferSize = 128 * 1024;
#if DEBUG
// on debug build, use a smaller buffer size to make sure we hit the retrying code path
bufferSize = 1024;
#endif
int requiredSize = 0;
int status;
ProcessInfo[] processInfos;
GCHandle bufferHandle = new GCHandle();
try {
// Retry until we get all the data
do {
Byte[] buffer = new Byte[bufferSize];
bufferHandle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
status = NativeMethods.NtQuerySystemInformation(
NativeMethods.NtQuerySystemProcessInformation,
bufferHandle.AddrOfPinnedObject(),
bufferSize,
out requiredSize);
if ((uint)status == NativeMethods.STATUS_INFO_LENGTH_MISMATCH) {
if (bufferHandle.IsAllocated) bufferHandle.Free();
bufferSize = GetNewBufferSize(bufferSize, requiredSize);
}
} while ((uint)status == NativeMethods.STATUS_INFO_LENGTH_MISMATCH);
if (status < 0) { // see definition of NT_SUCCESS(Status) in SDK
throw new InvalidOperationException(SR.GetString(SR.CouldntGetProcessInfos), new Win32Exception(status));
}
// Parse the data block to get process information
processInfos = GetProcessInfos(bufferHandle.AddrOfPinnedObject());
}
finally {
if (bufferHandle.IsAllocated) bufferHandle.Free();
}
return processInfos;
}
unsafe static ProcessInfo[] GetProcessInfos(IntPtr dataPtr) {
// 60 is a reasonable number for processes on a normal machine.
Hashtable processInfos = new Hashtable(60);
long totalOffset = 0;
while(true) {
IntPtr currentPtr = (IntPtr)((long)dataPtr + totalOffset);
SystemProcessInformation pi = new SystemProcessInformation();
Marshal.PtrToStructure(currentPtr, pi);
// get information for a process
ProcessInfo processInfo = new ProcessInfo();
// Process ID shouldn't overflow. OS API GetCurrentProcessID returns DWORD.
processInfo.processId = pi.UniqueProcessId.ToInt32();
processInfo.handleCount = (int)pi.HandleCount;
processInfo.sessionId = (int)pi.SessionId;
processInfo.poolPagedBytes = (long)pi.QuotaPagedPoolUsage;;
processInfo.poolNonpagedBytes = (long)pi.QuotaNonPagedPoolUsage;
processInfo.virtualBytes = (long)pi.VirtualSize;
processInfo.virtualBytesPeak = (long)pi.PeakVirtualSize;
processInfo.workingSetPeak = (long)pi.PeakWorkingSetSize;
processInfo.workingSet = (long)pi.WorkingSetSize;
processInfo.pageFileBytesPeak = (long)pi.PeakPagefileUsage;
processInfo.pageFileBytes = (long)pi.PagefileUsage;
processInfo.privateBytes = (long)pi.PrivatePageCount;
processInfo.basePriority = pi.BasePriority;
if( pi.NamePtr == IntPtr.Zero) {
if( processInfo.processId == NtProcessManager.SystemProcessID) {
processInfo.processName = "System";
}
else if( processInfo.processId == NtProcessManager.IdleProcessID) {
processInfo.processName = "Idle";
}
else {
// for normal process without name, using the process ID.
processInfo.processName = processInfo.processId.ToString(CultureInfo.InvariantCulture);
}
}
else {
string processName = GetProcessShortName((char *)pi.NamePtr.ToPointer(), pi.NameLength/2);
//
// On old operating system (NT4 and windows 2000), the process name might be truncated to 15
// characters. For example, aspnet_admin.exe will show up in performance counter as aspnet_admin.ex.
// Process class try to return a nicer name. We used to get the main module name for a process and
// use that as the process name. However normal user doesn't have access to module information,
// so normal user will see an exception when we try to get a truncated process name.
//
if (ProcessManager.IsOSOlderThanXP && (processName.Length == 15)) {
if (processName.EndsWith(".", StringComparison.OrdinalIgnoreCase)) {
processName = processName.Substring(0, 14);
}
else if (processName.EndsWith(".e", StringComparison.OrdinalIgnoreCase)) {
processName = processName.Substring(0, 13);
}
else if (processName.EndsWith(".ex", StringComparison.OrdinalIgnoreCase)) {
processName = processName.Substring(0, 12);
}
}
processInfo.processName = processName;
}
// get the threads for current process
processInfos[processInfo.processId] = processInfo;
currentPtr = (IntPtr)((long)currentPtr + Marshal.SizeOf(pi));
int i = 0;
while( i < pi.NumberOfThreads) {
SystemThreadInformation ti = new SystemThreadInformation();
Marshal.PtrToStructure(currentPtr, ti);
ThreadInfo threadInfo = new ThreadInfo();
threadInfo.processId = (int)ti.UniqueProcess;
threadInfo.threadId = (int)ti.UniqueThread;
threadInfo.basePriority = ti.BasePriority;
threadInfo.currentPriority = ti.Priority;
threadInfo.startAddress = ti.StartAddress;
threadInfo.threadState = (ThreadState)ti.ThreadState;
threadInfo.threadWaitReason = NtProcessManager.GetThreadWaitReason((int)ti.WaitReason);
processInfo.threadInfoList.Add(threadInfo);
currentPtr = (IntPtr)((long)currentPtr + Marshal.SizeOf(ti));
i++;
}
if (pi.NextEntryOffset == 0) {
break;
}
totalOffset += pi.NextEntryOffset;
}
ProcessInfo[] temp = new ProcessInfo[processInfos.Values.Count];
processInfos.Values.CopyTo(temp, 0);
return temp;
}
// This function generates the short form of process name.
//
// This is from GetProcessShortName in NT code base.
// Check base\screg\winreg\perfdlls\process\perfsprc.c for details.
internal unsafe static string GetProcessShortName(char* name, int length) {
char* pSlash = name;
char* pPeriod = name;
char* pThisChar = name;
int wThisChar = 0;
while (*pThisChar != 0) { // go until null
if (*pThisChar == '\\') {
pSlash = pThisChar;
} else if (*pThisChar == '.') {
pPeriod = pThisChar;
}
pThisChar++; // point to next char
wThisChar++;
if (wThisChar >= length) {
break;
}
}
// if pPeriod is still pointing to the beginning of the
// string, then no period was found
if (pPeriod == name) {
pPeriod = pThisChar; // set to end of string;
} else {
// if a period was found, then see if the extension is
// .EXE, if so drop it, if not, then use end of string
// (i.e. include extension in name)
String extension = new String(pPeriod);
if(!String.Equals(".exe", extension, StringComparison.OrdinalIgnoreCase) ) {
pPeriod = pThisChar;
}
}
if (*pSlash == '\\') { // if pSlash is pointing to a slash, then
pSlash++; // point to character next to slash
}
// copy characters between period (or end of string) and
// slash (or start of string) to make image name
int wStringSize = (int)(pPeriod - pSlash); // size in bytes
return new String(pSlash, 0, wStringSize);
}
[StructLayout(LayoutKind.Sequential)]
internal class SystemProcessInformation {
internal int NextEntryOffset;
internal uint NumberOfThreads;
long SpareLi1;
long SpareLi2;
long SpareLi3;
long CreateTime;
long UserTime;
long KernelTime;
internal ushort NameLength; // UNICODE_STRING
internal ushort MaximumNameLength;
internal IntPtr NamePtr; // This will point into the data block returned by NtQuerySystemInformation
internal int BasePriority;
internal IntPtr UniqueProcessId;
internal IntPtr InheritedFromUniqueProcessId;
internal uint HandleCount;
internal uint SessionId;
internal IntPtr PageDirectoryBase;
internal IntPtr PeakVirtualSize;
internal IntPtr VirtualSize;
internal uint PageFaultCount;
internal IntPtr PeakWorkingSetSize;
internal IntPtr WorkingSetSize;
internal IntPtr QuotaPeakPagedPoolUsage;
internal IntPtr QuotaPagedPoolUsage;
internal IntPtr QuotaPeakNonPagedPoolUsage;
internal IntPtr QuotaNonPagedPoolUsage;
internal IntPtr PagefileUsage;
internal IntPtr PeakPagefileUsage;
internal IntPtr PrivatePageCount;
long ReadOperationCount;
long WriteOperationCount;
long OtherOperationCount;
long ReadTransferCount;
long WriteTransferCount;
long OtherTransferCount;
}
[StructLayout(LayoutKind.Sequential)]
internal class SystemThreadInformation {
long KernelTime;
long UserTime;
long CreateTime;
uint WaitTime;
internal IntPtr StartAddress;
internal IntPtr UniqueProcess;
internal IntPtr UniqueThread;
internal int Priority;
internal int BasePriority;
internal uint ContextSwitches;
internal uint ThreadState;
internal uint WaitReason;
}
#pragma warning restore 169
}
}
#endif // !FEATURE_PAL
// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.
#if !FEATURE_PAL
namespace System.Diagnostics {
using System.Text;
using System.Threading;
using System.Runtime.InteropServices;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Diagnostics;
using System;
using System.Collections;
using System.IO;
using Microsoft.Win32;
using Microsoft.Win32.SafeHandles;
using System.Collections.Specialized;
using System.Globalization;
using System.Security;
using System.Security.Permissions;
using System.Security.Principal;
using System.Runtime.Versioning;
///
/// This class finds the main window of a process. It needs to be
/// class because we need to store state while searching the set
/// of windows.
///
///
internal class MainWindowFinder {
IntPtr bestHandle;
int processId;
public IntPtr FindMainWindow(int processId) {
bestHandle = (IntPtr)0;
this.processId = processId;
NativeMethods.EnumThreadWindowsCallback callback = new NativeMethods.EnumThreadWindowsCallback(this.EnumWindowsCallback);
NativeMethods.EnumWindows(callback, IntPtr.Zero);
GC.KeepAlive(callback);
return bestHandle;
}
bool IsMainWindow(IntPtr handle) {
if (NativeMethods.GetWindow(new HandleRef(this, handle), NativeMethods.GW_OWNER) != (IntPtr)0 || !NativeMethods.IsWindowVisible(new HandleRef(this, handle)))
return false;
// [....]: should we use no window title to mean not a main window? (task man does)
/*
int length = NativeMethods.GetWindowTextLength(handle) * 2;
StringBuilder builder = new StringBuilder(length);
if (NativeMethods.GetWindowText(handle, builder, builder.Capacity) == 0)
return false;
if (builder.ToString() == string.Empty)
return false;
*/
return true;
}
bool EnumWindowsCallback(IntPtr handle, IntPtr extraParameter) {
int processId;
NativeMethods.GetWindowThreadProcessId(new HandleRef(this, handle), out processId);
if (processId == this.processId) {
if (IsMainWindow(handle)) {
bestHandle = handle;
return false;
}
}
return true;
}
}
///
/// This static class is a platform independent Api for querying information
/// about processes, threads and modules. It delegates to the platform
/// specific classes WinProcessManager for Win9x and NtProcessManager
/// for WinNt.
///
///
internal static class ProcessManager {
static ProcessManager() {
// In order to query information (OpenProcess) on some protected processes
// like csrss, we need SeDebugPrivilege privilege.
// After removing the depenecy on Performance Counter, we don't have a chance
// to run the code in CLR performance counter to ask for this privilege.
// So we will try to get the privilege here.
// We could fail if the user account doesn't have right to do this, but that's fair.
NativeMethods.LUID luid = new NativeMethods.LUID();
if (!NativeMethods.LookupPrivilegeValue(null, "SeDebugPrivilege", out luid)) {
return;
}
IntPtr tokenHandle = IntPtr.Zero;
try {
if( !NativeMethods.OpenProcessToken(
new HandleRef(null, NativeMethods.GetCurrentProcess()),
(int)TokenAccessLevels.AdjustPrivileges,
out tokenHandle)) {
return;
}
NativeMethods.TokenPrivileges tp = new NativeMethods.TokenPrivileges();
tp.PrivilegeCount = 1;
tp.Luid = luid;
tp.Attributes = NativeMethods.SE_PRIVILEGE_ENABLED;
// AdjustTokenPrivileges can return true even if it didn't succeed (when ERROR_NOT_ALL_ASSIGNED is returned).
NativeMethods.AdjustTokenPrivileges(new HandleRef(null,tokenHandle), false, tp, 0, IntPtr.Zero, IntPtr.Zero);
}
finally {
if( tokenHandle != IntPtr.Zero) {
SafeNativeMethods.CloseHandle(new HandleRef(null,tokenHandle));
}
}
}
public static bool IsNt {
get {
return Environment.OSVersion.Platform == PlatformID.Win32NT;
}
}
public static bool IsOSOlderThanXP {
get {
return Environment.OSVersion.Version.Major < 5 ||
(Environment.OSVersion.Version.Major == 5 && Environment.OSVersion.Version.Minor == 0);
}
}
public static ProcessInfo[] GetProcessInfos(string machineName) {
bool isRemoteMachine = IsRemoteMachine(machineName);
if (IsNt) {
// Do not use performance counter for local machine with Win2000 and above
if( !isRemoteMachine &&
(Environment.OSVersion.Version.Major >= 5 )) {
return NtProcessInfoHelper.GetProcessInfos();
}
return NtProcessManager.GetProcessInfos(machineName, isRemoteMachine);
}
else {
if (isRemoteMachine)
throw new PlatformNotSupportedException(SR.GetString(SR.WinNTRequiredForRemote));
return WinProcessManager.GetProcessInfos();
}
}
public static int[] GetProcessIds() {
if (IsNt)
return NtProcessManager.GetProcessIds();
else {
return WinProcessManager.GetProcessIds();
}
}
public static int[] GetProcessIds(string machineName) {
if (IsRemoteMachine(machineName)) {
if (IsNt) {
return NtProcessManager.GetProcessIds(machineName, true);
}
else {
throw new PlatformNotSupportedException(SR.GetString(SR.WinNTRequiredForRemote));
}
}
else {
return GetProcessIds();
}
}
public static bool IsProcessRunning(int processId, string machineName) {
return IsProcessRunning(processId, GetProcessIds(machineName));
}
public static bool IsProcessRunning(int processId) {
return IsProcessRunning(processId, GetProcessIds());
}
static bool IsProcessRunning(int processId, int[] processIds) {
for (int i = 0; i < processIds.Length; i++)
if (processIds[i] == processId)
return true;
return false;
}
public static int GetProcessIdFromHandle(SafeProcessHandle processHandle) {
if (IsNt)
return NtProcessManager.GetProcessIdFromHandle(processHandle);
else
throw new PlatformNotSupportedException(SR.GetString(SR.WinNTRequired));
}
public static IntPtr GetMainWindowHandle(ProcessInfo processInfo) {
MainWindowFinder finder = new MainWindowFinder();
return finder.FindMainWindow(processInfo.processId);
}
public static ModuleInfo[] GetModuleInfos(int processId) {
if (IsNt)
return NtProcessManager.GetModuleInfos(processId);
else
return WinProcessManager.GetModuleInfos(processId);
}
public static SafeProcessHandle OpenProcess(int processId, int access, bool throwIfExited) {
SafeProcessHandle processHandle = NativeMethods.OpenProcess(access, false, processId);
int result = Marshal.GetLastWin32Error();
if (!processHandle.IsInvalid) {
return processHandle;
}
if (processId == 0) {
throw new Win32Exception(5);
}
// If the handle is invalid because the process has exited, only throw an exception if throwIfExited is true.
if (!IsProcessRunning(processId)) {
if (throwIfExited) {
throw new InvalidOperationException(SR.GetString(SR.ProcessHasExited, processId.ToString(CultureInfo.CurrentCulture)));
}
else {
return SafeProcessHandle.InvalidHandle;
}
}
throw new Win32Exception(result);
}
public static SafeThreadHandle OpenThread(int threadId, int access) {
try {
SafeThreadHandle threadHandle = NativeMethods.OpenThread(access, false, threadId);
int result = Marshal.GetLastWin32Error();
if (threadHandle.IsInvalid) {
if (result == NativeMethods.ERROR_INVALID_PARAMETER)
throw new InvalidOperationException(SR.GetString(SR.ThreadExited, threadId.ToString(CultureInfo.CurrentCulture)));
throw new Win32Exception(result);
}
return threadHandle;
}
catch (EntryPointNotFoundException x) {
throw new PlatformNotSupportedException(SR.GetString(SR.Win2000Required), x);
}
}
public static bool IsRemoteMachine(string machineName) {
if (machineName == null)
throw new ArgumentNullException("machineName");
if (machineName.Length == 0)
throw new ArgumentException(SR.GetString(SR.InvalidParameter, "machineName", machineName));
string baseName;
if (machineName.StartsWith("\\", StringComparison.Ordinal))
baseName = machineName.Substring(2);
else
baseName = machineName;
if (baseName.Equals(".")) return false;
StringBuilder sb = new StringBuilder(256);
SafeNativeMethods.GetComputerName(sb, new int[] {sb.Capacity});
string computerName = sb.ToString();
if (String.Compare(computerName, baseName, StringComparison.OrdinalIgnoreCase) == 0) return false;
return true;
}
}
///
/// This static class provides the process api for the Win9x platform.
/// We use the toolhelp32 api to query process, thread and module information.
///
///
internal static class WinProcessManager {
// This is expensive. We should specialize getprocessinfos and only get
// the ids instead of getting all the info and then copying the ids out.
public static int[] GetProcessIds() {
ProcessInfo[] infos = GetProcessInfos();
int[] ids = new int[infos.Length];
for (int i = 0; i < infos.Length; i++) {
ids[i] = infos[i].processId;
}
return ids;
}
public static ProcessInfo[] GetProcessInfos() {
IntPtr handle = (IntPtr)(-1);
GCHandle bufferHandle = new GCHandle();
ArrayList threadInfos = new ArrayList();
Hashtable processInfos = new Hashtable();
try {
handle = NativeMethods.CreateToolhelp32Snapshot(NativeMethods.TH32CS_SNAPPROCESS | NativeMethods.TH32CS_SNAPTHREAD, 0);
if (handle == (IntPtr)(-1)) throw new Win32Exception();
int entrySize = (int)Marshal.SizeOf(typeof(NativeMethods.WinProcessEntry));
int bufferSize = entrySize + NativeMethods.WinProcessEntry.sizeofFileName;
int[] buffer = new int[bufferSize / 4];
bufferHandle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
IntPtr bufferPtr = bufferHandle.AddrOfPinnedObject();
Marshal.WriteInt32(bufferPtr, bufferSize);
HandleRef handleRef = new HandleRef(null, handle);
if (NativeMethods.Process32First(handleRef, bufferPtr)) {
do {
NativeMethods.WinProcessEntry process = new NativeMethods.WinProcessEntry();
Marshal.PtrToStructure(bufferPtr, process);
ProcessInfo processInfo = new ProcessInfo();
String name = Marshal.PtrToStringAnsi((IntPtr)((long)bufferPtr + entrySize));
processInfo.processName = Path.ChangeExtension(Path.GetFileName(name), null);
processInfo.handleCount = process.cntUsage;
processInfo.processId = process.th32ProcessID;
processInfo.basePriority = process.pcPriClassBase;
processInfo.mainModuleId = process.th32ModuleID;
processInfos.Add(processInfo.processId, processInfo);
Marshal.WriteInt32(bufferPtr, bufferSize);
}
while (NativeMethods.Process32Next(handleRef, bufferPtr));
}
NativeMethods.WinThreadEntry thread = new NativeMethods.WinThreadEntry();
thread.dwSize = Marshal.SizeOf(thread);
if (NativeMethods.Thread32First(handleRef, thread)) {
do {
ThreadInfo threadInfo = new ThreadInfo();
threadInfo.threadId = thread.th32ThreadID;
threadInfo.processId = thread.th32OwnerProcessID;
threadInfo.basePriority = thread.tpBasePri;
threadInfo.currentPriority = thread.tpBasePri + thread.tpDeltaPri;
threadInfos.Add(threadInfo);
}
while (NativeMethods.Thread32Next(handleRef, thread));
}
for (int i = 0; i < threadInfos.Count; i++) {
ThreadInfo threadInfo = (ThreadInfo)threadInfos[i];
ProcessInfo processInfo = (ProcessInfo)processInfos[threadInfo.processId];
if (processInfo != null)
processInfo.threadInfoList.Add(threadInfo);
//else
// throw new InvalidOperationException(SR.GetString(SR.ProcessNotFound, threadInfo.threadId.ToString(), threadInfo.processId.ToString()));
}
}
finally {
if (bufferHandle.IsAllocated) bufferHandle.Free();
Debug.WriteLineIf(Process.processTracing.TraceVerbose, "Process - CloseHandle(toolhelp32 snapshot handle)");
if (handle != (IntPtr)(-1)) SafeNativeMethods.CloseHandle(new HandleRef(null, handle));
}
ProcessInfo[] temp = new ProcessInfo[processInfos.Values.Count];
processInfos.Values.CopyTo(temp, 0);
return temp;
}
public static ModuleInfo[] GetModuleInfos(int processId) {
IntPtr handle = (IntPtr)(-1);
GCHandle bufferHandle = new GCHandle();
ArrayList moduleInfos = new ArrayList();
try {
handle = NativeMethods.CreateToolhelp32Snapshot(NativeMethods.TH32CS_SNAPMODULE, processId);
if (handle == (IntPtr)(-1)) throw new Win32Exception();
int entrySize = Marshal.SizeOf(typeof(NativeMethods.WinModuleEntry));
int bufferSize = entrySize + NativeMethods.WinModuleEntry.sizeofFileName + NativeMethods.WinModuleEntry.sizeofModuleName;
int[] buffer = new int[bufferSize / 4];
bufferHandle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
IntPtr bufferPtr = bufferHandle.AddrOfPinnedObject();
Marshal.WriteInt32(bufferPtr, bufferSize);
HandleRef handleRef = new HandleRef(null, handle);
if (NativeMethods.Module32First(handleRef, bufferPtr)) {
do {
NativeMethods.WinModuleEntry module = new NativeMethods.WinModuleEntry();
Marshal.PtrToStructure(bufferPtr, module);
ModuleInfo moduleInfo = new ModuleInfo();
moduleInfo.baseName = Marshal.PtrToStringAnsi((IntPtr)((long)bufferPtr + entrySize));
moduleInfo.fileName = Marshal.PtrToStringAnsi((IntPtr)((long)bufferPtr + entrySize + NativeMethods.WinModuleEntry.sizeofModuleName));
moduleInfo.baseOfDll = module.modBaseAddr;
moduleInfo.sizeOfImage = module.modBaseSize;
moduleInfo.Id = module.th32ModuleID;
moduleInfos.Add(moduleInfo);
Marshal.WriteInt32(bufferPtr, bufferSize);
}
while (NativeMethods.Module32Next(handleRef, bufferPtr));
}
}
finally {
if (bufferHandle.IsAllocated) bufferHandle.Free();
Debug.WriteLineIf(Process.processTracing.TraceVerbose, "Process - CloseHandle(toolhelp32 snapshot handle)");
if (handle != (IntPtr)(-1)) SafeNativeMethods.CloseHandle(new HandleRef(null, handle));
}
ModuleInfo[] temp = new ModuleInfo[moduleInfos.Count];
moduleInfos.CopyTo(temp, 0);
return temp;
}
}
///
/// This static class provides the process api for the WinNt platform.
/// We use the performance counter api to query process and thread
/// information. Module information is obtained using PSAPI.
///
///
internal static class NtProcessManager {
private const int ProcessPerfCounterId = 230;
private const int ThreadPerfCounterId = 232;
private const string PerfCounterQueryString = "230 232";
internal const int IdleProcessID = 0;
static Hashtable valueIds;
static NtProcessManager() {
valueIds = new Hashtable();
valueIds.Add("Handle Count", ValueId.HandleCount);
valueIds.Add("Pool Paged Bytes", ValueId.PoolPagedBytes);
valueIds.Add("Pool Nonpaged Bytes", ValueId.PoolNonpagedBytes);
valueIds.Add("Elapsed Time", ValueId.ElapsedTime);
valueIds.Add("Virtual Bytes Peak", ValueId.VirtualBytesPeak);
valueIds.Add("Virtual Bytes", ValueId.VirtualBytes);
valueIds.Add("Private Bytes", ValueId.PrivateBytes);
valueIds.Add("Page File Bytes", ValueId.PageFileBytes);
valueIds.Add("Page File Bytes Peak", ValueId.PageFileBytesPeak);
valueIds.Add("Working Set Peak", ValueId.WorkingSetPeak);
valueIds.Add("Working Set", ValueId.WorkingSet);
valueIds.Add("ID Thread", ValueId.ThreadId);
valueIds.Add("ID Process", ValueId.ProcessId);
valueIds.Add("Priority Base", ValueId.BasePriority);
valueIds.Add("Priority Current", ValueId.CurrentPriority);
valueIds.Add("% User Time", ValueId.UserTime);
valueIds.Add("% Privileged Time", ValueId.PrivilegedTime);
valueIds.Add("Start Address", ValueId.StartAddress);
valueIds.Add("Thread State", ValueId.ThreadState);
valueIds.Add("Thread Wait Reason", ValueId.ThreadWaitReason);
}
internal static int SystemProcessID {
get {
const int systemProcessIDOnXP = 4;
const int systemProcessIDOn2K = 8;
if( ProcessManager.IsOSOlderThanXP) {
return systemProcessIDOn2K;
}
else {
return systemProcessIDOnXP;
}
}
}
[ResourceExposure(ResourceScope.Machine)]
[ResourceConsumption(ResourceScope.Machine)]
public static int[] GetProcessIds(string machineName, bool isRemoteMachine) {
ProcessInfo[] infos = GetProcessInfos(machineName, isRemoteMachine);
int[] ids = new int[infos.Length];
for (int i = 0; i < infos.Length; i++)
ids[i] = infos[i].processId;
return ids;
}
[ResourceExposure(ResourceScope.Machine)]
[ResourceConsumption(ResourceScope.Machine)]
public static int[] GetProcessIds() {
int[] processIds = new int[256];
int size;
for (;;) {
if (!NativeMethods.EnumProcesses(processIds, processIds.Length * 4, out size))
throw new Win32Exception();
if (size == processIds.Length * 4) {
processIds = new int[processIds.Length * 2];
continue;
}
break;
}
int[] ids = new int[size / 4];
Array.Copy(processIds, ids, ids.Length);
return ids;
}
[ResourceExposure(ResourceScope.Machine)]
[ResourceConsumption(ResourceScope.Machine)]
public static ModuleInfo[] GetModuleInfos(int processId) {
return GetModuleInfos(processId, false);
}
[ResourceExposure(ResourceScope.Machine)]
[ResourceConsumption(ResourceScope.Machine)]
public static ModuleInfo GetFirstModuleInfo(int processId) {
ModuleInfo[] moduleInfos = GetModuleInfos(processId, true);
if( moduleInfos.Length == 0) {
return null;
}
else {
return moduleInfos[0];
}
}
[ResourceExposure(ResourceScope.Machine)]
[ResourceConsumption(ResourceScope.Machine)]
private static ModuleInfo[] GetModuleInfos(int processId, bool firstModuleOnly) {
// preserving Everett behavior.
if( processId == SystemProcessID || processId == IdleProcessID) {
// system process and idle process doesn't have any modules
throw new Win32Exception(HResults.EFail,SR.GetString(SR.EnumProcessModuleFailed));
}
SafeProcessHandle processHandle = SafeProcessHandle.InvalidHandle;
try {
processHandle = ProcessManager.OpenProcess(processId, NativeMethods.PROCESS_QUERY_INFORMATION | NativeMethods.PROCESS_VM_READ, true);
IntPtr[] moduleHandles = new IntPtr[64];
GCHandle moduleHandlesArrayHandle = new GCHandle();
int moduleCount = 0;
for (;;) {
bool enumResult = false;
try {
moduleHandlesArrayHandle = GCHandle.Alloc(moduleHandles, GCHandleType.Pinned);
//
// EnumProcessModules is not a reliable method to get the modules for a process.
// If OS loader is touching module information, this method might fail and copy part of the data.
// This is no easy solution to this problem. The only reliable way to fix this is to
// suspend all the threads in target process. Of course we don't want to do this in Process class.
// So we just to try avoid the ---- by calling the same method 10 (an arbitary number) times.
//
for( int i = 0; i < 10; i++) {
enumResult = NativeMethods.EnumProcessModules(processHandle, moduleHandlesArrayHandle.AddrOfPinnedObject(), moduleHandles.Length * IntPtr.Size, ref moduleCount);
if( enumResult) {
break;
}
Thread.Sleep(1);
}
}
finally {
moduleHandlesArrayHandle.Free();
}
if (!enumResult) {
throw new Win32Exception();
}
moduleCount /= IntPtr.Size;
if (moduleCount <= moduleHandles.Length) break;
moduleHandles = new IntPtr[moduleHandles.Length * 2];
}
ArrayList moduleInfos = new ArrayList();
int ret;
for (int i = 0; i < moduleCount; i++) {
ModuleInfo moduleInfo = new ModuleInfo();
IntPtr moduleHandle = moduleHandles[i];
NativeMethods.NtModuleInfo ntModuleInfo = new NativeMethods.NtModuleInfo();
if (!NativeMethods.GetModuleInformation(processHandle, new HandleRef(null, moduleHandle), ntModuleInfo, Marshal.SizeOf(ntModuleInfo)))
throw new Win32Exception();
moduleInfo.sizeOfImage = ntModuleInfo.SizeOfImage;
moduleInfo.entryPoint = ntModuleInfo.EntryPoint;
moduleInfo.baseOfDll = ntModuleInfo.BaseOfDll;
StringBuilder baseName = new StringBuilder(1024);
ret = NativeMethods.GetModuleBaseName(processHandle, new HandleRef(null, moduleHandle), baseName, baseName.Capacity * 2);
if (ret == 0) throw new Win32Exception();
moduleInfo.baseName = baseName.ToString();
StringBuilder fileName = new StringBuilder(1024);
ret = NativeMethods.GetModuleFileNameEx(processHandle, new HandleRef(null, moduleHandle), fileName, fileName.Capacity * 2);
if (ret == 0) throw new Win32Exception();
moduleInfo.fileName = fileName.ToString();
// smss.exe is started before the win32 subsystem so it get this funny "\systemroot\.." path.
// We change this to the actual path by appending "smss.exe" to GetSystemDirectory()
if (string.Compare(moduleInfo.fileName, "\\SystemRoot\\System32\\smss.exe", StringComparison.OrdinalIgnoreCase) == 0) {
moduleInfo.fileName = Path.Combine(Environment.SystemDirectory, "smss.exe");
}
// Avoid returning Unicode-style long string paths. IO methods cannot handle them.
if (moduleInfo.fileName != null
&& moduleInfo.fileName.Length >= 4
&& moduleInfo.fileName.StartsWith(@"\\?\", StringComparison.Ordinal)) {
moduleInfo.fileName = moduleInfo.fileName.Substring(4);
}
moduleInfos.Add(moduleInfo);
//
// If the user is only interested in the main module, break now.
// This avoid some waste of time. In addition, if the application unloads a DLL
// we will not get an exception.
//
if( firstModuleOnly) { break; }
}
ModuleInfo[] temp = new ModuleInfo[moduleInfos.Count];
moduleInfos.CopyTo(temp, 0);
return temp;
}
finally {
Debug.WriteLineIf(Process.processTracing.TraceVerbose, "Process - CloseHandle(process)");
if (!processHandle.IsInvalid ) {
processHandle.Close();
}
}
}
[ResourceExposure(ResourceScope.Process)]
[ResourceConsumption(ResourceScope.Process)]
public static int GetProcessIdFromHandle(SafeProcessHandle processHandle) {
NativeMethods.NtProcessBasicInfo info = new NativeMethods.NtProcessBasicInfo();
int status = NativeMethods.NtQueryInformationProcess(processHandle, NativeMethods.NtQueryProcessBasicInfo, info, (int)Marshal.SizeOf(info), null);
if (status != 0) {
throw new InvalidOperationException(SR.GetString(SR.CantGetProcessId), new Win32Exception(status));
}
// We should change the signature of this function and ID property in process class.
return info.UniqueProcessId.ToInt32();
}
public static ProcessInfo[] GetProcessInfos(string machineName, bool isRemoteMachine) {
// We demand unmanaged code here because PerformanceCounterLib doesn't demand
// anything. This is the only place we do GetPerformanceCounterLib, and it isn't cached.
new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Demand();
PerformanceCounterLib library = null;
try {
library = PerformanceCounterLib.GetPerformanceCounterLib(machineName, new CultureInfo(0x009));
return GetProcessInfos(library);
}
catch(Exception e) {
if( isRemoteMachine) {
throw new InvalidOperationException(SR.GetString(SR.CouldntConnectToRemoteMachine), e);
}
else {
throw e;
}
}
catch {
if(isRemoteMachine) {
throw new InvalidOperationException(SR.GetString(SR.CouldntConnectToRemoteMachine));
}
else {
throw;
}
}
// We don't want to call library.Close() here because that would cause us to unload all of the perflibs.
// On the next call to GetProcessInfos, we'd have to load them all up again, which is SLOW!
}
static ProcessInfo[] GetProcessInfos(PerformanceCounterLib library) {
ProcessInfo[] processInfos = new ProcessInfo[0] ;
byte[] dataPtr = null;
int retryCount = 5;
while (processInfos.Length == 0 && retryCount != 0) {
try {
dataPtr = library.GetPerformanceData(PerfCounterQueryString);
processInfos = GetProcessInfos(library, ProcessPerfCounterId, ThreadPerfCounterId, dataPtr);
}
catch (Exception e) {
throw new InvalidOperationException(SR.GetString(SR.CouldntGetProcessInfos), e);
}
catch {
throw new InvalidOperationException(SR.GetString(SR.CouldntGetProcessInfos));
}
--retryCount;
}
if (processInfos.Length == 0)
throw new InvalidOperationException(SR.GetString(SR.ProcessDisabled));
return processInfos;
}
unsafe static ProcessInfo[] GetProcessInfos(PerformanceCounterLib library, int processIndex, int threadIndex, byte[] data) {
Debug.WriteLineIf(Process.processTracing.TraceVerbose, "GetProcessInfos()");
Hashtable processInfos = new Hashtable();
ArrayList threadInfos = new ArrayList();
fixed (byte* dataPtr = data) {
IntPtr dataBlockPtr = new IntPtr( (void*) dataPtr);
NativeMethods.PERF_DATA_BLOCK dataBlock = new NativeMethods.PERF_DATA_BLOCK();
Marshal.PtrToStructure(dataBlockPtr, dataBlock);
IntPtr typePtr = (IntPtr)((long)dataBlockPtr + dataBlock.HeaderLength);
NativeMethods.PERF_INSTANCE_DEFINITION instance = new NativeMethods.PERF_INSTANCE_DEFINITION();
NativeMethods.PERF_COUNTER_BLOCK counterBlock = new NativeMethods.PERF_COUNTER_BLOCK();
for (int i = 0; i < dataBlock.NumObjectTypes; i++) {
NativeMethods.PERF_OBJECT_TYPE type = new NativeMethods.PERF_OBJECT_TYPE();
Marshal.PtrToStructure(typePtr, type);
IntPtr instancePtr = (IntPtr)((long)typePtr + type.DefinitionLength);
IntPtr counterPtr = (IntPtr)((long)typePtr + type.HeaderLength);
ArrayList counterList = new ArrayList();
for (int j = 0; j < type.NumCounters; j++) {
NativeMethods.PERF_COUNTER_DEFINITION counter = new NativeMethods.PERF_COUNTER_DEFINITION();
Marshal.PtrToStructure(counterPtr, counter);
string counterName = library.GetCounterName(counter.CounterNameTitleIndex);
if (type.ObjectNameTitleIndex == processIndex)
counter.CounterNameTitlePtr = (int)GetValueId(counterName);
else if (type.ObjectNameTitleIndex == threadIndex)
counter.CounterNameTitlePtr = (int)GetValueId(counterName);
counterList.Add(counter);
counterPtr = (IntPtr)((long)counterPtr + counter.ByteLength);
}
NativeMethods.PERF_COUNTER_DEFINITION[] counters = new NativeMethods.PERF_COUNTER_DEFINITION[counterList.Count];
counterList.CopyTo(counters, 0);
for (int j = 0; j < type.NumInstances; j++) {
Marshal.PtrToStructure(instancePtr, instance);
IntPtr namePtr = (IntPtr)((long)instancePtr + instance.NameOffset);
string instanceName = Marshal.PtrToStringUni(namePtr);
if (instanceName.Equals("_Total")) continue;
IntPtr counterBlockPtr = (IntPtr)((long)instancePtr + instance.ByteLength);
Marshal.PtrToStructure(counterBlockPtr, counterBlock);
if (type.ObjectNameTitleIndex == processIndex) {
ProcessInfo processInfo = GetProcessInfo(type, (IntPtr)((long)instancePtr + instance.ByteLength), counters);
if (processInfo.processId == 0 && string.Compare(instanceName, "Idle", StringComparison.OrdinalIgnoreCase) != 0) {
// Sometimes we'll get a process structure that is not completely filled in.
// We can catch some of these by looking for non-"idle" processes that have id 0
// and ignoring those.
Debug.WriteLineIf(Process.processTracing.TraceVerbose, "GetProcessInfos() - found a non-idle process with id 0; ignoring.");
}
else {
if (processInfos[processInfo.processId] != null) {
// We've found two entries in the perfcounters that claim to be the
// same process. We throw an exception. Is this really going to be
// helpfull to the user? Should we just ignore?
Debug.WriteLineIf(Process.processTracing.TraceVerbose, "GetProcessInfos() - found a duplicate process id");
}
else {
// the performance counters keep a 15 character prefix of the exe name, and then delete the ".exe",
// if it's in the first 15. The problem is that sometimes that will leave us with part of ".exe"
// at the end. If instanceName ends in ".", ".e", or ".ex" we remove it.
string processName = instanceName;
if (processName.Length == 15) {
if (instanceName.EndsWith(".", StringComparison.Ordinal )) processName = instanceName.Substring(0, 14);
else if (instanceName.EndsWith(".e", StringComparison.Ordinal )) processName = instanceName.Substring(0, 13);
else if (instanceName.EndsWith(".ex", StringComparison.Ordinal)) processName = instanceName.Substring(0, 12);
}
processInfo.processName = processName;
processInfos.Add(processInfo.processId, processInfo);
}
}
}
else if (type.ObjectNameTitleIndex == threadIndex) {
ThreadInfo threadInfo = GetThreadInfo(type, (IntPtr)((long)instancePtr + instance.ByteLength), counters);
if (threadInfo.threadId != 0) threadInfos.Add(threadInfo);
}
instancePtr = (IntPtr)((long)instancePtr + instance.ByteLength + counterBlock.ByteLength);
}
typePtr = (IntPtr)((long)typePtr + type.TotalByteLength);
}
}
for (int i = 0; i < threadInfos.Count; i++) {
ThreadInfo threadInfo = (ThreadInfo)threadInfos[i];
ProcessInfo processInfo = (ProcessInfo)processInfos[threadInfo.processId];
if (processInfo != null) {
processInfo.threadInfoList.Add(threadInfo);
}
//else {
// throw new InvalidOperationException(SR.GetString(SR.ProcessNotFound, threadInfo.threadId.ToString(), threadInfo.processId.ToString()));
//}
}
ProcessInfo[] temp = new ProcessInfo[processInfos.Values.Count];
processInfos.Values.CopyTo(temp, 0);
return temp;
}
static ThreadInfo GetThreadInfo(NativeMethods.PERF_OBJECT_TYPE type, IntPtr instancePtr, NativeMethods.PERF_COUNTER_DEFINITION[] counters) {
ThreadInfo threadInfo = new ThreadInfo();
for (int i = 0; i < counters.Length; i++) {
NativeMethods.PERF_COUNTER_DEFINITION counter = counters[i];
long value = ReadCounterValue(counter.CounterType, (IntPtr)((long)instancePtr + counter.CounterOffset));
switch ((ValueId)counter.CounterNameTitlePtr) {
case ValueId.ProcessId:
threadInfo.processId = (int)value;
break;
case ValueId.ThreadId:
threadInfo.threadId = (int)value;
break;
case ValueId.BasePriority:
threadInfo.basePriority = (int)value;
break;
case ValueId.CurrentPriority:
threadInfo.currentPriority = (int)value;
break;
case ValueId.StartAddress:
threadInfo.startAddress = (IntPtr)value;
break;
case ValueId.ThreadState:
threadInfo.threadState = (ThreadState)value;
break;
case ValueId.ThreadWaitReason:
threadInfo.threadWaitReason = GetThreadWaitReason((int)value);
break;
}
}
return threadInfo;
}
internal static ThreadWaitReason GetThreadWaitReason(int value) {
switch (value) {
case 0:
case 7: return ThreadWaitReason.Executive;
case 1:
case 8: return ThreadWaitReason.FreePage;
case 2:
case 9: return ThreadWaitReason.PageIn;
case 3:
case 10: return ThreadWaitReason.SystemAllocation;
case 4:
case 11: return ThreadWaitReason.ExecutionDelay;
case 5:
case 12: return ThreadWaitReason.Suspended;
case 6:
case 13: return ThreadWaitReason.UserRequest;
case 14: return ThreadWaitReason.EventPairHigh;;
case 15: return ThreadWaitReason.EventPairLow;
case 16: return ThreadWaitReason.LpcReceive;
case 17: return ThreadWaitReason.LpcReply;
case 18: return ThreadWaitReason.VirtualMemory;
case 19: return ThreadWaitReason.PageOut;
default: return ThreadWaitReason.Unknown;
}
}
static ProcessInfo GetProcessInfo(NativeMethods.PERF_OBJECT_TYPE type, IntPtr instancePtr, NativeMethods.PERF_COUNTER_DEFINITION[] counters) {
ProcessInfo processInfo = new ProcessInfo();
for (int i = 0; i < counters.Length; i++) {
NativeMethods.PERF_COUNTER_DEFINITION counter = counters[i];
long value = ReadCounterValue(counter.CounterType, (IntPtr)((long)instancePtr + counter.CounterOffset));
switch ((ValueId)counter.CounterNameTitlePtr) {
case ValueId.ProcessId:
processInfo.processId = (int)value;
break;
case ValueId.HandleCount:
processInfo.handleCount = (int)value;
break;
case ValueId.PoolPagedBytes:
processInfo.poolPagedBytes = (int)value;
break;
case ValueId.PoolNonpagedBytes:
processInfo.poolNonpagedBytes = (int)value;
break;
case ValueId.VirtualBytes:
processInfo.virtualBytes = (int)value;
break;
case ValueId.VirtualBytesPeak:
processInfo.virtualBytesPeak = (int)value;
break;
case ValueId.WorkingSetPeak:
processInfo.workingSetPeak = (int)value;
break;
case ValueId.WorkingSet:
processInfo.workingSet = (int)value;
break;
case ValueId.PageFileBytesPeak:
processInfo.pageFileBytesPeak = (int)value;
break;
case ValueId.PageFileBytes:
processInfo.pageFileBytes = (int)value;
break;
case ValueId.PrivateBytes:
processInfo.privateBytes = (int)value;
break;
case ValueId.BasePriority:
processInfo.basePriority = (int)value;
break;
}
}
return processInfo;
}
static ValueId GetValueId(string counterName) {
if (counterName != null) {
object id = valueIds[counterName];
if (id != null)
return(ValueId)id;
}
return ValueId.Unknown;
}
static long ReadCounterValue(int counterType, IntPtr dataPtr) {
if ((counterType & NativeMethods.NtPerfCounterSizeLarge) != 0)
return Marshal.ReadInt64(dataPtr);
else
return(long)Marshal.ReadInt32(dataPtr);
}
enum ValueId {
Unknown = -1,
HandleCount,
PoolPagedBytes,
PoolNonpagedBytes,
ElapsedTime,
VirtualBytesPeak,
VirtualBytes,
PrivateBytes,
PageFileBytes,
PageFileBytesPeak,
WorkingSetPeak,
WorkingSet,
ThreadId,
ProcessId,
BasePriority,
CurrentPriority,
UserTime,
PrivilegedTime,
StartAddress,
ThreadState,
ThreadWaitReason
}
}
internal static class NtProcessInfoHelper {
private static int GetNewBufferSize(int existingBufferSize, int requiredSize) {
if( requiredSize == 0) {
//
// On some old OS like win2000, requiredSize will not be set if the buffer
// passed to NtQuerySystemInformation is not enough.
//
int newSize = existingBufferSize * 2;
if ( newSize < existingBufferSize ) {
// In reality, we should never overflow.
// Adding the code here just in case it happens.
throw new OutOfMemoryException();
}
return newSize;
}
else {
// allocating a few more kilo bytes just in case there are some new process
// kicked in since new call to NtQuerySystemInformation
return requiredSize + 1024 * 10;
}
}
#pragma warning disable 169
public static ProcessInfo[] GetProcessInfos() {
// On a normal machine, 30k to 40K will be enough.
// We use 128K here to tolerate mutilple connections to a machine.
int bufferSize = 128 * 1024;
#if DEBUG
// on debug build, use a smaller buffer size to make sure we hit the retrying code path
bufferSize = 1024;
#endif
int requiredSize = 0;
int status;
ProcessInfo[] processInfos;
GCHandle bufferHandle = new GCHandle();
try {
// Retry until we get all the data
do {
Byte[] buffer = new Byte[bufferSize];
bufferHandle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
status = NativeMethods.NtQuerySystemInformation(
NativeMethods.NtQuerySystemProcessInformation,
bufferHandle.AddrOfPinnedObject(),
bufferSize,
out requiredSize);
if ((uint)status == NativeMethods.STATUS_INFO_LENGTH_MISMATCH) {
if (bufferHandle.IsAllocated) bufferHandle.Free();
bufferSize = GetNewBufferSize(bufferSize, requiredSize);
}
} while ((uint)status == NativeMethods.STATUS_INFO_LENGTH_MISMATCH);
if (status < 0) { // see definition of NT_SUCCESS(Status) in SDK
throw new InvalidOperationException(SR.GetString(SR.CouldntGetProcessInfos), new Win32Exception(status));
}
// Parse the data block to get process information
processInfos = GetProcessInfos(bufferHandle.AddrOfPinnedObject());
}
finally {
if (bufferHandle.IsAllocated) bufferHandle.Free();
}
return processInfos;
}
unsafe static ProcessInfo[] GetProcessInfos(IntPtr dataPtr) {
// 60 is a reasonable number for processes on a normal machine.
Hashtable processInfos = new Hashtable(60);
long totalOffset = 0;
while(true) {
IntPtr currentPtr = (IntPtr)((long)dataPtr + totalOffset);
SystemProcessInformation pi = new SystemProcessInformation();
Marshal.PtrToStructure(currentPtr, pi);
// get information for a process
ProcessInfo processInfo = new ProcessInfo();
// Process ID shouldn't overflow. OS API GetCurrentProcessID returns DWORD.
processInfo.processId = pi.UniqueProcessId.ToInt32();
processInfo.handleCount = (int)pi.HandleCount;
processInfo.sessionId = (int)pi.SessionId;
processInfo.poolPagedBytes = (long)pi.QuotaPagedPoolUsage;;
processInfo.poolNonpagedBytes = (long)pi.QuotaNonPagedPoolUsage;
processInfo.virtualBytes = (long)pi.VirtualSize;
processInfo.virtualBytesPeak = (long)pi.PeakVirtualSize;
processInfo.workingSetPeak = (long)pi.PeakWorkingSetSize;
processInfo.workingSet = (long)pi.WorkingSetSize;
processInfo.pageFileBytesPeak = (long)pi.PeakPagefileUsage;
processInfo.pageFileBytes = (long)pi.PagefileUsage;
processInfo.privateBytes = (long)pi.PrivatePageCount;
processInfo.basePriority = pi.BasePriority;
if( pi.NamePtr == IntPtr.Zero) {
if( processInfo.processId == NtProcessManager.SystemProcessID) {
processInfo.processName = "System";
}
else if( processInfo.processId == NtProcessManager.IdleProcessID) {
processInfo.processName = "Idle";
}
else {
// for normal process without name, using the process ID.
processInfo.processName = processInfo.processId.ToString(CultureInfo.InvariantCulture);
}
}
else {
string processName = GetProcessShortName((char *)pi.NamePtr.ToPointer(), pi.NameLength/2);
//
// On old operating system (NT4 and windows 2000), the process name might be truncated to 15
// characters. For example, aspnet_admin.exe will show up in performance counter as aspnet_admin.ex.
// Process class try to return a nicer name. We used to get the main module name for a process and
// use that as the process name. However normal user doesn't have access to module information,
// so normal user will see an exception when we try to get a truncated process name.
//
if (ProcessManager.IsOSOlderThanXP && (processName.Length == 15)) {
if (processName.EndsWith(".", StringComparison.OrdinalIgnoreCase)) {
processName = processName.Substring(0, 14);
}
else if (processName.EndsWith(".e", StringComparison.OrdinalIgnoreCase)) {
processName = processName.Substring(0, 13);
}
else if (processName.EndsWith(".ex", StringComparison.OrdinalIgnoreCase)) {
processName = processName.Substring(0, 12);
}
}
processInfo.processName = processName;
}
// get the threads for current process
processInfos[processInfo.processId] = processInfo;
currentPtr = (IntPtr)((long)currentPtr + Marshal.SizeOf(pi));
int i = 0;
while( i < pi.NumberOfThreads) {
SystemThreadInformation ti = new SystemThreadInformation();
Marshal.PtrToStructure(currentPtr, ti);
ThreadInfo threadInfo = new ThreadInfo();
threadInfo.processId = (int)ti.UniqueProcess;
threadInfo.threadId = (int)ti.UniqueThread;
threadInfo.basePriority = ti.BasePriority;
threadInfo.currentPriority = ti.Priority;
threadInfo.startAddress = ti.StartAddress;
threadInfo.threadState = (ThreadState)ti.ThreadState;
threadInfo.threadWaitReason = NtProcessManager.GetThreadWaitReason((int)ti.WaitReason);
processInfo.threadInfoList.Add(threadInfo);
currentPtr = (IntPtr)((long)currentPtr + Marshal.SizeOf(ti));
i++;
}
if (pi.NextEntryOffset == 0) {
break;
}
totalOffset += pi.NextEntryOffset;
}
ProcessInfo[] temp = new ProcessInfo[processInfos.Values.Count];
processInfos.Values.CopyTo(temp, 0);
return temp;
}
// This function generates the short form of process name.
//
// This is from GetProcessShortName in NT code base.
// Check base\screg\winreg\perfdlls\process\perfsprc.c for details.
internal unsafe static string GetProcessShortName(char* name, int length) {
char* pSlash = name;
char* pPeriod = name;
char* pThisChar = name;
int wThisChar = 0;
while (*pThisChar != 0) { // go until null
if (*pThisChar == '\\') {
pSlash = pThisChar;
} else if (*pThisChar == '.') {
pPeriod = pThisChar;
}
pThisChar++; // point to next char
wThisChar++;
if (wThisChar >= length) {
break;
}
}
// if pPeriod is still pointing to the beginning of the
// string, then no period was found
if (pPeriod == name) {
pPeriod = pThisChar; // set to end of string;
} else {
// if a period was found, then see if the extension is
// .EXE, if so drop it, if not, then use end of string
// (i.e. include extension in name)
String extension = new String(pPeriod);
if(!String.Equals(".exe", extension, StringComparison.OrdinalIgnoreCase) ) {
pPeriod = pThisChar;
}
}
if (*pSlash == '\\') { // if pSlash is pointing to a slash, then
pSlash++; // point to character next to slash
}
// copy characters between period (or end of string) and
// slash (or start of string) to make image name
int wStringSize = (int)(pPeriod - pSlash); // size in bytes
return new String(pSlash, 0, wStringSize);
}
[StructLayout(LayoutKind.Sequential)]
internal class SystemProcessInformation {
internal int NextEntryOffset;
internal uint NumberOfThreads;
long SpareLi1;
long SpareLi2;
long SpareLi3;
long CreateTime;
long UserTime;
long KernelTime;
internal ushort NameLength; // UNICODE_STRING
internal ushort MaximumNameLength;
internal IntPtr NamePtr; // This will point into the data block returned by NtQuerySystemInformation
internal int BasePriority;
internal IntPtr UniqueProcessId;
internal IntPtr InheritedFromUniqueProcessId;
internal uint HandleCount;
internal uint SessionId;
internal IntPtr PageDirectoryBase;
internal IntPtr PeakVirtualSize;
internal IntPtr VirtualSize;
internal uint PageFaultCount;
internal IntPtr PeakWorkingSetSize;
internal IntPtr WorkingSetSize;
internal IntPtr QuotaPeakPagedPoolUsage;
internal IntPtr QuotaPagedPoolUsage;
internal IntPtr QuotaPeakNonPagedPoolUsage;
internal IntPtr QuotaNonPagedPoolUsage;
internal IntPtr PagefileUsage;
internal IntPtr PeakPagefileUsage;
internal IntPtr PrivatePageCount;
long ReadOperationCount;
long WriteOperationCount;
long OtherOperationCount;
long ReadTransferCount;
long WriteTransferCount;
long OtherTransferCount;
}
[StructLayout(LayoutKind.Sequential)]
internal class SystemThreadInformation {
long KernelTime;
long UserTime;
long CreateTime;
uint WaitTime;
internal IntPtr StartAddress;
internal IntPtr UniqueProcess;
internal IntPtr UniqueThread;
internal int Priority;
internal int BasePriority;
internal uint ContextSwitches;
internal uint ThreadState;
internal uint WaitReason;
}
#pragma warning restore 169
}
}
#endif // !FEATURE_PAL
// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.
Link Menu

This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- DropShadowEffect.cs
- DbParameterHelper.cs
- StopStoryboard.cs
- TreeViewItemAutomationPeer.cs
- TableItemPatternIdentifiers.cs
- Error.cs
- Choices.cs
- WindowsFont.cs
- PointLight.cs
- TimelineCollection.cs
- BooleanKeyFrameCollection.cs
- CalculatedColumn.cs
- SystemTcpConnection.cs
- JavaScriptObjectDeserializer.cs
- MenuItem.cs
- ParameterElementCollection.cs
- CompletionBookmark.cs
- Double.cs
- ReservationNotFoundException.cs
- PropertyDescriptorCollection.cs
- EventSinkHelperWriter.cs
- ScriptingWebServicesSectionGroup.cs
- MsmqInputChannel.cs
- StructuralObject.cs
- NTAccount.cs
- control.ime.cs
- SqlReorderer.cs
- DataGridViewRowPostPaintEventArgs.cs
- BuildProvidersCompiler.cs
- DbProviderFactory.cs
- ListParaClient.cs
- WindowPattern.cs
- XPathException.cs
- SimpleTextLine.cs
- ConfigurationSectionCollection.cs
- OdbcCommand.cs
- PerformanceCounterPermissionEntry.cs
- ClassImporter.cs
- DataBoundControl.cs
- DeferredReference.cs
- ISAPIWorkerRequest.cs
- DoWorkEventArgs.cs
- ForEachAction.cs
- ErrorBehavior.cs
- TypographyProperties.cs
- MenuItemCollection.cs
- CssClassPropertyAttribute.cs
- NetworkInformationException.cs
- ZipPackage.cs
- ExtendedPropertyDescriptor.cs
- MetadataHelper.cs
- GridViewCancelEditEventArgs.cs
- SoapExtensionImporter.cs
- BamlLocalizer.cs
- LexicalChunk.cs
- WindowsGraphicsWrapper.cs
- HttpCapabilitiesEvaluator.cs
- PersianCalendar.cs
- TextTreeUndo.cs
- DataListItem.cs
- Unit.cs
- AsyncPostBackTrigger.cs
- XmlDocumentType.cs
- ScriptMethodAttribute.cs
- ApplicationSettingsBase.cs
- DatagramAdapter.cs
- Adorner.cs
- PropertyValueUIItem.cs
- Context.cs
- GeneratedContractType.cs
- EntityDataReader.cs
- TextServicesCompartmentContext.cs
- ILGenerator.cs
- SortedDictionary.cs
- PropertyCollection.cs
- TemplateControlBuildProvider.cs
- SQLInt64Storage.cs
- CodeBlockBuilder.cs
- HtmlInputRadioButton.cs
- DoubleConverter.cs
- FacetChecker.cs
- UserMapPath.cs
- PathGeometry.cs
- InvalidWorkflowException.cs
- SelectionPattern.cs
- ServiceKnownTypeAttribute.cs
- UrlPath.cs
- NameTable.cs
- AbandonedMutexException.cs
- XslCompiledTransform.cs
- ServerIdentity.cs
- Unit.cs
- HyperlinkAutomationPeer.cs
- FigureParaClient.cs
- SerializationHelper.cs
- TextFindEngine.cs
- MergePropertyDescriptor.cs
- ClientSettingsStore.cs
- COM2Enum.cs
- TimeEnumHelper.cs