mirror of
https://github.com/terrymacdonald/DisplayMagician.git
synced 2024-08-30 18:32:20 +00:00
First (probably) working process creation upgrade
I have completely rewritten the process creation logic for the start programs to handle launchers, and applications that require higher level privileges. It doesn't quite handle asking for UAC if the programn requires it, but that's something I can add at a later date. At the moment it seems to work a lot more reliably than the last set of code.
This commit is contained in:
parent
9200b82e29
commit
a1f9bb5e2f
@ -5,6 +5,7 @@ using System.Diagnostics;
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Management;
|
using System.Management;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Text;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
@ -15,6 +16,8 @@ namespace DisplayMagician
|
|||||||
|
|
||||||
private static readonly NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
|
private static readonly NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
|
||||||
|
|
||||||
|
const uint SE_GROUP_INTEGRITY = 0x00000020;
|
||||||
|
|
||||||
[Flags]
|
[Flags]
|
||||||
public enum PROCESS_CREATION_FLAGS : UInt32
|
public enum PROCESS_CREATION_FLAGS : UInt32
|
||||||
{
|
{
|
||||||
@ -45,6 +48,177 @@ namespace DisplayMagician
|
|||||||
REALTIME_PRIORITY_CLASS = 0x00000100,
|
REALTIME_PRIORITY_CLASS = 0x00000100,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public enum SaferLevel : uint
|
||||||
|
{
|
||||||
|
Disallowed = 0,
|
||||||
|
Untrusted = 0x1000,
|
||||||
|
Constrained = 0x10000,
|
||||||
|
NormalUser = 0x20000,
|
||||||
|
FullyTrusted = 0x40000
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum SaferScope : uint
|
||||||
|
{
|
||||||
|
Machine = 1,
|
||||||
|
User = 2
|
||||||
|
}
|
||||||
|
|
||||||
|
[Flags]
|
||||||
|
public enum SaferOpenFlags : uint
|
||||||
|
{
|
||||||
|
Open = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum TOKEN_INFORMATION_CLASS
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The buffer receives a TOKEN_USER structure that contains the user account of the token.
|
||||||
|
/// </summary>
|
||||||
|
TokenUser = 1,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The buffer receives a TOKEN_GROUPS structure that contains the group accounts associated with the token.
|
||||||
|
/// </summary>
|
||||||
|
TokenGroups,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The buffer receives a TOKEN_PRIVILEGES structure that contains the privileges of the token.
|
||||||
|
/// </summary>
|
||||||
|
TokenPrivileges,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The buffer receives a TOKEN_OWNER structure that contains the default owner security identifier (SID) for newly created objects.
|
||||||
|
/// </summary>
|
||||||
|
TokenOwner,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The buffer receives a TOKEN_PRIMARY_GROUP structure that contains the default primary group SID for newly created objects.
|
||||||
|
/// </summary>
|
||||||
|
TokenPrimaryGroup,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The buffer receives a TOKEN_DEFAULT_DACL structure that contains the default DACL for newly created objects.
|
||||||
|
/// </summary>
|
||||||
|
TokenDefaultDacl,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The buffer receives a TOKEN_SOURCE structure that contains the source of the token. TOKEN_QUERY_SOURCE access is needed to retrieve this information.
|
||||||
|
/// </summary>
|
||||||
|
TokenSource,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The buffer receives a TOKEN_TYPE value that indicates whether the token is a primary or impersonation token.
|
||||||
|
/// </summary>
|
||||||
|
TokenType,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The buffer receives a SECURITY_IMPERSONATION_LEVEL value that indicates the impersonation level of the token. If the access token is not an impersonation token, the function fails.
|
||||||
|
/// </summary>
|
||||||
|
TokenImpersonationLevel,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The buffer receives a TOKEN_STATISTICS structure that contains various token statistics.
|
||||||
|
/// </summary>
|
||||||
|
TokenStatistics,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The buffer receives a TOKEN_GROUPS structure that contains the list of restricting SIDs in a restricted token.
|
||||||
|
/// </summary>
|
||||||
|
TokenRestrictedSids,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The buffer receives a DWORD value that indicates the Terminal Services session identifier that is associated with the token.
|
||||||
|
/// </summary>
|
||||||
|
TokenSessionId,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The buffer receives a TOKEN_GROUPS_AND_PRIVILEGES structure that contains the user SID, the group accounts, the restricted SIDs, and the authentication ID associated with the token.
|
||||||
|
/// </summary>
|
||||||
|
TokenGroupsAndPrivileges,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reserved.
|
||||||
|
/// </summary>
|
||||||
|
TokenSessionReference,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The buffer receives a DWORD value that is nonzero if the token includes the SANDBOX_INERT flag.
|
||||||
|
/// </summary>
|
||||||
|
TokenSandBoxInert,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reserved.
|
||||||
|
/// </summary>
|
||||||
|
TokenAuditPolicy,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The buffer receives a TOKEN_ORIGIN value.
|
||||||
|
/// </summary>
|
||||||
|
TokenOrigin,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The buffer receives a TOKEN_ELEVATION_TYPE value that specifies the elevation level of the token.
|
||||||
|
/// </summary>
|
||||||
|
TokenElevationType,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The buffer receives a TOKEN_LINKED_TOKEN structure that contains a handle to another token that is linked to this token.
|
||||||
|
/// </summary>
|
||||||
|
TokenLinkedToken,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The buffer receives a TOKEN_ELEVATION structure that specifies whether the token is elevated.
|
||||||
|
/// </summary>
|
||||||
|
TokenElevation,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The buffer receives a DWORD value that is nonzero if the token has ever been filtered.
|
||||||
|
/// </summary>
|
||||||
|
TokenHasRestrictions,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The buffer receives a TOKEN_ACCESS_INFORMATION structure that specifies security information contained in the token.
|
||||||
|
/// </summary>
|
||||||
|
TokenAccessInformation,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The buffer receives a DWORD value that is nonzero if virtualization is allowed for the token.
|
||||||
|
/// </summary>
|
||||||
|
TokenVirtualizationAllowed,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The buffer receives a DWORD value that is nonzero if virtualization is enabled for the token.
|
||||||
|
/// </summary>
|
||||||
|
TokenVirtualizationEnabled,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The buffer receives a TOKEN_MANDATORY_LABEL structure that specifies the token's integrity level.
|
||||||
|
/// </summary>
|
||||||
|
TokenIntegrityLevel,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The buffer receives a DWORD value that is nonzero if the token has the UIAccess flag set.
|
||||||
|
/// </summary>
|
||||||
|
TokenUIAccess,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The buffer receives a TOKEN_MANDATORY_POLICY structure that specifies the token's mandatory integrity policy.
|
||||||
|
/// </summary>
|
||||||
|
TokenMandatoryPolicy,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The buffer receives the token's logon security identifier (SID).
|
||||||
|
/// </summary>
|
||||||
|
TokenLogonSid,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The maximum value for this enumeration
|
||||||
|
/// </summary>
|
||||||
|
MaxTokenInfoClass
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
|
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
|
||||||
public struct STARTUPINFOEX
|
public struct STARTUPINFOEX
|
||||||
{
|
{
|
||||||
@ -92,6 +266,42 @@ namespace DisplayMagician
|
|||||||
public int bInheritHandle;
|
public int bInheritHandle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
private struct SID_AND_ATTRIBUTES
|
||||||
|
{
|
||||||
|
public IntPtr Sid;
|
||||||
|
public uint Attributes;
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
private struct TOKEN_MANDATORY_LABEL
|
||||||
|
{
|
||||||
|
public SID_AND_ATTRIBUTES Label;
|
||||||
|
}
|
||||||
|
|
||||||
|
[DllImport("advapi32", SetLastError = true, CallingConvention = CallingConvention.StdCall)]
|
||||||
|
private static extern bool SaferCreateLevel(SaferScope scope, SaferLevel level, SaferOpenFlags openFlags, out IntPtr pLevelHandle, IntPtr lpReserved);
|
||||||
|
|
||||||
|
[DllImport("advapi32", SetLastError = true, CallingConvention = CallingConvention.StdCall)]
|
||||||
|
private static extern bool SaferComputeTokenFromLevel(IntPtr LevelHandle, IntPtr InAccessToken, out IntPtr OutAccessToken, int dwFlags, IntPtr lpReserved);
|
||||||
|
|
||||||
|
[DllImport("advapi32", SetLastError = true)]
|
||||||
|
private static extern bool SaferCloseLevel(IntPtr hLevelHandle);
|
||||||
|
|
||||||
|
[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
|
||||||
|
private static extern bool ConvertStringSidToSid(string StringSid, out IntPtr ptrSid);
|
||||||
|
|
||||||
|
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
|
||||||
|
[return: MarshalAs(UnmanagedType.Bool)]
|
||||||
|
private static extern bool CloseHandle(IntPtr hObject);
|
||||||
|
|
||||||
|
private static bool SafeCloseHandle(IntPtr hObject)
|
||||||
|
{
|
||||||
|
return (hObject == IntPtr.Zero) ? true : CloseHandle(hObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
[DllImport("kernel32.dll", SetLastError = true)]
|
||||||
|
static extern IntPtr LocalFree(IntPtr hMem);
|
||||||
|
|
||||||
[DllImport("kernel32.dll")]
|
[DllImport("kernel32.dll")]
|
||||||
[return: MarshalAs(UnmanagedType.Bool)]
|
[return: MarshalAs(UnmanagedType.Bool)]
|
||||||
@ -109,6 +319,27 @@ namespace DisplayMagician
|
|||||||
IntPtr lpEnvironment, string lpCurrentDirectory, [In] ref STARTUPINFOEX lpStartupInfo,
|
IntPtr lpEnvironment, string lpCurrentDirectory, [In] ref STARTUPINFOEX lpStartupInfo,
|
||||||
out PROCESS_INFORMATION lpProcessInformation);
|
out PROCESS_INFORMATION lpProcessInformation);
|
||||||
|
|
||||||
|
[DllImport("advapi32.dll", SetLastError = true)]
|
||||||
|
static extern Boolean SetTokenInformation(
|
||||||
|
IntPtr TokenHandle,
|
||||||
|
TOKEN_INFORMATION_CLASS TokenInformationClass,
|
||||||
|
IntPtr TokenInformation,
|
||||||
|
UInt32 TokenInformationLength);
|
||||||
|
|
||||||
|
[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
|
||||||
|
static extern bool CreateProcessAsUser(
|
||||||
|
IntPtr hToken,
|
||||||
|
string lpApplicationName,
|
||||||
|
string lpCommandLine,
|
||||||
|
IntPtr lpProcessAttributes,
|
||||||
|
IntPtr lpThreadAttributes,
|
||||||
|
bool bInheritHandles,
|
||||||
|
uint dwCreationFlags,
|
||||||
|
IntPtr lpEnvironment,
|
||||||
|
string lpCurrentDirectory,
|
||||||
|
ref STARTUPINFO lpStartupInfo,
|
||||||
|
out PROCESS_INFORMATION lpProcessInformation);
|
||||||
|
|
||||||
[DllImport("kernel32.dll", SetLastError = true)]
|
[DllImport("kernel32.dll", SetLastError = true)]
|
||||||
[return: MarshalAs(UnmanagedType.Bool)]
|
[return: MarshalAs(UnmanagedType.Bool)]
|
||||||
private static extern bool UpdateProcThreadAttribute(
|
private static extern bool UpdateProcThreadAttribute(
|
||||||
@ -124,9 +355,6 @@ namespace DisplayMagician
|
|||||||
[return: MarshalAs(UnmanagedType.Bool)]
|
[return: MarshalAs(UnmanagedType.Bool)]
|
||||||
private static extern bool DeleteProcThreadAttributeList(IntPtr lpAttributeList);
|
private static extern bool DeleteProcThreadAttributeList(IntPtr lpAttributeList);
|
||||||
|
|
||||||
[DllImport("kernel32.dll", SetLastError = true)]
|
|
||||||
private static extern bool CloseHandle(IntPtr hObject);
|
|
||||||
|
|
||||||
[DllImport("kernel32.dll", SetLastError = true)]
|
[DllImport("kernel32.dll", SetLastError = true)]
|
||||||
private static extern uint ResumeThread(IntPtr hThread);
|
private static extern uint ResumeThread(IntPtr hThread);
|
||||||
|
|
||||||
@ -192,13 +420,37 @@ namespace DisplayMagician
|
|||||||
List<Process> runningProcesses = new List<Process>();
|
List<Process> runningProcesses = new List<Process>();
|
||||||
Process process = null;
|
Process process = null;
|
||||||
PROCESS_INFORMATION processInfo;
|
PROCESS_INFORMATION processInfo;
|
||||||
|
bool usingChildProcess = false;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (CreateProcessWithPriority(executable, arguments, ProcessUtils.TranslatePriorityToClass(processPriority), out processInfo))
|
if (CreateProcessWithPriorityAsRestrictedUser(executable, arguments, ProcessUtils.TranslatePriorityToClass(processPriority), out processInfo))
|
||||||
{
|
{
|
||||||
if (processInfo.dwProcessId > 0)
|
if (processInfo.dwProcessId > 0)
|
||||||
|
{
|
||||||
|
try
|
||||||
{
|
{
|
||||||
process = Process.GetProcessById(processInfo.dwProcessId);
|
process = Process.GetProcessById(processInfo.dwProcessId);
|
||||||
|
Task.Delay(500);
|
||||||
|
if (process.HasExited)
|
||||||
|
{
|
||||||
|
// it's a launcher! We need to look for children
|
||||||
|
List<Process> childProcesses = GetChildProcesses(process);
|
||||||
|
runningProcesses.AddRange(childProcesses);
|
||||||
|
usingChildProcess = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
runningProcesses.Add(process);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch(Exception ex)
|
||||||
|
{
|
||||||
|
// it's a launcher! We need to look for children
|
||||||
|
List<Process> childProcesses = GetChildProcesses(processInfo.dwProcessId);
|
||||||
|
runningProcesses.AddRange(childProcesses);
|
||||||
|
usingChildProcess = true;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -211,9 +463,10 @@ namespace DisplayMagician
|
|||||||
process = Process.Start(psi);
|
process = Process.Start(psi);
|
||||||
processInfo.hProcess = process.Handle;
|
processInfo.hProcess = process.Handle;
|
||||||
processInfo.dwProcessId = process.Id;
|
processInfo.dwProcessId = process.Id;
|
||||||
processInfo.dwThreadId = process.Threads[0].Id;
|
|
||||||
if (!process.HasExited)
|
if (!process.HasExited)
|
||||||
{
|
{
|
||||||
|
processInfo.dwThreadId = process.Threads[0].Id;
|
||||||
|
|
||||||
// Change priority if we can (not always possible in this mode :(
|
// Change priority if we can (not always possible in this mode :(
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@ -286,6 +539,8 @@ namespace DisplayMagician
|
|||||||
|
|
||||||
|
|
||||||
// Check the launched exe hasn't exited within 2 secs
|
// Check the launched exe hasn't exited within 2 secs
|
||||||
|
if (!usingChildProcess)
|
||||||
|
{
|
||||||
for (int secs = 0; secs <= (startTimeout * 1000); secs += 500)
|
for (int secs = 0; secs <= (startTimeout * 1000); secs += 500)
|
||||||
{
|
{
|
||||||
// If we have no more processes left then we're done!
|
// If we have no more processes left then we're done!
|
||||||
@ -302,6 +557,8 @@ namespace DisplayMagician
|
|||||||
Thread.Sleep(500);
|
Thread.Sleep(500);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
return runningProcesses;
|
return runningProcesses;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -316,9 +573,26 @@ namespace DisplayMagician
|
|||||||
return children;
|
return children;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool CreateProcessWithPriority(string exeName, string cmdLine, ProcessPriorityClass priorityClass, out PROCESS_INFORMATION processInfo)
|
public static List<Process> GetChildProcesses(int processId)
|
||||||
|
{
|
||||||
|
List<Process> children = new List<Process>();
|
||||||
|
ManagementObjectSearcher mos = new ManagementObjectSearcher($"Select * From Win32_Process Where ParentProcessID={processId}");
|
||||||
|
foreach (ManagementObject mo in mos.Get())
|
||||||
|
{
|
||||||
|
children.Add(Process.GetProcessById(Convert.ToInt32(mo["ProcessID"])));
|
||||||
|
}
|
||||||
|
return children;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool CreateProcessWithPriority(string fileName, string args, ProcessPriorityClass priorityClass, out PROCESS_INFORMATION processInfo)
|
||||||
{
|
{
|
||||||
PROCESS_CREATION_FLAGS processFlags = TranslatePriorityClassToFlags(priorityClass);
|
PROCESS_CREATION_FLAGS processFlags = TranslatePriorityClassToFlags(priorityClass);
|
||||||
|
var cmd = new StringBuilder();
|
||||||
|
cmd.Append('"').Append(fileName).Append('"');
|
||||||
|
if (!string.IsNullOrWhiteSpace(args))
|
||||||
|
{
|
||||||
|
cmd.Append(' ').Append(args);
|
||||||
|
}
|
||||||
bool success = false;
|
bool success = false;
|
||||||
PROCESS_INFORMATION pInfo = new PROCESS_INFORMATION();
|
PROCESS_INFORMATION pInfo = new PROCESS_INFORMATION();
|
||||||
var pSec = new SECURITY_ATTRIBUTES();
|
var pSec = new SECURITY_ATTRIBUTES();
|
||||||
@ -329,7 +603,7 @@ namespace DisplayMagician
|
|||||||
sInfoEx.StartupInfo.cb = Marshal.SizeOf(sInfoEx);
|
sInfoEx.StartupInfo.cb = Marshal.SizeOf(sInfoEx);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
success = CreateProcess(exeName, cmdLine, ref pSec, ref tSec, false, processFlags, IntPtr.Zero, null, ref sInfoEx, out pInfo);
|
success = CreateProcess(fileName, cmd.ToString(), ref pSec, ref tSec, false, processFlags, IntPtr.Zero, null, ref sInfoEx, out pInfo);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@ -339,7 +613,7 @@ namespace DisplayMagician
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
success = CreateProcess(exeName, cmdLine, IntPtr.Zero, IntPtr.Zero, false, processFlags, IntPtr.Zero, null, ref sInfoEx, out pInfo);
|
success = CreateProcess(fileName, cmd.ToString(), IntPtr.Zero, IntPtr.Zero, false, processFlags, IntPtr.Zero, null, ref sInfoEx, out pInfo);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@ -351,6 +625,117 @@ namespace DisplayMagician
|
|||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Runs a process as a non-elevated version of the current user.
|
||||||
|
public static bool CreateProcessWithPriorityAsRestrictedUser(string fileName, string args, ProcessPriorityClass priorityClass, out PROCESS_INFORMATION processInfo)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(fileName))
|
||||||
|
throw new ArgumentException("Value cannot be null or whitespace.", nameof(fileName));
|
||||||
|
|
||||||
|
var pi = new PROCESS_INFORMATION();
|
||||||
|
if (GetRestrictedSessionUserToken(out var hRestrictedToken))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var si = new STARTUPINFO();
|
||||||
|
var cmd = new StringBuilder();
|
||||||
|
cmd.Append('"').Append(fileName).Append('"');
|
||||||
|
if (!string.IsNullOrWhiteSpace(args))
|
||||||
|
{
|
||||||
|
cmd.Append(' ').Append(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!CreateProcessAsUser(
|
||||||
|
hRestrictedToken,
|
||||||
|
fileName,
|
||||||
|
cmd.ToString(),
|
||||||
|
IntPtr.Zero,
|
||||||
|
IntPtr.Zero,
|
||||||
|
true, // inherit handle
|
||||||
|
0,
|
||||||
|
IntPtr.Zero,
|
||||||
|
Path.GetDirectoryName(fileName),
|
||||||
|
ref si,
|
||||||
|
out pi))
|
||||||
|
{
|
||||||
|
processInfo = pi;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
CloseHandle(hRestrictedToken);
|
||||||
|
}
|
||||||
|
processInfo = pi;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
processInfo = pi;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// based on https://stackoverflow.com/a/16110126/862099
|
||||||
|
private static bool GetRestrictedSessionUserToken(out IntPtr token)
|
||||||
|
{
|
||||||
|
token = IntPtr.Zero;
|
||||||
|
if (!SaferCreateLevel(SaferScope.User, SaferLevel.NormalUser, SaferOpenFlags.Open, out var hLevel, IntPtr.Zero))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
IntPtr hRestrictedToken = IntPtr.Zero;
|
||||||
|
TOKEN_MANDATORY_LABEL tml = default;
|
||||||
|
tml.Label.Sid = IntPtr.Zero;
|
||||||
|
IntPtr tmlPtr = IntPtr.Zero;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!SaferComputeTokenFromLevel(hLevel, IntPtr.Zero, out hRestrictedToken, 0, IntPtr.Zero))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the token to medium integrity.
|
||||||
|
tml.Label.Attributes = SE_GROUP_INTEGRITY;
|
||||||
|
tml.Label.Sid = IntPtr.Zero;
|
||||||
|
if (!ConvertStringSidToSid("S-1-16-8192", out tml.Label.Sid))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
tmlPtr = Marshal.AllocHGlobal(Marshal.SizeOf(tml));
|
||||||
|
Marshal.StructureToPtr(tml, tmlPtr, false);
|
||||||
|
if (!SetTokenInformation(hRestrictedToken,
|
||||||
|
TOKEN_INFORMATION_CLASS.TokenIntegrityLevel,
|
||||||
|
tmlPtr, (uint)Marshal.SizeOf(tml)))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
token = hRestrictedToken;
|
||||||
|
hRestrictedToken = IntPtr.Zero; // make sure finally() doesn't close the handle
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
SaferCloseLevel(hLevel);
|
||||||
|
SafeCloseHandle(hRestrictedToken);
|
||||||
|
if (tml.Label.Sid != IntPtr.Zero)
|
||||||
|
{
|
||||||
|
LocalFree(tml.Label.Sid);
|
||||||
|
}
|
||||||
|
if (tmlPtr != IntPtr.Zero)
|
||||||
|
{
|
||||||
|
Marshal.FreeHGlobal(tmlPtr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
public static void ResumeProcess(PROCESS_INFORMATION processInfo)
|
public static void ResumeProcess(PROCESS_INFORMATION processInfo)
|
||||||
{
|
{
|
||||||
ResumeThread(processInfo.hThread);
|
ResumeThread(processInfo.hThread);
|
||||||
@ -492,7 +877,7 @@ namespace DisplayMagician
|
|||||||
{
|
{
|
||||||
// Stop the process
|
// Stop the process
|
||||||
processToStop.CloseMainWindow();
|
processToStop.CloseMainWindow();
|
||||||
if (!processToStop.WaitForExit(5000))
|
if (!processToStop.WaitForExit(1000))
|
||||||
{
|
{
|
||||||
logger.Trace($"ProcessUtils/StopProcess: Process {processToStop.StartInfo.FileName} wouldn't stop cleanly. Forcing program close.");
|
logger.Trace($"ProcessUtils/StopProcess: Process {processToStop.StartInfo.FileName} wouldn't stop cleanly. Forcing program close.");
|
||||||
processToStop.Kill();
|
processToStop.Kill();
|
||||||
@ -550,6 +935,10 @@ namespace DisplayMagician
|
|||||||
{
|
{
|
||||||
logger.Debug($"ShortcutRepository/RunShortcut: Successfully stopped process {processToStop.StartInfo.FileName}");
|
logger.Debug($"ShortcutRepository/RunShortcut: Successfully stopped process {processToStop.StartInfo.FileName}");
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
logger.Warn($"ShortcutRepository/RunShortcut: Failed to stop process {processToStop.StartInfo.FileName} after main executable or game was exited by the user.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
|
@ -26,8 +26,8 @@ using System.Resources;
|
|||||||
[assembly: Guid("e4ceaf5e-ad01-4695-b179-31168eb74c48")]
|
[assembly: Guid("e4ceaf5e-ad01-4695-b179-31168eb74c48")]
|
||||||
|
|
||||||
// Version information
|
// Version information
|
||||||
[assembly: AssemblyVersion("2.1.0.190")]
|
[assembly: AssemblyVersion("2.1.0.195")]
|
||||||
[assembly: AssemblyFileVersion("2.1.0.190")]
|
[assembly: AssemblyFileVersion("2.1.0.195")]
|
||||||
[assembly: NeutralResourcesLanguageAttribute( "en" )]
|
[assembly: NeutralResourcesLanguageAttribute( "en" )]
|
||||||
[assembly: CLSCompliant(true)]
|
[assembly: CLSCompliant(true)]
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user