diff --git a/System/Extensions/ProcessExtension.cs b/System/Extensions/ProcessExtension.cs
index 45f4830e..c0dcb3d1 100644
--- a/System/Extensions/ProcessExtension.cs
+++ b/System/Extensions/ProcessExtension.cs
@@ -1,45 +1,66 @@
using System;
+using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
+using System.IO;
using System.Linq;
#if (NET20 || NET35 || NET40 || NET45)
using System.Management;
#endif
using System.Runtime.InteropServices;
+using System.Text;
+using Vanara.Extensions.Reflection;
+using Vanara.InteropServices;
+using Vanara.IO;
+using Vanara.PInvoke;
using Vanara.Security;
using Vanara.Security.AccessControl;
using static Vanara.PInvoke.AdvApi32;
+using static Vanara.PInvoke.Kernel32;
namespace Vanara.Extensions
{
/// Values which define a processes integrity level.
public enum ProcessIntegrityLevel
{
+ /// Untrusted.
Untrusted,
+
+ /// Undefined.
Undefined,
+
+ /// Low.
Low,
+
+ /// Medium.
Medium,
+
+ /// High.
High,
+
+ /// System.
System
}
- /// Extension methods for for privilegs, status, elevation and relationships.
+ /// Extension methods for for privileges, status, elevation and relationships.
public static partial class ProcessExtension
{
+ /// Disables a specified system privilege on a process.
+ /// The process on which to disable the privilege.
+ /// The privilege to disable.
public static void DisablePrivilege(this Process process, SystemPrivilege privilege)
{
- using (var hObj = SafeHTOKEN.FromProcess(process, TokenAccess.TOKEN_ADJUST_PRIVILEGES | TokenAccess.TOKEN_QUERY))
- {
- hObj.AdjustPrivilege(privilege, PrivilegeAttributes.SE_PRIVILEGE_DISABLED);
- }
+ using var hObj = SafeHTOKEN.FromProcess(process, TokenAccess.TOKEN_ADJUST_PRIVILEGES | TokenAccess.TOKEN_QUERY);
+ hObj.AdjustPrivilege(privilege, PrivilegeAttributes.SE_PRIVILEGE_DISABLED);
}
+ /// Enables a specified system privilege on a process.
+ /// The process on which to enable the privilege.
+ /// The privilege to enable.
public static void EnablePrivilege(this Process process, SystemPrivilege privilege)
{
- using (var hObj = SafeHTOKEN.FromProcess(process, TokenAccess.TOKEN_ADJUST_PRIVILEGES | TokenAccess.TOKEN_QUERY))
- {
- hObj.AdjustPrivilege(privilege, PrivilegeAttributes.SE_PRIVILEGE_ENABLED);
- }
+ using var hObj = SafeHTOKEN.FromProcess(process, TokenAccess.TOKEN_ADJUST_PRIVILEGES | TokenAccess.TOKEN_QUERY);
+ hObj.AdjustPrivilege(privilege, PrivilegeAttributes.SE_PRIVILEGE_ENABLED);
}
#if (NET35 || NET40 || NET45)
@@ -83,21 +104,15 @@ namespace Vanara.Extensions
var tokenIL = hObject.GetInfo(TOKEN_INFORMATION_CLASS.TokenIntegrityLevel);
// Integrity Level SIDs are in the form of S-1-16-0xXXXX. (e.g. S-1-16-0x1000 stands for low integrity level SID). There is one and only one subauthority.
- switch (GetSidSubAuthority(tokenIL.Label.Sid, 0))
+ return (GetSidSubAuthority(tokenIL.Label.Sid, 0)) switch
{
- case 0:
- return ProcessIntegrityLevel.Untrusted;
- case 0x1000:
- return ProcessIntegrityLevel.Low;
- case var iVal when iVal >= 0x2000 && iVal < 0x3000:
- return ProcessIntegrityLevel.Medium;
- case var iVal when iVal >= 0x4000:
- return ProcessIntegrityLevel.System;
- case var iVal when iVal >= 0x3000:
- return ProcessIntegrityLevel.High;
- default:
- return ProcessIntegrityLevel.Undefined;
- }
+ 0 => ProcessIntegrityLevel.Untrusted,
+ 0x1000 => ProcessIntegrityLevel.Low,
+ var iVal when iVal >= 0x2000 && iVal < 0x3000 => ProcessIntegrityLevel.Medium,
+ var iVal when iVal >= 0x4000 => ProcessIntegrityLevel.System,
+ var iVal when iVal >= 0x3000 => ProcessIntegrityLevel.High,
+ _ => ProcessIntegrityLevel.Undefined,
+ };
}
#if (NET20 || NET35 || NET40 || NET45)
@@ -112,11 +127,9 @@ namespace Vanara.Extensions
throw new ArgumentNullException(nameof(p));
try
{
- using (var mo = new ManagementObject($"win32_process.handle='{p.Id}'"))
- {
- mo.Get();
- return Process.GetProcessById(Convert.ToInt32(mo["ParentProcessId"]), p.MachineName);
- }
+ using var mo = new ManagementObject($"win32_process.handle='{p.Id}'");
+ mo.Get();
+ return Process.GetProcessById(Convert.ToInt32(mo["ParentProcessId"]), p.MachineName);
}
catch { }
return null;
@@ -131,10 +144,8 @@ namespace Vanara.Extensions
///
public static IEnumerable GetPrivileges(this Process process)
{
- using (var hObj = SafeHTOKEN.FromProcess(process, TokenAccess.TOKEN_QUERY | TokenAccess.TOKEN_DUPLICATE))
- {
- return hObj.GetPrivileges().Select(la => new PrivilegeAndAttributes(la.Luid.GetPrivilege(process.MachineName), la.Attributes));
- }
+ using var hObj = SafeHTOKEN.FromProcess(process, TokenAccess.TOKEN_QUERY | TokenAccess.TOKEN_DUPLICATE);
+ return hObj.GetPrivileges().Select(la => new PrivilegeAndAttributes(la.Luid.GetPrivilege(process.MachineName), la.Attributes));
}
/// Determines whether the specified privilege is had by the process.
@@ -150,8 +161,8 @@ namespace Vanara.Extensions
/// true if the process has the specified privilege; otherwise, false.
public static bool HasPrivileges(this Process process, bool requireAll, params SystemPrivilege[] privs)
{
- using (var hObj = SafeHTOKEN.FromProcess(process, TokenAccess.TOKEN_QUERY | TokenAccess.TOKEN_DUPLICATE))
- return hObj.HasPrivileges(requireAll, privs);
+ using var hObj = SafeHTOKEN.FromProcess(process, TokenAccess.TOKEN_QUERY | TokenAccess.TOKEN_DUPLICATE);
+ return hObj.HasPrivileges(requireAll, privs);
}
///
@@ -178,13 +189,23 @@ namespace Vanara.Extensions
try
{
// Open the access token of the current process with TOKEN_QUERY.
- using (var hObject = SafeHTOKEN.FromProcess(p, TokenAccess.TOKEN_QUERY | TokenAccess.TOKEN_DUPLICATE))
- return hObject.IsElevated;
+ using var hObject = SafeHTOKEN.FromProcess(p, TokenAccess.TOKEN_QUERY | TokenAccess.TOKEN_DUPLICATE);
+ return hObject.IsElevated;
}
catch { }
return false;
}
+ /// Determines whether the process is running within a job object.
+ ///
+ ///
+ /// The process to be tested. The handle must have the PROCESS_QUERY_INFORMATION or PROCESS_QUERY_LIMITED_INFORMATION access right.
+ ///
+ /// Windows Server 2003 and Windows XP: The handle must have the PROCESS_QUERY_INFORMATION access right.
+ ///
+ /// if the process is running in a job, and otherwise.
+ public static bool IsInJob(this Process p) => IsProcessInJob(p, HJOB.NULL, out var res) ? res : throw Win32Error.GetLastError().GetException();
+
///
/// The function checks whether the primary access token of the process belongs to user account that is a member of the local Administrators group,
/// even if it currently is not elevated.
@@ -196,12 +217,197 @@ namespace Vanara.Extensions
///
public static bool IsRunningAsAdmin(this Process proc) => UAC.IsRunningAsAdmin(proc);
+ /// Removes a specified system privilege from a process.
+ /// The process from which to remove the privilege.
+ /// The privilege to remove.
public static void RemovePrivilege(this Process process, SystemPrivilege privilege)
{
- using (var hObj = SafeHTOKEN.FromProcess(process, TokenAccess.TOKEN_ADJUST_PRIVILEGES | TokenAccess.TOKEN_QUERY))
+ using var hObj = SafeHTOKEN.FromProcess(process, TokenAccess.TOKEN_ADJUST_PRIVILEGES | TokenAccess.TOKEN_QUERY);
+ hObj.AdjustPrivilege(privilege, PrivilegeAttributes.SE_PRIVILEGE_REMOVED);
+ }
+
+ /// Resumes the primary thread on the process.
+ /// The running process.
+ public static void ResumePrimaryThread(this Process process)
+ {
+ using var hTh = OpenThread((uint)ThreadAccess.THREAD_RESUME, true, (uint)process.Threads[0].Id);
+ ResumeThread(hTh);
+ }
+
+ /// Extension method to start a process with extra flags.
+ /// The process to start.
+ /// The process flags.
+ /// if successful.
+ ///
+ /// Process.StartEx cannot be used when StartInfo.UseShellExecute is true. or File name is missing. or StandardOutputEncoding not
+ /// allowed or StandardErrorEncoding not allowed
+ ///
+ /// Can't set duplicate password
+ public static bool StartEx(this Process process, CREATE_PROCESS flags)
+ {
+ var startInfo = process.StartInfo;
+ if (startInfo.UseShellExecute)
+ throw new InvalidOperationException("Process.StartEx cannot be used when StartInfo.UseShellExecute is true.");
+ if (startInfo.FileName.Length == 0)
+ throw new InvalidOperationException("File name is missing.");
+
+ process.Close();
+
+ if (startInfo.StandardOutputEncoding != null && !startInfo.RedirectStandardOutput)
+ throw new InvalidOperationException("StandardOutputEncoding not allowed");
+
+ if (startInfo.StandardErrorEncoding != null && !startInfo.RedirectStandardError)
+ throw new InvalidOperationException("StandardErrorEncoding not allowed");
+
+ // TODO: Cannot start a new process and store its handle if the object has been disposed, since finalization has been suppressed.
+ // if (process.disposed) throw new ObjectDisposedException(GetType().Name);
+
+ var commandLine = new StringBuilder(string.Concat(PathEx.QuoteIfHasSpaces(startInfo.FileName), string.IsNullOrEmpty(startInfo.Arguments) ? "" : " " + startInfo.Arguments));
+
+ var startupInfo = STARTUPINFO.Default;
+ bool retVal;
+ Win32Error errorCode = 0;
+ // handles used in parent process
+ SafeHPIPE standardInputWritePipeHandle = null, standardOutputReadPipeHandle = null, standardErrorReadPipeHandle = null, hStdInput = null, hStdOutput = null, hStdError = null;
+
+ // set up the streams
+ if (startInfo.RedirectStandardInput || startInfo.RedirectStandardOutput || startInfo.RedirectStandardError)
{
- hObj.AdjustPrivilege(privilege, PrivilegeAttributes.SE_PRIVILEGE_REMOVED);
+ if (startInfo.RedirectStandardInput)
+ {
+ CreatePipe(out standardInputWritePipeHandle, out hStdInput, new SECURITY_ATTRIBUTES(), 0);
+ startupInfo.hStdInput = hStdInput;
+ }
+ else
+ {
+ startupInfo.hStdInput = (IntPtr)GetStdHandle(StdHandleType.STD_INPUT_HANDLE);
+ }
+
+ if (startInfo.RedirectStandardOutput)
+ {
+ CreatePipe(out standardOutputReadPipeHandle, out hStdOutput, new SECURITY_ATTRIBUTES(), 0);
+ startupInfo.hStdOutput = hStdOutput;
+ }
+ else
+ {
+ startupInfo.hStdOutput = (IntPtr)GetStdHandle(StdHandleType.STD_OUTPUT_HANDLE);
+ }
+
+ if (startInfo.RedirectStandardError)
+ {
+ CreatePipe(out standardErrorReadPipeHandle, out hStdError, new SECURITY_ATTRIBUTES(), 0);
+ startupInfo.hStdError = hStdError;
+ }
+ else
+ {
+ startupInfo.hStdError = (IntPtr)GetStdHandle(StdHandleType.STD_ERROR_HANDLE);
+ }
+
+ startupInfo.dwFlags = STARTF.STARTF_USESTDHANDLES;
}
+
+ // set up the creation flags paramater
+ var creationFlags = flags;
+ if (startInfo.CreateNoWindow) creationFlags |= CREATE_PROCESS.CREATE_NO_WINDOW;
+
+ var workingDirectory = startInfo.WorkingDirectory;
+ if (workingDirectory == string.Empty)
+ workingDirectory = Environment.CurrentDirectory;
+
+ SafePROCESS_INFORMATION processInfo;
+ if (startInfo.UserName.Length != 0)
+ {
+ if (startInfo.Password != null)
+ throw new ArgumentException("Can't set duplicate password");
+
+ ProcessLogonFlags logonFlags = 0;
+ if (startInfo.LoadUserProfile)
+ logonFlags = ProcessLogonFlags.LOGON_WITH_PROFILE;
+
+ using var password = startInfo.Password != null ? new SafeCoTaskMemString(startInfo.Password) : new SafeCoTaskMemString(string.Empty);
+ System.Runtime.CompilerServices.RuntimeHelpers.PrepareConstrainedRegions();
+ try { }
+ finally
+ {
+ retVal = CreateProcessWithLogonW(
+ startInfo.UserName,
+ startInfo.Domain,
+ password,
+ logonFlags,
+ null, // we don't need this since all the info is in commandLine
+ commandLine,
+ creationFlags,
+ startInfo.EnvironmentVariables?.Cast().Select(e => $"{e.Key}={e.Value}").ToArray(),
+ workingDirectory,
+ startupInfo, // pointer to STARTUPINFO
+ out processInfo); // pointer to PROCESS_INFORMATION
+ if (!retVal)
+ errorCode = Win32Error.GetLastError();
+ }
+ if (!retVal)
+ {
+ if (errorCode == Win32Error.ERROR_BAD_EXE_FORMAT || errorCode == Win32Error.ERROR_EXE_MACHINE_TYPE_MISMATCH)
+ throw errorCode.GetException("Invalid application");
+ throw errorCode.GetException();
+ }
+ }
+ else
+ {
+ System.Runtime.CompilerServices.RuntimeHelpers.PrepareConstrainedRegions();
+ try { }
+ finally
+ {
+ retVal = CreateProcess(
+ null, // we don't need this since all the info is in commandLine
+ commandLine, // pointer to the command line string
+ null, // pointer to process security attributes, we don't need to inheriat the handle
+ null, // pointer to thread security attributes
+ true, // handle inheritance flag
+ creationFlags, // creation flags
+ startInfo.EnvironmentVariables?.Cast().Select(e => $"{e.Key}={e.Value}").ToArray(),
+ workingDirectory, // pointer to current directory name
+ startupInfo, // pointer to STARTUPINFO
+ out processInfo); // pointer to PROCESS_INFORMATION
+ if (!retVal)
+ errorCode = Marshal.GetLastWin32Error();
+ }
+ if (!retVal)
+ {
+ if (errorCode == Win32Error.ERROR_BAD_EXE_FORMAT || errorCode == Win32Error.ERROR_EXE_MACHINE_TYPE_MISMATCH)
+ throw errorCode.GetException("Invalid application");
+ throw errorCode.GetException();
+ }
+ }
+
+#pragma warning disable CS0618 // Type or member is obsolete
+ if (startInfo.RedirectStandardInput)
+ {
+ var stdIn = new StreamWriter(new FileStream(standardInputWritePipeHandle.DangerousGetHandle(), System.IO.FileAccess.Write, false, 4096), Console.InputEncoding, 4096);
+ stdIn.AutoFlush = true;
+ process.SetFieldValue("standardInput", stdIn);
+ }
+ if (startInfo.RedirectStandardOutput)
+ {
+ var enc = (startInfo.StandardOutputEncoding != null) ? startInfo.StandardOutputEncoding : Console.OutputEncoding;
+ var stdOut = new StreamReader(new FileStream(standardOutputReadPipeHandle.DangerousGetHandle(), System.IO.FileAccess.Read, false, 4096), enc, true, 4096);
+ process.SetFieldValue("standardOutput", stdOut);
+ }
+ if (startInfo.RedirectStandardError)
+ {
+ var enc = (startInfo.StandardErrorEncoding != null) ? startInfo.StandardErrorEncoding : Console.OutputEncoding;
+ var stdErr = new StreamReader(new FileStream(standardErrorReadPipeHandle.DangerousGetHandle(), System.IO.FileAccess.Read, false, 4096), enc, true, 4096);
+ process.SetFieldValue("standardError", stdErr);
+ }
+#pragma warning restore CS0618 // Type or member is obsolete
+
+ if (!processInfo.hProcess.IsInvalid)
+ {
+ //process.InvokeMethod("SetProcessHandle", new Microsoft.Win32.SafeHandles.SafeProcessHandle(processInfo.hProcess.Duplicate(), true));
+ process.InvokeMethod("SetProcessId", (int)processInfo.dwProcessId);
+ var h = process.Handle;
+ return true;
+ }
+ return false;
}
private static IEnumerable GetChildProcesses(int pid, Dictionary>> allProcs, string machineName, bool allChildren = true)