using System; using System.Collections.Generic; using System.Linq; using System.Text; using Diag = System.Diagnostics; using System.Management; namespace Yaulw.Process { /// /// Wrapper Class around System.Diagnostics's Process Class. /// Helper functions to make sure that we can safely interact with Processes. /// public static class ProcessW { #region Process Caching /// /// Process PID Lookup Cache - so that we can cache PIDs, we don't make a new system call /// Use this Property to Cache Process PIDs Lookups /// public static List ProcessPidsRunningCache { get { return s_RunningProcessesPids; } } private static List s_RunningProcessesPids = new List(); /// /// Process Obj Lookup Cache - so that for processes with the same name, we don't make a new system call /// Use this Property to Cache Process Obj Lookups /// public static Dictionary> ProcessObjsGetterCache { get { return s_processGetterCache; } } private static Dictionary> s_processGetterCache = new Dictionary>(); /// /// Clears the Process Obj Lookup Cache - Used in conjuction with AllRunningProcessesOf() /// public static void ClearProcessObjsGetterCache() { s_processGetterCache.Clear(); } #endregion #region Query For Processes /// /// Safe GetProcessById Function, to make sure that it returns a valid Process Object /// /// the PID to get the Process Object for /// a valid Process obj, or null /// true if successful, false otherwise public static bool GetProcessById(int PID, out Diag.Process p) { p = null; try { p = Diag.Process.GetProcessById(PID); return (p != null); } catch (Exception) { /*ignore */ } return false; } /// /// Quick Check to see if other Process of that Name Are running /// /// name of process /// set to true to ignore Visual Studio's host .exe (debugger process) /// true, if only one Process of that Name is running, false otherwise public static bool IsTheOnlyProcessRunning(string ProcessName, bool bIgnoreVSHost = true) { if (!string.IsNullOrEmpty(ProcessName)) { // Visual Studio could be running this... int nLength1 = Diag.Process.GetProcessesByName(ProcessName).Length; int nLength2 = (bIgnoreVSHost)? 0 : Diag.Process.GetProcessesByName(ProcessName + ".vshost").Length; int nProcessCount = nLength1 + nLength2; return (nProcessCount == 0 || nProcessCount == 1); } return true; } /// /// Use this to quickly retrieve all Running PID on this machine /// * Excellent performance 10 MS * /// /// returns all running Pids public static List AllRunningPids() { Diag.Process[] processes = Diag.Process.GetProcesses(); List allRunningPids = new List(); if (allRunningPids != null) { foreach (Diag.Process p in processes) allRunningPids.Add((uint)p.Id); } return allRunningPids; } /// /// Returns all Processes of that ProcessName /// /// name of process /// If true, for .exe's in the Cache will return immediatly without a system call. Use ClearProcessObjsGetterCache() to clear Cache. /// set to true to ignore Visual Studio's host .exe (debugger process) /// Returns all processes that match the process name, or empty array, if none found public static Diag.Process[] AllRunningProcessesOf(string ProcessName, bool bUseLookupCache = false, bool bIgnoreVSHost = true) { if (!string.IsNullOrEmpty(ProcessName)) { try { // Immediatly Return from Cache the Process Getter Calls * Performance Improvement * if (bUseLookupCache && s_processGetterCache.ContainsKey(ProcessName)) return s_processGetterCache[ProcessName].ToArray(); // If a lookup cache is used, we won't deal with the complexity of also dealing with VSHost, // so ignore this setting if (bIgnoreVSHost && bUseLookupCache) bIgnoreVSHost = true; // Running Processes found List runningProcess = new List(); // Regular Process Lookup... Diag.Process[] processes = Diag.Process.GetProcessesByName(ProcessName); if (processes != null && processes.Length > 0) { foreach (Diag.Process p in processes) runningProcess.Add(p); } // Visual Studio could be running this... if (!bIgnoreVSHost) { processes = Diag.Process.GetProcessesByName(ProcessName + ".vshost"); if (processes != null && processes.Length > 0) { foreach (Diag.Process p in processes) runningProcess.Add(p); } } // Immediatly Cache the Process Getter Calls if (bUseLookupCache && !s_processGetterCache.ContainsKey(ProcessName)) { s_processGetterCache[ProcessName] = new List(); s_processGetterCache[ProcessName].AddRange(runningProcess); } // Return the found Processes return runningProcess.ToArray(); } catch (Exception) { /* ignore */ } } return new Diag.Process[0]{}; // return empty array } #endregion #region Process Exe FileName N CommandLine Getters /// /// Uses WMI to retrieve Process's FileNameNPath and the Command-Line Parameters for the specified Process /// /// a valid Pid to retrieve Command-line and Process FileName for /// passing out full process exe file name and path for pid, if found /// passing out CommandLine params for the Pid, if found /// true, if the process was found and at least ProcessFileNameNPath was written out, false otherwise public static bool GetCommandLineArgumentsForProcessPID(int ProcessId, out string ProcessFileNameNPath, out string CommandLineParams) { ProcessFileNameNPath = String.Empty; CommandLineParams = String.Empty; try { // Use WMI to retrieve the info we need (.net does not provide any other way, it appears) string wmiQuery = string.Format("SELECT CommandLine from Win32_Process WHERE ProcessId = {0}", ProcessId); ManagementObjectSearcher searcher = new ManagementObjectSearcher(wmiQuery); ManagementObjectCollection retObjectCollection = searcher.Get(); if ((retObjectCollection != null) && (retObjectCollection.Count > 0)) { foreach (ManagementObject retObject in retObjectCollection) // This will always be only 1 * retObjectCollection doesn't implement [] { if (retObject != null) // to be even more save... { string strLocal = String.Empty; if (retObject["CommandLine"] != null && !String.IsNullOrEmpty(retObject["CommandLine"].ToString())) strLocal = retObject["CommandLine"].ToString().Trim(); if (!String.IsNullOrEmpty(strLocal)) { // This should work int firstQuotIndex = strLocal.IndexOf('\"'); int SecondQuotIndex = (firstQuotIndex >= 0) ? strLocal.IndexOf('\"', firstQuotIndex + 1) : -1; // Pass out * if we at least have a Process FileNameNPath * if (firstQuotIndex > -1 && SecondQuotIndex > -1) { ProcessFileNameNPath = Win32.Functions.GetLongFileNameNPathOrPath(strLocal.Substring(firstQuotIndex + 1, SecondQuotIndex - 1)); CommandLineParams = (strLocal.Length > SecondQuotIndex + 2) ? strLocal.Substring(SecondQuotIndex + 2) : String.Empty; return true; } } } } } } catch (Exception) { /* ignore */ } return false; } /// /// Usefull for making sure that the exact Process Image (including Command-Line Prms) is currently in a running state. /// /// Pass in a list of Process[] where to search for ProcessFileNameNPath and CommandLineParams /// The exact Process FileName and Path (Image .exe) to find match for /// The exact Command-Line Parameters to find match for /// a PID hint * to speed up look * caller can pass in a PID that 'most likely' is the PID we are looking for (Can be zero) /// If we found the process match, we'll pass out the process to the caller (runningProcesses[]), null otherwise /// If we found the process match, we'll pass out the index to the caller (runningProcesses[]), -1 otherwise /// True, if exact Match was found, False Otherwise public static bool FoundExactProcessExeNCommandLinePrmsInRunningProcesses(Diag.Process[] runningProcesses, string ProcessFileNameNPath, string CommandLineParams, int PID_Hint, out Diag.Process process, out int indexFound) { process = null; indexFound = -1; try { if (runningProcesses == null || runningProcesses.Length == 0) return false; if (PID_Hint < 0) PID_Hint = 0; bool bFoundProcessRunning = false; // * For Performance Reasons * ProcessW.GetCommandLineArgumentsForProcessPID() takes a long time, // Use PID_Hint first, to check if that PID is it if (PID_Hint > 0) { for (int i = 0; i < runningProcesses.Length; ++i) { Diag.Process rProcess = runningProcesses[i]; if (rProcess != null && (rProcess.Id == PID_Hint)) { string ProcessFileNameNPath_Local; string CommandLineParams_Local; if (ProcessW.GetCommandLineArgumentsForProcessPID(rProcess.Id, out ProcessFileNameNPath_Local, out CommandLineParams_Local)) { // See if we find a match if (ProcessFileExeNCommandLineParamsComparer_FoundMatch(ProcessFileNameNPath_Local, CommandLineParams_Local, ProcessFileNameNPath, CommandLineParams)) { bFoundProcessRunning = true; process = rProcess; indexFound = i; } } break; } } } // PID_Hint Worked, no need to continue... if (bFoundProcessRunning) return bFoundProcessRunning; // Iterate all others for (int i = 0; i < runningProcesses.Length; ++i) { Diag.Process rProcess = runningProcesses[i]; if (rProcess != null && (rProcess.Id != PID_Hint)) // Ignore PID_Hint * already been tried * { string ProcessFileNameNPath_Local; string CommandLineParams_Local; if (ProcessW.GetCommandLineArgumentsForProcessPID(rProcess.Id, out ProcessFileNameNPath_Local, out CommandLineParams_Local)) { // See if we find a match if (ProcessFileExeNCommandLineParamsComparer_FoundMatch(ProcessFileNameNPath_Local, CommandLineParams_Local, ProcessFileNameNPath, CommandLineParams)) { bFoundProcessRunning = true; process = rProcess; indexFound = i; break; } } } } return bFoundProcessRunning; } catch (Exception) { /* ignore */ } return false; } /// /// Returns true if the passed in ProcessExeFileNameNPath, CommandLinePrms match _Left and _Right /// /// Process FileNameNPath to compare to Right /// CommandLine Parameters to compare to Right /// Process FileNameNPath to compare to Left /// CommandLine Parameters to compare to Left /// true if _left and _right match, false otherwise public static bool ProcessFileExeNCommandLineParamsComparer_FoundMatch(string ProcessFileNameNPath_Left, string CommandLineParams_Left, string ProcessFileNameNPath_Right, string CommandLineParams_Right) { if ((String.Compare(ProcessFileNameNPath_Left.Trim(), ProcessFileNameNPath_Right.Trim(), true) == 0) && (String.Compare(CommandLineParams_Left.Trim(), CommandLineParams_Right.Trim(), true) == 0)) { return true; } return false; } #endregion } }