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