mirror of
https://github.com/terrymacdonald/DisplayMagician.git
synced 2024-08-30 18:32:20 +00:00
[WIP] Process launching refactor
The previous launching process just didn't work! It's the last outstanding thing I've got to sort out, so trying a new process launching method as used within MediaPortal project.
This commit is contained in:
parent
10d387d53b
commit
ba2e0afb71
@ -121,8 +121,14 @@
|
|||||||
<Compile Include="IconFromFile.cs" />
|
<Compile Include="IconFromFile.cs" />
|
||||||
<Compile Include="IconUtils.cs" />
|
<Compile Include="IconUtils.cs" />
|
||||||
<Compile Include="ImageUtils.cs" />
|
<Compile Include="ImageUtils.cs" />
|
||||||
|
<Compile Include="Processes\ImpersonationHelper.cs" />
|
||||||
<Compile Include="MessageItem.cs" />
|
<Compile Include="MessageItem.cs" />
|
||||||
<Compile Include="ProcessUtils.cs" />
|
<Compile Include="Processes\ImpersonationProcess.cs">
|
||||||
|
<SubType>Component</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="Processes\NativeMethods.cs" />
|
||||||
|
<Compile Include="Processes\ProcessUtils.cs" />
|
||||||
|
<Compile Include="Processes\WindowsAPI.cs" />
|
||||||
<Compile Include="ProgramSettings.cs" />
|
<Compile Include="ProgramSettings.cs" />
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
<Compile Include="Properties\Resources.Designer.cs">
|
<Compile Include="Properties\Resources.Designer.cs">
|
||||||
@ -137,6 +143,7 @@
|
|||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="ShortcutItem.cs" />
|
<Compile Include="ShortcutItem.cs" />
|
||||||
<Compile Include="ShortcutRepository.cs" />
|
<Compile Include="ShortcutRepository.cs" />
|
||||||
|
<Compile Include="Processes\SingleInstanceHelper.cs" />
|
||||||
<Compile Include="UIForms\GameAdaptor.cs" />
|
<Compile Include="UIForms\GameAdaptor.cs" />
|
||||||
<Compile Include="UIForms\HotkeyForm.cs">
|
<Compile Include="UIForms\HotkeyForm.cs">
|
||||||
<SubType>Form</SubType>
|
<SubType>Form</SubType>
|
||||||
|
@ -1,966 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.ComponentModel;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.IO;
|
|
||||||
using System.Management;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace DisplayMagician
|
|
||||||
{
|
|
||||||
public class ProcessUtils
|
|
||||||
{
|
|
||||||
|
|
||||||
private static readonly NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
|
|
||||||
|
|
||||||
const uint SE_GROUP_INTEGRITY = 0x00000020;
|
|
||||||
|
|
||||||
[Flags]
|
|
||||||
public enum PROCESS_CREATION_FLAGS : UInt32
|
|
||||||
{
|
|
||||||
ZERO_FLAG = 0x00000000,
|
|
||||||
CREATE_BREAKAWAY_FROM_JOB = 0x01000000,
|
|
||||||
CREATE_DEFAULT_ERROR_MODE = 0x04000000,
|
|
||||||
CREATE_NEW_CONSOLE = 0x00000010,
|
|
||||||
CREATE_NEW_PROCESS_GROUP = 0x00000200,
|
|
||||||
CREATE_NO_WINDOW = 0x08000000,
|
|
||||||
CREATE_PROTECTED_PROCESS = 0x00040000,
|
|
||||||
CREATE_PRESERVE_CODE_AUTHZ_LEVEL = 0x02000000,
|
|
||||||
CREATE_SEPARATE_WOW_VDM = 0x00001000,
|
|
||||||
CREATE_SHARED_WOW_VDM = 0x00001000,
|
|
||||||
CREATE_SUSPENDED = 0x00000004,
|
|
||||||
CREATE_UNICODE_ENVIRONMENT = 0x00000400,
|
|
||||||
DEBUG_ONLY_THIS_PROCESS = 0x00000002,
|
|
||||||
DEBUG_PROCESS = 0x00000001,
|
|
||||||
DETACHED_PROCESS = 0x00000008,
|
|
||||||
EXTENDED_STARTUPINFO_PRESENT = 0x00080000,
|
|
||||||
INHERIT_PARENT_AFFINITY = 0x00010000,
|
|
||||||
|
|
||||||
// Process creations flags
|
|
||||||
ABOVE_NORMAL_PRIORITY_CLASS = 0x00008000,
|
|
||||||
BELOW_NORMAL_PRIORITY_CLASS = 0x00004000,
|
|
||||||
HIGH_PRIORITY_CLASS = 0x00000080,
|
|
||||||
IDLE_PRIORITY_CLASS = 0x00000040,
|
|
||||||
NORMAL_PRIORITY_CLASS = 0x00000020,
|
|
||||||
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)]
|
|
||||||
public struct STARTUPINFOEX
|
|
||||||
{
|
|
||||||
public STARTUPINFO StartupInfo;
|
|
||||||
public IntPtr lpAttributeList;
|
|
||||||
}
|
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
|
|
||||||
public struct STARTUPINFO
|
|
||||||
{
|
|
||||||
public Int32 cb;
|
|
||||||
public string lpReserved;
|
|
||||||
public string lpDesktop;
|
|
||||||
public string lpTitle;
|
|
||||||
public Int32 dwX;
|
|
||||||
public Int32 dwY;
|
|
||||||
public Int32 dwXSize;
|
|
||||||
public Int32 dwYSize;
|
|
||||||
public Int32 dwXCountChars;
|
|
||||||
public Int32 dwYCountChars;
|
|
||||||
public Int32 dwFillAttribute;
|
|
||||||
public Int32 dwFlags;
|
|
||||||
public Int16 wShowWindow;
|
|
||||||
public Int16 cbReserved2;
|
|
||||||
public IntPtr lpReserved2;
|
|
||||||
public IntPtr hStdInput;
|
|
||||||
public IntPtr hStdOutput;
|
|
||||||
public IntPtr hStdError;
|
|
||||||
}
|
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
|
||||||
public struct PROCESS_INFORMATION
|
|
||||||
{
|
|
||||||
public IntPtr hProcess;
|
|
||||||
public IntPtr hThread;
|
|
||||||
public int dwProcessId;
|
|
||||||
public int dwThreadId;
|
|
||||||
}
|
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
|
||||||
public struct SECURITY_ATTRIBUTES
|
|
||||||
{
|
|
||||||
public int nLength;
|
|
||||||
public IntPtr lpSecurityDescriptor;
|
|
||||||
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")]
|
|
||||||
[return: MarshalAs(UnmanagedType.Bool)]
|
|
||||||
private static extern bool CreateProcess(
|
|
||||||
string lpApplicationName, string lpCommandLine, ref SECURITY_ATTRIBUTES lpProcessAttributes,
|
|
||||||
ref SECURITY_ATTRIBUTES lpThreadAttributes, bool bInheritHandles, PROCESS_CREATION_FLAGS dwCreationFlags,
|
|
||||||
IntPtr lpEnvironment, string lpCurrentDirectory, [In] ref STARTUPINFOEX lpStartupInfo,
|
|
||||||
out PROCESS_INFORMATION lpProcessInformation);
|
|
||||||
|
|
||||||
[DllImport("kernel32.dll")]
|
|
||||||
[return: MarshalAs(UnmanagedType.Bool)]
|
|
||||||
private static extern bool CreateProcess(
|
|
||||||
string lpApplicationName, string lpCommandLine, IntPtr lpProcessAttributes,
|
|
||||||
IntPtr lpThreadAttributes, bool bInheritHandles, PROCESS_CREATION_FLAGS dwCreationFlags,
|
|
||||||
IntPtr lpEnvironment, string lpCurrentDirectory, [In] ref STARTUPINFOEX lpStartupInfo,
|
|
||||||
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)]
|
|
||||||
[return: MarshalAs(UnmanagedType.Bool)]
|
|
||||||
private static extern bool UpdateProcThreadAttribute(
|
|
||||||
IntPtr lpAttributeList, uint dwFlags, IntPtr Attribute, IntPtr lpValue,
|
|
||||||
IntPtr cbSize, IntPtr lpPreviousValue, IntPtr lpReturnSize);
|
|
||||||
|
|
||||||
[DllImport("kernel32.dll", SetLastError = true)]
|
|
||||||
[return: MarshalAs(UnmanagedType.Bool)]
|
|
||||||
private static extern bool InitializeProcThreadAttributeList(
|
|
||||||
IntPtr lpAttributeList, int dwAttributeCount, int dwFlags, ref IntPtr lpSize);
|
|
||||||
|
|
||||||
[DllImport("kernel32.dll", SetLastError = true)]
|
|
||||||
[return: MarshalAs(UnmanagedType.Bool)]
|
|
||||||
private static extern bool DeleteProcThreadAttributeList(IntPtr lpAttributeList);
|
|
||||||
|
|
||||||
[DllImport("kernel32.dll", SetLastError = true)]
|
|
||||||
private static extern uint ResumeThread(IntPtr hThread);
|
|
||||||
|
|
||||||
[DllImport("kernel32.dll", SetLastError = true)]
|
|
||||||
private static extern uint SuspendThread(IntPtr hThread);
|
|
||||||
|
|
||||||
public static ProcessPriorityClass TranslatePriorityToClass(ProcessPriority processPriorityClass)
|
|
||||||
{
|
|
||||||
ProcessPriorityClass wantedPriorityClass = ProcessPriorityClass.Normal;
|
|
||||||
switch (processPriorityClass)
|
|
||||||
{
|
|
||||||
case ProcessPriority.High:
|
|
||||||
wantedPriorityClass = ProcessPriorityClass.High;
|
|
||||||
break;
|
|
||||||
case ProcessPriority.AboveNormal:
|
|
||||||
wantedPriorityClass = ProcessPriorityClass.AboveNormal;
|
|
||||||
break;
|
|
||||||
case ProcessPriority.Normal:
|
|
||||||
wantedPriorityClass = ProcessPriorityClass.Normal;
|
|
||||||
break;
|
|
||||||
case ProcessPriority.BelowNormal:
|
|
||||||
wantedPriorityClass = ProcessPriorityClass.BelowNormal;
|
|
||||||
break;
|
|
||||||
case ProcessPriority.Idle:
|
|
||||||
wantedPriorityClass = ProcessPriorityClass.Idle;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
wantedPriorityClass = ProcessPriorityClass.Normal;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return wantedPriorityClass;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static PROCESS_CREATION_FLAGS TranslatePriorityClassToFlags(ProcessPriorityClass processPriorityClass)
|
|
||||||
{
|
|
||||||
PROCESS_CREATION_FLAGS wantedPriorityClass = PROCESS_CREATION_FLAGS.NORMAL_PRIORITY_CLASS;
|
|
||||||
switch (processPriorityClass)
|
|
||||||
{
|
|
||||||
case ProcessPriorityClass.High:
|
|
||||||
wantedPriorityClass = PROCESS_CREATION_FLAGS.HIGH_PRIORITY_CLASS;
|
|
||||||
break;
|
|
||||||
case ProcessPriorityClass.AboveNormal:
|
|
||||||
wantedPriorityClass = PROCESS_CREATION_FLAGS.ABOVE_NORMAL_PRIORITY_CLASS;
|
|
||||||
break;
|
|
||||||
case ProcessPriorityClass.Normal:
|
|
||||||
wantedPriorityClass = PROCESS_CREATION_FLAGS.NORMAL_PRIORITY_CLASS;
|
|
||||||
break;
|
|
||||||
case ProcessPriorityClass.BelowNormal:
|
|
||||||
wantedPriorityClass = PROCESS_CREATION_FLAGS.BELOW_NORMAL_PRIORITY_CLASS;
|
|
||||||
break;
|
|
||||||
case ProcessPriorityClass.Idle:
|
|
||||||
wantedPriorityClass = PROCESS_CREATION_FLAGS.IDLE_PRIORITY_CLASS;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
wantedPriorityClass = PROCESS_CREATION_FLAGS.NORMAL_PRIORITY_CLASS;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return wantedPriorityClass;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static List<Process> StartProcess(string executable, string arguments, ProcessPriority processPriority, int startTimeout = 1)
|
|
||||||
{
|
|
||||||
List<Process> runningProcesses = new List<Process>();
|
|
||||||
Process process = null;
|
|
||||||
PROCESS_INFORMATION processInfo;
|
|
||||||
bool usingChildProcess = false;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (CreateProcessWithPriorityAsRestrictedUser(executable, arguments, ProcessUtils.TranslatePriorityToClass(processPriority), out processInfo))
|
|
||||||
//if (CreateProcessWithPriority(executable, arguments, ProcessUtils.TranslatePriorityToClass(processPriority), out processInfo))
|
|
||||||
{
|
|
||||||
if (processInfo.dwProcessId > 0)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
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
|
|
||||||
{
|
|
||||||
logger.Warn($"ProcessUtils/StartProcess: CreateProcessWithPriority returned a process with PID 0 when trying to start process {executable}. This indicates that the process was not started, so we'll try it a different way.");
|
|
||||||
// Start the process using built in process library
|
|
||||||
ProcessStartInfo psi = new ProcessStartInfo();
|
|
||||||
psi.FileName = executable;
|
|
||||||
psi.Arguments = arguments;
|
|
||||||
psi.WorkingDirectory = Path.GetDirectoryName(executable);
|
|
||||||
process = Process.Start(psi);
|
|
||||||
processInfo.hProcess = process.Handle;
|
|
||||||
processInfo.dwProcessId = process.Id;
|
|
||||||
if (!process.HasExited)
|
|
||||||
{
|
|
||||||
processInfo.dwThreadId = process.Threads[0].Id;
|
|
||||||
|
|
||||||
// Change priority if we can (not always possible in this mode :(
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// If this process is a protected process, then this will fail!
|
|
||||||
process.PriorityClass = ProcessUtils.TranslatePriorityToClass(processPriority);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
// We would need need higher rights for this processto set the priority
|
|
||||||
// https://docs.microsoft.com/en-us/windows/win32/procthread/process-security-and-access-rights
|
|
||||||
// At this stage I am not writing this, as it is a lot of work for a niche issue.
|
|
||||||
}
|
|
||||||
runningProcesses.Add(process);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Start the process using built in process library
|
|
||||||
ProcessStartInfo psi = new ProcessStartInfo();
|
|
||||||
string extension = Path.GetExtension(executable);
|
|
||||||
if (extension.Equals("com", StringComparison.CurrentCultureIgnoreCase)
|
|
||||||
|| extension.Equals("exe", StringComparison.CurrentCultureIgnoreCase)
|
|
||||||
|| extension.Equals("msi", StringComparison.CurrentCultureIgnoreCase))
|
|
||||||
{
|
|
||||||
psi.UseShellExecute = false;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
psi.Verb = "Open";
|
|
||||||
}
|
|
||||||
psi.FileName = executable;
|
|
||||||
psi.Arguments = arguments;
|
|
||||||
psi.WorkingDirectory = Path.GetDirectoryName(executable);
|
|
||||||
process = Process.Start(psi);
|
|
||||||
processInfo.hProcess = process.Handle;
|
|
||||||
processInfo.dwProcessId = process.Id;
|
|
||||||
if (!process.HasExited)
|
|
||||||
{
|
|
||||||
processInfo.dwThreadId = process.Threads[0].Id;
|
|
||||||
// Change priority if we can (not always possible in this mode :(
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// If this process is a protected process, then this will fail!
|
|
||||||
process.PriorityClass = ProcessUtils.TranslatePriorityToClass(processPriority);
|
|
||||||
}
|
|
||||||
catch(Exception ex)
|
|
||||||
{
|
|
||||||
// We would need need higher rights for this processto set the priority
|
|
||||||
// https://docs.microsoft.com/en-us/windows/win32/procthread/process-security-and-access-rights
|
|
||||||
// At this stage I am not writing this, as it is a lot of work for a niche issue.
|
|
||||||
}
|
|
||||||
runningProcesses.Add(process);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
if (!process.HasExited)
|
|
||||||
{
|
|
||||||
// Start the process using built in process library
|
|
||||||
ProcessStartInfo psi = new ProcessStartInfo();
|
|
||||||
psi.FileName = executable;
|
|
||||||
psi.Arguments = arguments;
|
|
||||||
psi.WorkingDirectory = Path.GetDirectoryName(executable);
|
|
||||||
process = Process.Start(psi);
|
|
||||||
processInfo.hProcess = process.Handle;
|
|
||||||
processInfo.dwProcessId = process.Id;
|
|
||||||
processInfo.dwThreadId = process.Threads[0].Id;
|
|
||||||
//pInfo.dwThreadId = process.Threads[0].Id;
|
|
||||||
// Change priority
|
|
||||||
if (!process.HasExited)
|
|
||||||
{
|
|
||||||
runningProcesses.Add(process);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Check the launched exe hasn't exited within 2 secs
|
|
||||||
if (!usingChildProcess)
|
|
||||||
{
|
|
||||||
for (int secs = 0; secs <= (startTimeout * 1000); secs += 500)
|
|
||||||
{
|
|
||||||
// If we have no more processes left then we're done!
|
|
||||||
if (process.HasExited)
|
|
||||||
{
|
|
||||||
logger.Trace($"ProcessUtils/StartProcess: {executable} has exited early! It's likely to be a launcher! Trying to detect it's children.");
|
|
||||||
// As the original process has left the building, we'll overwrite it with the children processes
|
|
||||||
runningProcesses = GetChildProcesses(process);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
// Send a message to windows so that it doesn't think
|
|
||||||
// we're locked and try to kill us
|
|
||||||
System.Threading.Thread.CurrentThread.Join(0);
|
|
||||||
Thread.Sleep(500);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return runningProcesses;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static List<Process> GetChildProcesses(Process process)
|
|
||||||
{
|
|
||||||
List<Process> children = new List<Process>();
|
|
||||||
ManagementObjectSearcher mos = new ManagementObjectSearcher($"Select * From Win32_Process Where ParentProcessID={process.Id}");
|
|
||||||
foreach (ManagementObject mo in mos.Get())
|
|
||||||
{
|
|
||||||
children.Add(Process.GetProcessById(Convert.ToInt32(mo["ProcessID"])));
|
|
||||||
}
|
|
||||||
return children;
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
var cmd = new StringBuilder();
|
|
||||||
cmd.Append('"').Append(fileName).Append('"');
|
|
||||||
if (!string.IsNullOrWhiteSpace(args))
|
|
||||||
{
|
|
||||||
cmd.Append(' ').Append(args);
|
|
||||||
}
|
|
||||||
bool success = false;
|
|
||||||
PROCESS_INFORMATION pInfo = new PROCESS_INFORMATION();
|
|
||||||
var pSec = new SECURITY_ATTRIBUTES();
|
|
||||||
var tSec = new SECURITY_ATTRIBUTES();
|
|
||||||
pSec.nLength = Marshal.SizeOf(pSec);
|
|
||||||
tSec.nLength = Marshal.SizeOf(tSec);
|
|
||||||
var sInfoEx = new STARTUPINFOEX();
|
|
||||||
sInfoEx.StartupInfo.cb = Marshal.SizeOf(sInfoEx);
|
|
||||||
try
|
|
||||||
{
|
|
||||||
success = CreateProcess(null, cmd.ToString(), ref pSec, ref tSec, false, processFlags, IntPtr.Zero, null, ref sInfoEx, out pInfo);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
// This is a problem
|
|
||||||
}
|
|
||||||
if (!success)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
success = CreateProcess(null, cmd.ToString(), IntPtr.Zero, IntPtr.Zero, false, processFlags, IntPtr.Zero, null, ref sInfoEx, out pInfo);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
// This is a problem too
|
|
||||||
}
|
|
||||||
}
|
|
||||||
processInfo = pInfo;
|
|
||||||
|
|
||||||
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,
|
|
||||||
null,
|
|
||||||
cmd.ToString(),
|
|
||||||
IntPtr.Zero,
|
|
||||||
IntPtr.Zero,
|
|
||||||
true, // inherit handle
|
|
||||||
0,
|
|
||||||
IntPtr.Zero,
|
|
||||||
null,
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
ResumeThread(processInfo.hThread);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool CreateProcessWithParent(int parentProcessId)
|
|
||||||
{
|
|
||||||
const int PROC_THREAD_ATTRIBUTE_PARENT_PROCESS = 0x00020000;
|
|
||||||
|
|
||||||
var pInfo = new PROCESS_INFORMATION();
|
|
||||||
var sInfoEx = new STARTUPINFOEX();
|
|
||||||
sInfoEx.StartupInfo.cb = Marshal.SizeOf(sInfoEx);
|
|
||||||
IntPtr lpValue = IntPtr.Zero;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (parentProcessId > 0)
|
|
||||||
{
|
|
||||||
var lpSize = IntPtr.Zero;
|
|
||||||
var success = InitializeProcThreadAttributeList(IntPtr.Zero, 1, 0, ref lpSize);
|
|
||||||
if (success || lpSize == IntPtr.Zero)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
sInfoEx.lpAttributeList = Marshal.AllocHGlobal(lpSize);
|
|
||||||
success = InitializeProcThreadAttributeList(sInfoEx.lpAttributeList, 1, 0, ref lpSize);
|
|
||||||
if (!success)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
var parentHandle = Process.GetProcessById(parentProcessId).Handle;
|
|
||||||
// This value should persist until the attribute list is destroyed using the DeleteProcThreadAttributeList function
|
|
||||||
lpValue = Marshal.AllocHGlobal(IntPtr.Size);
|
|
||||||
Marshal.WriteIntPtr(lpValue, parentHandle);
|
|
||||||
|
|
||||||
success = UpdateProcThreadAttribute(
|
|
||||||
sInfoEx.lpAttributeList,
|
|
||||||
0,
|
|
||||||
(IntPtr)PROC_THREAD_ATTRIBUTE_PARENT_PROCESS,
|
|
||||||
lpValue,
|
|
||||||
(IntPtr)IntPtr.Size,
|
|
||||||
IntPtr.Zero,
|
|
||||||
IntPtr.Zero);
|
|
||||||
if (!success)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var pSec = new SECURITY_ATTRIBUTES();
|
|
||||||
var tSec = new SECURITY_ATTRIBUTES();
|
|
||||||
pSec.nLength = Marshal.SizeOf(pSec);
|
|
||||||
tSec.nLength = Marshal.SizeOf(tSec);
|
|
||||||
var lpApplicationName = Path.Combine(Environment.SystemDirectory, "notepad.exe");
|
|
||||||
return CreateProcess(lpApplicationName, null, ref pSec, ref tSec, false, PROCESS_CREATION_FLAGS.EXTENDED_STARTUPINFO_PRESENT, IntPtr.Zero, null, ref sInfoEx, out pInfo);
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
// Free the attribute list
|
|
||||||
if (sInfoEx.lpAttributeList != IntPtr.Zero)
|
|
||||||
{
|
|
||||||
DeleteProcThreadAttributeList(sInfoEx.lpAttributeList);
|
|
||||||
Marshal.FreeHGlobal(sInfoEx.lpAttributeList);
|
|
||||||
}
|
|
||||||
Marshal.FreeHGlobal(lpValue);
|
|
||||||
|
|
||||||
// Close process and thread handles
|
|
||||||
if (pInfo.hProcess != IntPtr.Zero)
|
|
||||||
{
|
|
||||||
CloseHandle(pInfo.hProcess);
|
|
||||||
}
|
|
||||||
if (pInfo.hThread != IntPtr.Zero)
|
|
||||||
{
|
|
||||||
CloseHandle(pInfo.hThread);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool ProcessExited(Process process)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Process processToTest = Process.GetProcessById(process.Id);
|
|
||||||
if (processToTest.HasExited)
|
|
||||||
{
|
|
||||||
logger.Trace($"ProcessUtils/ProcessExited: {process.Id} has exited and is not running. This means the process has finished!");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
logger.Trace($"ProcessUtils/ProcessExited: {process.Id} is still running as is has not exited yet.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (ArgumentException ex)
|
|
||||||
{
|
|
||||||
logger.Trace($"ProcessUtils/ProcessExited: {process.Id} is not running, and the process ID has expired. This means the process has finished!");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
catch (InvalidOperationException ex)
|
|
||||||
{
|
|
||||||
logger.Warn($"ProcessUtils/ProcessExited: {process.Id} was not started by this process object. This likely means the process has finished!");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
logger.Trace($"ProcessUtils/ProcessExited: Exception when checking if {process.Id} is still running, so assuming the process has finished!");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool ProcessExited(List<Process> processes)
|
|
||||||
{
|
|
||||||
int processClosedCount = 0;
|
|
||||||
foreach (Process p in processes)
|
|
||||||
{
|
|
||||||
if (ProcessExited(p))
|
|
||||||
{
|
|
||||||
processClosedCount++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (processClosedCount == processes.Count)
|
|
||||||
{
|
|
||||||
logger.Trace($"ProcessUtils/ProcessExited2: All processes being monitored have exited, so no processes still running!");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
logger.Trace($"ProcessUtils/ProcessExited2: {processClosedCount} processes out of {processes.Count} processes have exited. At least one process is still running!");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool StopProcess(Process processToStop)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// Stop the process
|
|
||||||
processToStop.CloseMainWindow();
|
|
||||||
if (!processToStop.WaitForExit(1000))
|
|
||||||
{
|
|
||||||
logger.Trace($"ProcessUtils/StopProcess: Process {processToStop.StartInfo.FileName} wouldn't stop cleanly. Forcing program close.");
|
|
||||||
processToStop.Kill();
|
|
||||||
if (!processToStop.WaitForExit(5000))
|
|
||||||
{
|
|
||||||
logger.Error($"ProcessUtils/StopProcess: Process {processToStop.StartInfo.FileName} couldn't be killed! It seems like something is actively preventing us from stopping the process");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
logger.Trace($"ProcessUtils/StopProcess: Process {processToStop.StartInfo.FileName} was successfully killed.");
|
|
||||||
}
|
|
||||||
processToStop.Close();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
catch (Win32Exception ex)
|
|
||||||
{
|
|
||||||
logger.Warn(ex, $"ProcessUtils/StopProcess: Win32Exception! Couldn't access the wait status for a named process we're trying to stop. So now just killing the process.");
|
|
||||||
processToStop.Kill();
|
|
||||||
if (!processToStop.WaitForExit(5000))
|
|
||||||
{
|
|
||||||
logger.Error($"ProcessUtils/StopProcess: Win32Exception! Process {processToStop.StartInfo.FileName} couldn't be killed! It seems like something is actively preventing us from stopping the process");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
logger.Trace($"ProcessUtils/StopProcess: Win32Exception! Process {processToStop.StartInfo.FileName} was successfully killed.");
|
|
||||||
processToStop.Close();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
catch (InvalidOperationException ex)
|
|
||||||
{
|
|
||||||
logger.Error(ex, $"ProcessUtils/StopProcess: Couldn't kill the named process as the process appears to have closed already.");
|
|
||||||
}
|
|
||||||
catch (SystemException ex)
|
|
||||||
{
|
|
||||||
logger.Error(ex, $"ProcessUtils/StopProcess: Couldn't WaitForExit the named process as there is no process associated with the Process object (or cannot get the ID from the named process handle).");
|
|
||||||
}
|
|
||||||
|
|
||||||
catch (AggregateException ae)
|
|
||||||
{
|
|
||||||
logger.Error(ae, $"ProcessUtils/StopProcess: Got an AggregateException.");
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool StopProcess(List<Process> processes)
|
|
||||||
{
|
|
||||||
// Stop the programs in the reverse order we started them
|
|
||||||
foreach (Process processToStop in processes)
|
|
||||||
{
|
|
||||||
// Stop the process if it hasn't stopped already
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (!processToStop.HasExited)
|
|
||||||
{
|
|
||||||
logger.Debug($"ShortcutRepository/RunShortcut: Stopping process {processToStop.StartInfo.FileName}");
|
|
||||||
if (ProcessUtils.StopProcess(processToStop))
|
|
||||||
{
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
logger.Error(ex, $"ShortcutRepository/RunShortcut: Exception while checking if processToStop has already exited");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
258
DisplayMagician/Processes/ImpersonationHelper.cs
Normal file
258
DisplayMagician/Processes/ImpersonationHelper.cs
Normal file
@ -0,0 +1,258 @@
|
|||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Security.Principal;
|
||||||
|
using Microsoft.Win32.SafeHandles;
|
||||||
|
|
||||||
|
namespace DisplayMagician.Processes
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Helper class to logon as a new user. This is be required to access network resources when running the main program as LocalSystem.
|
||||||
|
/// </summary>
|
||||||
|
public class ImpersonationHelper
|
||||||
|
{
|
||||||
|
#region ImpersonationContext
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Helper class to store <see cref="Identity"/> and automatically impersonate.
|
||||||
|
/// </summary>
|
||||||
|
public class ImpersonationContext : IDisposable
|
||||||
|
{
|
||||||
|
private WindowsIdentity _identity;
|
||||||
|
|
||||||
|
public WindowsIdentity Identity
|
||||||
|
{
|
||||||
|
get { return _identity; }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_identity = value;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Context = value.Impersonate();
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public WindowsImpersonationContext Context { get; private set; }
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
WindowsImpersonationContext wic = Context;
|
||||||
|
if (wic != null)
|
||||||
|
wic.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Constants and imports
|
||||||
|
|
||||||
|
private static readonly WellKnownSidType[] KNOWN_SID_TYPES = new[] { WellKnownSidType.NetworkServiceSid, WellKnownSidType.LocalServiceSid, WellKnownSidType.LocalSystemSid };
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if the caller needs to impersonate (again).
|
||||||
|
/// </summary>
|
||||||
|
/// <returns><c>true</c> if impersonate is required.</returns>
|
||||||
|
public static bool RequiresImpersonate(WindowsIdentity requestedIdentity)
|
||||||
|
{
|
||||||
|
if (requestedIdentity == null)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
WindowsIdentity current = WindowsIdentity.GetCurrent();
|
||||||
|
if (current == null || current.User == null) // Can never happen, just to avoid R# warning.
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return
|
||||||
|
current.User != requestedIdentity.User || /* Current user is not the requested one. We need to compare SIDs here, instances are not equal */
|
||||||
|
IsWellknownIdentity(current) /* User is any of well known SIDs, those have no network access */;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Indicates if the <see cref="WindowsIdentity.GetCurrent()"/> represents one of the <see cref="KNOWN_SID_TYPES"/>, which do not have network access.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns><c>true</c> for a well known identity.</returns>
|
||||||
|
public static bool IsWellknownIdentity()
|
||||||
|
{
|
||||||
|
return IsWellknownIdentity(WindowsIdentity.GetCurrent());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Indicates if the given <paramref name="identity"/> represents one of the <see cref="KNOWN_SID_TYPES"/>, which do not have network access.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="identity">Identity to check.</param>
|
||||||
|
/// <returns><c>true</c> for a well known identity.</returns>
|
||||||
|
public static bool IsWellknownIdentity(WindowsIdentity identity)
|
||||||
|
{
|
||||||
|
return KNOWN_SID_TYPES.Any(wellKnownSidType => identity.User != null && identity.User.IsWellKnown(wellKnownSidType));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Attempts to impersonate an user based on an running process. If successful, it returns a WindowsImpersonationContext of the new users identity.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="processName">Process name to take user account from (without .exe).</param>
|
||||||
|
/// <returns>WindowsImpersonationContext if successful.</returns>
|
||||||
|
public static ImpersonationContext ImpersonateByProcess(string processName)
|
||||||
|
{
|
||||||
|
IntPtr userToken;
|
||||||
|
if (!GetTokenByProcess(processName, out userToken))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return new ImpersonationContext { Identity = new WindowsIdentity(userToken) };
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
// Close handle.
|
||||||
|
SafeCloseHandle(userToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Attempts to impersonate an user. If successful, it returns a WindowsImpersonationContext of the new users identity.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="username">Username you want to impersonate.</param>
|
||||||
|
/// <param name="password">User's password to logon with.</param>
|
||||||
|
/// <param name="domain">Logon domain, defaults to local system.</param>
|
||||||
|
/// <returns>WindowsImpersonationContext if successful.</returns>
|
||||||
|
public static ImpersonationContext ImpersonateUser(string username, string password, string domain = null)
|
||||||
|
{
|
||||||
|
// Initialize tokens
|
||||||
|
IntPtr userToken = IntPtr.Zero;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!GetTokenByUser(username, password, domain, out userToken))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
// Create new identity using new primary token.
|
||||||
|
return new ImpersonationContext { Identity = new WindowsIdentity(userToken) };
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
// Close handle(s)
|
||||||
|
SafeCloseHandle(userToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tries to get an existing user token running <c>explorer.exe</c>. If <paramref name="duplicate"/> is set to <c>true</c>, the caller must call <see cref="NativeMethods.CloseHandle"/>
|
||||||
|
/// for the returned <paramref name="existingTokenHandle"/> when it is no longer required.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="existingTokenHandle">Outputs an existing token.</param>
|
||||||
|
/// <param name="duplicate"><c>true</c> to duplicate handle.</param>
|
||||||
|
public static bool GetTokenByProcess(out IntPtr existingTokenHandle, bool duplicate = false)
|
||||||
|
{
|
||||||
|
return GetTokenByProcess("explorer", out existingTokenHandle, duplicate);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tries to get an existing user token from the given <paramref name="processName"/>. If <paramref name="duplicate"/> is set to <c>true</c>, the caller must call <see cref="NativeMethods.CloseHandle"/>
|
||||||
|
/// for the returned <paramref name="existingTokenHandle"/> when it is no longer required.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="processName">Process name to take user account from (without .exe).</param>
|
||||||
|
/// <param name="existingTokenHandle">Outputs an existing token.</param>
|
||||||
|
/// <param name="duplicate"><c>true</c> to duplicate handle.</param>
|
||||||
|
/// <returns><c>true</c> if successful.</returns>
|
||||||
|
public static bool GetTokenByProcess(string processName, out IntPtr existingTokenHandle, bool duplicate = false)
|
||||||
|
{
|
||||||
|
// Try to find a process for given processName. There can be multiple processes, we will take the first one.
|
||||||
|
// Attention: when working on a RemoteDesktop/Terminal session, there can be multiple user logged in. The result of finding the first process
|
||||||
|
// might be not deterministic.
|
||||||
|
existingTokenHandle = IntPtr.Zero;
|
||||||
|
System.Diagnostics.Process process = System.Diagnostics.Process.GetProcessesByName(processName).FirstOrDefault();
|
||||||
|
if (process == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!NativeMethods.OpenProcessToken(process.Handle, NativeMethods.TokenAccess.AssignPrimary | NativeMethods.TokenAccess.Duplicate | NativeMethods.TokenAccess.Query, ref existingTokenHandle))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
IntPtr impersonationToken = existingTokenHandle;
|
||||||
|
return !duplicate || CreatePrimaryToken(impersonationToken, out existingTokenHandle);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{ }
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool CreatePrimaryToken(IntPtr impersonationToken, out IntPtr primaryToken)
|
||||||
|
{
|
||||||
|
// Convert the impersonation token into Primary token
|
||||||
|
NativeMethods.SecurityAttributes sa = new NativeMethods.SecurityAttributes();
|
||||||
|
|
||||||
|
bool retVal = NativeMethods.DuplicateTokenEx(
|
||||||
|
impersonationToken,
|
||||||
|
NativeMethods.TokenAccess.AssignPrimary | NativeMethods.TokenAccess.Duplicate | NativeMethods.TokenAccess.Query,
|
||||||
|
sa,
|
||||||
|
NativeMethods.SecurityImpersonationLevel.Identification,
|
||||||
|
NativeMethods.TokenType.Primary,
|
||||||
|
out primaryToken);
|
||||||
|
|
||||||
|
// Close the Token that was previously opened.
|
||||||
|
NativeMethods.CloseHandle(impersonationToken);
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tries to create a new user token based on given user credentials. Caller must call <see cref="NativeMethods.CloseHandle"/> for the returned <paramref name="duplicateTokenHandle"/>
|
||||||
|
/// when it is no longer required.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="uername">User name.</param>
|
||||||
|
/// <param name="password">Password.</param>
|
||||||
|
/// <param name="domain">Domain name, <c>null</c> defaults to computer name.</param>
|
||||||
|
/// <param name="duplicateTokenHandle">Outputs a duplicated token.</param>
|
||||||
|
/// <returns><c>true</c> if successful.</returns>
|
||||||
|
public static bool GetTokenByUser(string uername, string password, string domain, out IntPtr duplicateTokenHandle)
|
||||||
|
{
|
||||||
|
// Initialize tokens
|
||||||
|
duplicateTokenHandle = IntPtr.Zero;
|
||||||
|
IntPtr existingTokenHandle = IntPtr.Zero;
|
||||||
|
|
||||||
|
// If domain name was blank, assume local machine
|
||||||
|
if (string.IsNullOrWhiteSpace(domain))
|
||||||
|
domain = Environment.MachineName;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Get handle to token
|
||||||
|
if (!NativeMethods.LogonUser(uername, domain, password, NativeMethods.LogonType.Interactive, NativeMethods.LogonProvider.Default, out existingTokenHandle))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return NativeMethods.DuplicateToken(existingTokenHandle, NativeMethods.SecurityImpersonationLevel.Impersonation, ref duplicateTokenHandle);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
// Close handle(s)
|
||||||
|
SafeCloseHandle(existingTokenHandle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static void SafeCloseHandle(IntPtr handle)
|
||||||
|
{
|
||||||
|
if (handle != IntPtr.Zero)
|
||||||
|
NativeMethods.CloseHandle(handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static void SafeCloseHandle(ref IntPtr handle)
|
||||||
|
{
|
||||||
|
SafeCloseHandle(handle);
|
||||||
|
handle = IntPtr.Zero;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static void SafeCloseHandle(SafeFileHandle handle)
|
||||||
|
{
|
||||||
|
if (handle != null && !handle.IsInvalid)
|
||||||
|
handle.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static void SafeCloseHandle(ref SafeFileHandle handle)
|
||||||
|
{
|
||||||
|
SafeCloseHandle(handle);
|
||||||
|
handle = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
238
DisplayMagician/Processes/ImpersonationProcess.cs
Normal file
238
DisplayMagician/Processes/ImpersonationProcess.cs
Normal file
@ -0,0 +1,238 @@
|
|||||||
|
using System;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Text;
|
||||||
|
using Microsoft.Win32.SafeHandles;
|
||||||
|
|
||||||
|
namespace DisplayMagician.Processes
|
||||||
|
{
|
||||||
|
public class ImpersonationProcess : System.Diagnostics.Process
|
||||||
|
{
|
||||||
|
private const int HANDLE_FLAG_INHERIT = 1;
|
||||||
|
|
||||||
|
private const uint STARTF_USESHOWWINDOW = 0x00000001;
|
||||||
|
private const uint STARTF_USESTDHANDLES = 0x00000100;
|
||||||
|
|
||||||
|
private const int STD_INPUT_HANDLE = -10;
|
||||||
|
private const int STD_OUTPUT_HANDLE = -11;
|
||||||
|
private const int STD_ERROR_HANDLE = -12;
|
||||||
|
|
||||||
|
private const int SW_HIDE = 0;
|
||||||
|
private const int SW_MAXIMIZE = 3;
|
||||||
|
private const int SW_MINIMIZE = 6;
|
||||||
|
private const int SW_SHOW = 5;
|
||||||
|
|
||||||
|
private SafeFileHandle _stdinReadHandle;
|
||||||
|
private SafeFileHandle _stdinWriteHandle;
|
||||||
|
private SafeFileHandle _stdoutWriteHandle;
|
||||||
|
private SafeFileHandle _stderrWriteHandle;
|
||||||
|
private SafeFileHandle _stdoutReadHandle;
|
||||||
|
private SafeFileHandle _stderrReadHandle;
|
||||||
|
private NativeMethods.ProcessInformation _processInformation;
|
||||||
|
|
||||||
|
private string GetCommandLine()
|
||||||
|
{
|
||||||
|
StringBuilder result = new StringBuilder();
|
||||||
|
string applicationName = StartInfo.FileName.Trim();
|
||||||
|
string arguments = StartInfo.Arguments;
|
||||||
|
|
||||||
|
bool applicationNameIsQuoted = applicationName.StartsWith("\"") && applicationName.EndsWith("\"");
|
||||||
|
|
||||||
|
if (!applicationNameIsQuoted)
|
||||||
|
result.Append("\"");
|
||||||
|
|
||||||
|
result.Append(applicationName);
|
||||||
|
|
||||||
|
if (!applicationNameIsQuoted)
|
||||||
|
result.Append("\"");
|
||||||
|
|
||||||
|
if (arguments.Length > 0)
|
||||||
|
{
|
||||||
|
result.Append(" ");
|
||||||
|
result.Append(arguments);
|
||||||
|
}
|
||||||
|
return result.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public new void Kill()
|
||||||
|
{
|
||||||
|
IntPtr hProcess = IntPtr.Zero;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
int id = Id;
|
||||||
|
hProcess = NativeMethods.OpenProcess(NativeMethods.ProcessAccess.Terminate, false, id);
|
||||||
|
if (hProcess == IntPtr.Zero)
|
||||||
|
throw new Win32Exception(Marshal.GetLastWin32Error(), "ImpersonationProcess: OpenProcess failed");
|
||||||
|
NativeMethods.TerminateProcess(hProcess, 0);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
ImpersonationHelper.SafeCloseHandle(hProcess);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public new ProcessPriorityClass PriorityClass
|
||||||
|
{
|
||||||
|
get { return (ProcessPriorityClass)NativeMethods.GetPriorityClass(_processInformation.hProcess); }
|
||||||
|
set { NativeMethods.SetPriorityClass(_processInformation.hProcess, (uint)value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public new int ExitCode
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
uint exitCode;
|
||||||
|
if (NativeMethods.GetExitCodeProcess(_processInformation.hProcess, out exitCode))
|
||||||
|
return (int)exitCode;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool StartAsUser(string domain, string username, string password)
|
||||||
|
{
|
||||||
|
IntPtr userToken = IntPtr.Zero;
|
||||||
|
IntPtr token = IntPtr.Zero;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!NativeMethods.LogonUser(username, domain, password, NativeMethods.LogonType.Interactive, NativeMethods.LogonProvider.Default, out token))
|
||||||
|
throw new Win32Exception(Marshal.GetLastWin32Error(), String.Format("ImpersonationProcess: LogonUser {0}\\{1} failed", domain, username));
|
||||||
|
|
||||||
|
if (!NativeMethods.DuplicateTokenEx(token, NativeMethods.TokenAccess.AssignPrimary | NativeMethods.TokenAccess.Duplicate | NativeMethods.TokenAccess.Query, null, NativeMethods.SecurityImpersonationLevel.Impersonation, NativeMethods.TokenType.Primary, out userToken))
|
||||||
|
throw new Win32Exception(Marshal.GetLastWin32Error(), "ImpersonationProcess: DuplicateToken failed");
|
||||||
|
|
||||||
|
return StartAsUser(userToken);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
ImpersonationHelper.SafeCloseHandle(token);
|
||||||
|
ImpersonationHelper.SafeCloseHandle(userToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool StartAsUser(IntPtr userToken)
|
||||||
|
{
|
||||||
|
_processInformation = new NativeMethods.ProcessInformation();
|
||||||
|
NativeMethods.StartupInfo startupInfo = new NativeMethods.StartupInfo();
|
||||||
|
switch (StartInfo.WindowStyle)
|
||||||
|
{
|
||||||
|
case ProcessWindowStyle.Hidden:
|
||||||
|
startupInfo.wShowWindow = SW_HIDE;
|
||||||
|
break;
|
||||||
|
case ProcessWindowStyle.Maximized:
|
||||||
|
startupInfo.wShowWindow = SW_MAXIMIZE;
|
||||||
|
break;
|
||||||
|
case ProcessWindowStyle.Minimized:
|
||||||
|
startupInfo.wShowWindow = SW_MINIMIZE;
|
||||||
|
break;
|
||||||
|
case ProcessWindowStyle.Normal:
|
||||||
|
startupInfo.wShowWindow = SW_SHOW;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
CreateStandardPipe(out _stdinReadHandle, out _stdinWriteHandle, STD_INPUT_HANDLE, true, StartInfo.RedirectStandardInput);
|
||||||
|
CreateStandardPipe(out _stdoutReadHandle, out _stdoutWriteHandle, STD_OUTPUT_HANDLE, false, StartInfo.RedirectStandardOutput);
|
||||||
|
CreateStandardPipe(out _stderrReadHandle, out _stderrWriteHandle, STD_ERROR_HANDLE, false, StartInfo.RedirectStandardError);
|
||||||
|
|
||||||
|
startupInfo.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
|
||||||
|
startupInfo.hStdInput = _stdinReadHandle;
|
||||||
|
startupInfo.hStdOutput = _stdoutWriteHandle;
|
||||||
|
startupInfo.hStdError = _stderrWriteHandle;
|
||||||
|
|
||||||
|
NativeMethods.CreateProcessFlags createFlags = NativeMethods.CreateProcessFlags.CreateNewConsole | NativeMethods.CreateProcessFlags.CreateNewProcessGroup | NativeMethods.CreateProcessFlags.CreateDefaultErrorMode;
|
||||||
|
if (StartInfo.CreateNoWindow)
|
||||||
|
{
|
||||||
|
startupInfo.wShowWindow = SW_HIDE;
|
||||||
|
createFlags |= NativeMethods.CreateProcessFlags.CreateNoWindow;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create process as user, fail hard if this is unsuccessful so it can be caught in EncoderUnit
|
||||||
|
if (!NativeMethods.CreateProcessAsUserW(userToken, null, GetCommandLine(), IntPtr.Zero, IntPtr.Zero, true, createFlags, IntPtr.Zero, null, startupInfo, out _processInformation))
|
||||||
|
throw new Win32Exception(Marshal.GetLastWin32Error(), "ImpersonationProcess: CreateProcessAsUser failed");
|
||||||
|
|
||||||
|
if (_processInformation.hThread != (IntPtr)(-1))
|
||||||
|
{
|
||||||
|
ImpersonationHelper.SafeCloseHandle(ref _processInformation.hThread);
|
||||||
|
_processInformation.hThread = IntPtr.Zero;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (StartInfo.RedirectStandardInput)
|
||||||
|
{
|
||||||
|
ImpersonationHelper.SafeCloseHandle(ref _stdinReadHandle);
|
||||||
|
StreamWriter standardInput = new StreamWriter(new FileStream(_stdinWriteHandle, FileAccess.Write, 4096), Console.Out.Encoding) { AutoFlush = true };
|
||||||
|
SetField("standardInput", standardInput);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (StartInfo.RedirectStandardOutput)
|
||||||
|
{
|
||||||
|
ImpersonationHelper.SafeCloseHandle(ref _stdoutWriteHandle);
|
||||||
|
StreamReader standardOutput = new StreamReader(new FileStream(_stdoutReadHandle, FileAccess.Read, 4096), StartInfo.StandardOutputEncoding);
|
||||||
|
SetField("standardOutput", standardOutput);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (StartInfo.RedirectStandardError)
|
||||||
|
{
|
||||||
|
ImpersonationHelper.SafeCloseHandle(ref _stderrWriteHandle);
|
||||||
|
StreamReader standardError = new StreamReader(new FileStream(_stderrReadHandle, FileAccess.Read, 4096), StartInfo.StandardErrorEncoding);
|
||||||
|
SetField("standardError", standardError);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Workaround to get process handle as non-public SafeProcessHandle
|
||||||
|
Assembly processAssembly = typeof(System.Diagnostics.Process).Assembly;
|
||||||
|
Type processManager = processAssembly.GetType("System.Diagnostics.ProcessManager");
|
||||||
|
object safeProcessHandle = processManager.InvokeMember("OpenProcess", BindingFlags.InvokeMethod | BindingFlags.Public | BindingFlags.Static, null, this, new object[] { _processInformation.dwProcessId, 0x100000, false });
|
||||||
|
|
||||||
|
InvokeMethod("SetProcessHandle", safeProcessHandle);
|
||||||
|
InvokeMethod("SetProcessId", _processInformation.dwProcessId);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
ImpersonationHelper.SafeCloseHandle(ref _processInformation.hProcess);
|
||||||
|
ImpersonationHelper.SafeCloseHandle(ref _processInformation.hThread);
|
||||||
|
base.Dispose(disposing);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CreateStandardPipe(out SafeFileHandle readHandle, out SafeFileHandle writeHandle, int standardHandle, bool isInput, bool redirect)
|
||||||
|
{
|
||||||
|
if (redirect)
|
||||||
|
{
|
||||||
|
NativeMethods.SecurityAttributes security = new NativeMethods.SecurityAttributes { bInheritHandle = true };
|
||||||
|
|
||||||
|
bool success = NativeMethods.CreatePipe(out readHandle, out writeHandle, security, 4096);
|
||||||
|
|
||||||
|
if (success)
|
||||||
|
success = NativeMethods.SetHandleInformation(isInput ? writeHandle : readHandle, HANDLE_FLAG_INHERIT, 0);
|
||||||
|
|
||||||
|
if (!success)
|
||||||
|
throw new Win32Exception(Marshal.GetLastWin32Error(), "ImpersonationProcess: could not create standard pipe");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (isInput)
|
||||||
|
{
|
||||||
|
writeHandle = new SafeFileHandle(IntPtr.Zero, false);
|
||||||
|
readHandle = NativeMethods.GetStdHandle(standardHandle);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
readHandle = new SafeFileHandle(IntPtr.Zero, false);
|
||||||
|
writeHandle = NativeMethods.GetStdHandle(standardHandle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private object InvokeMethod(string member, params object[] args)
|
||||||
|
{
|
||||||
|
return typeof(System.Diagnostics.Process).InvokeMember(member, BindingFlags.InvokeMethod | BindingFlags.NonPublic | BindingFlags.Instance, null, this, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
private object SetField(string member, params object[] args)
|
||||||
|
{
|
||||||
|
return typeof(System.Diagnostics.Process).InvokeMember(member, BindingFlags.SetField | BindingFlags.NonPublic | BindingFlags.Instance, null, this, args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
489
DisplayMagician/Processes/NativeMethods.cs
Normal file
489
DisplayMagician/Processes/NativeMethods.cs
Normal file
@ -0,0 +1,489 @@
|
|||||||
|
using System;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using Microsoft.Win32.SafeHandles;
|
||||||
|
|
||||||
|
namespace DisplayMagician.Processes
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// This class is a static class containing definitions for system methods that are used in various places in MediaPortal.
|
||||||
|
/// </summary>
|
||||||
|
public static class NativeMethods
|
||||||
|
{
|
||||||
|
#region LoadLibraryEx Flags
|
||||||
|
|
||||||
|
public const uint LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR = 0x00000100;
|
||||||
|
public const uint LOAD_LIBRARY_SEARCH_DEFAULT_DIRS = 0x00001000;
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieves a module handle for the specified module. The module must have been loaded by the calling process.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="lpModuleName">The name of the loaded module (either a .dll or .exe file).</param>
|
||||||
|
/// <returns>If the function succeeds, the return value is a handle to the module.<br></br><br>If the function fails, the return value is NULL. To get extended error information, call Marshal.GetLastWin32Error.</br></returns>
|
||||||
|
[DllImport("kernel32.dll", EntryPoint = "GetModuleHandle", CharSet = CharSet.Ansi)]
|
||||||
|
public static extern IntPtr GetModuleHandle(string lpModuleName);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The GetProcAddress function retrieves the address of an exported function or variable from the specified dynamic-link library (DLL).
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="hModule">Handle to the DLL module that contains the function or variable. The LoadLibrary or GetModuleHandle function returns this handle.</param>
|
||||||
|
/// <param name="lpProcName">Pointer to a null-terminated string containing the function or variable name, or the function's ordinal value. If this parameter is an ordinal value, it must be in the low-order word; the high-order word must be zero.</param>
|
||||||
|
/// <returns>If the function succeeds, the return value is the address of the exported function or variable.<br></br><br>If the function fails, the return value is NULL. To get extended error information, call Marshal.GetLastWin32Error.</br></returns>
|
||||||
|
[DllImport("kernel32.dll", EntryPoint = "GetProcAddress", CharSet = CharSet.Ansi)]
|
||||||
|
public static extern IntPtr GetProcAddress(IntPtr hModule, string lpProcName);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines whether the specified process is running under WOW64.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="hProcess">A handle to the process. The handle must have the PROCESS_QUERY_INFORMATION or PROCESS_QUERY_LIMITED_INFORMATION access right.</param>
|
||||||
|
/// <param name="isWow64Process">A pointer to a value that is set to TRUE if the process is running under WOW64. If the process is running under 32-bit Windows, the value is set to FALSE. If the process is a 64-bit application running under 64-bit Windows, the value is also set to FALSE.</param>
|
||||||
|
/// <returns>If the function succeeds, the return value is nonzero.<br></br><br>If the function fails, the return value is zero. To get extended error information, call Marshal.GetLastWin32Error.</br></returns>
|
||||||
|
[DllImport("kernel32.dll", EntryPoint = "IsWow64Process")]
|
||||||
|
public static extern bool IsWow64Process(IntPtr hProcess, out bool isWow64Process);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The LoadLibrary function maps the specified executable module into the address space of the calling process.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="lpLibFileName">Pointer to a null-terminated string that names the executable module (either a .dll or .exe file). The name specified is the file name of the module and is not related to the name stored in the library module itself, as specified by the LIBRARY keyword in the module-definition (.def) file.</param>
|
||||||
|
/// <returns>If the function succeeds, the return value is a handle to the module.<br></br><br>If the function fails, the return value is NULL. To get extended error information, call Marshal.GetLastWin32Error.</br></returns>
|
||||||
|
[DllImport("kernel32.dll", EntryPoint = "LoadLibraryA", CharSet = CharSet.Ansi)]
|
||||||
|
public static extern IntPtr LoadLibrary(string lpLibFileName);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Loads the specified module into the address space of the calling process. The specified module may cause other modules to be loaded.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="lpFileName">Pointer to a null-terminated string that names the executable module (either a .dll or .exe file). The name specified is the file name of the module and is not related to the name stored in the library module itself, as specified by the LIBRARY keyword in the module-definition (.def) file.</param>
|
||||||
|
/// <param name="hFile">This parameter is reserved for future use. It must be IntPtr.Zero.</param>
|
||||||
|
/// <param name="dwFlags">The action to be taken when loading the module. If no flags are specified, the behavior of this function is identical to that of the <see cref="LoadLibrary"/> function.</param>
|
||||||
|
/// <returns>If the function succeeds, the return value is a handle to the module.<br/>If the function fails, the return value is NULL. To get extended error information, call Marshal.GetLastWin32Error.</returns>
|
||||||
|
[DllImport("kernel32.dll")]
|
||||||
|
public static extern IntPtr LoadLibraryEx(string lpFileName, IntPtr hFile, uint dwFlags);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The FreeLibrary function decrements the reference count of the loaded dynamic-link library (DLL). When the reference count reaches zero, the module is unmapped from the address space of the calling process and the handle is no longer valid.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="hLibModule">Handle to the loaded DLL module. The LoadLibrary or GetModuleHandle function returns this handle.</param>
|
||||||
|
/// <returns>If the function succeeds, the return value is nonzero.<br></br><br>If the function fails, the return value is zero. To get extended error information, call Marshal.GetLastWin32Error.</br></returns>
|
||||||
|
[DllImport("kernel32.dll", EntryPoint = "FreeLibrary", CharSet = CharSet.Ansi)]
|
||||||
|
public static extern int FreeLibrary(IntPtr hLibModule);
|
||||||
|
|
||||||
|
// Group type enum
|
||||||
|
public enum SecurityImpersonationLevel
|
||||||
|
{
|
||||||
|
Anonymous = 0,
|
||||||
|
Identification = 1,
|
||||||
|
Impersonation = 2,
|
||||||
|
Delegation = 3
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum LogonType
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// This logon type is intended for users who will be interactively using the computer, such as a user being logged on
|
||||||
|
/// by a terminal server, remote shell, or similar process.
|
||||||
|
/// This logon type has the additional expense of caching logon information for disconnected operations;
|
||||||
|
/// therefore, it is inappropriate for some client/server applications,
|
||||||
|
/// such as a mail server.
|
||||||
|
/// </summary>
|
||||||
|
Interactive = 2,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This logon type is intended for high performance servers to authenticate plaintext passwords.
|
||||||
|
|
||||||
|
/// The LogonUser function does not cache credentials for this logon type.
|
||||||
|
/// </summary>
|
||||||
|
Network = 3,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This logon type is intended for batch servers, where processes may be executing on behalf of a user without
|
||||||
|
/// their direct intervention. This type is also for higher performance servers that process many plaintext
|
||||||
|
/// authentication attempts at a time, such as mail or Web servers.
|
||||||
|
/// The LogonUser function does not cache credentials for this logon type.
|
||||||
|
/// </summary>
|
||||||
|
Batch = 4,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Indicates a service-type logon. The account provided must have the service privilege enabled.
|
||||||
|
/// </summary>
|
||||||
|
Service = 5,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This logon type is for GINA DLLs that log on users who will be interactively using the computer.
|
||||||
|
/// This logon type can generate a unique audit record that shows when the workstation was unlocked.
|
||||||
|
/// </summary>
|
||||||
|
Unlock = 7,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This logon type preserves the name and password in the authentication package, which allows the server to make
|
||||||
|
/// connections to other network servers while impersonating the client. A server can accept plaintext credentials
|
||||||
|
/// from a client, call LogonUser, verify that the user can access the system across the network, and still
|
||||||
|
/// communicate with other servers.
|
||||||
|
/// NOTE: Windows NT: This value is not supported.
|
||||||
|
/// </summary>
|
||||||
|
NetworkCleartext = 8,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This logon type allows the caller to clone its current token and specify new credentials for outbound connections.
|
||||||
|
/// The new logon session has the same local identifier but uses different credentials for other network connections.
|
||||||
|
/// NOTE: This logon type is supported only by the LOGON32_PROVIDER_WINNT50 logon provider.
|
||||||
|
/// NOTE: Windows NT: This value is not supported.
|
||||||
|
/// </summary>
|
||||||
|
NewCredentials = 9,
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum LogonProvider
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Use the standard logon provider for the system.
|
||||||
|
/// The default security provider is negotiate, unless you pass NULL for the domain name and the user name
|
||||||
|
/// is not in UPN format. In this case, the default provider is NTLM.
|
||||||
|
/// NOTE: Windows 2000/NT: The default security provider is NTLM.
|
||||||
|
/// </summary>
|
||||||
|
Default = 0,
|
||||||
|
WinNt35 = 1,
|
||||||
|
WinNt40 = 2,
|
||||||
|
WinNt50 = 3
|
||||||
|
}
|
||||||
|
|
||||||
|
[Flags]
|
||||||
|
public enum TokenAccess
|
||||||
|
{
|
||||||
|
StandardRightsRequired = 0x000F0000,
|
||||||
|
StandardRightsRead = 0x00020000,
|
||||||
|
AssignPrimary = 0x0001,
|
||||||
|
Duplicate = 0x0002,
|
||||||
|
Impersonate = 0x0004,
|
||||||
|
Query = 0x0008,
|
||||||
|
QuerySource = 0x0010,
|
||||||
|
AdjustPrivileges = 0x0020,
|
||||||
|
AdjustGroups = 0x0040,
|
||||||
|
AdjustDefault = 0x0080,
|
||||||
|
AdjustSessionId = 0x0100,
|
||||||
|
Read = (StandardRightsRead | Query),
|
||||||
|
AllAccess = (StandardRightsRequired | AssignPrimary | Duplicate | Impersonate | Query | QuerySource | AdjustPrivileges | AdjustGroups | AdjustDefault | AdjustSessionId)
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public struct ProcessInformation
|
||||||
|
{
|
||||||
|
public IntPtr hProcess;
|
||||||
|
public IntPtr hThread;
|
||||||
|
public int dwProcessId;
|
||||||
|
public int dwThreadId;
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public class StartupInfo
|
||||||
|
{
|
||||||
|
public int cb;
|
||||||
|
public string lpReserved;
|
||||||
|
public string lpDesktop;
|
||||||
|
public string lpTitle;
|
||||||
|
public uint dwX;
|
||||||
|
public uint dwY;
|
||||||
|
public uint dwXSize;
|
||||||
|
public uint dwYSize;
|
||||||
|
public uint dwXCountChars;
|
||||||
|
public uint dwYCountChars;
|
||||||
|
public uint dwFillAttribute;
|
||||||
|
public uint dwFlags;
|
||||||
|
public short wShowWindow;
|
||||||
|
public short cbReserved2;
|
||||||
|
public IntPtr lpReserved2;
|
||||||
|
public SafeFileHandle hStdInput;
|
||||||
|
public SafeFileHandle hStdOutput;
|
||||||
|
public SafeFileHandle hStdError;
|
||||||
|
|
||||||
|
public StartupInfo()
|
||||||
|
{
|
||||||
|
cb = Marshal.SizeOf(typeof(StartupInfo));
|
||||||
|
hStdInput = new SafeFileHandle(IntPtr.Zero, false);
|
||||||
|
hStdOutput = new SafeFileHandle(IntPtr.Zero, false);
|
||||||
|
hStdError = new SafeFileHandle(IntPtr.Zero, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public class SecurityAttributes
|
||||||
|
{
|
||||||
|
public uint nLength;
|
||||||
|
public IntPtr lpSecurityDescriptor;
|
||||||
|
public bool bInheritHandle;
|
||||||
|
public SecurityAttributes()
|
||||||
|
{
|
||||||
|
nLength = (uint)Marshal.SizeOf(typeof(SecurityAttributes));
|
||||||
|
lpSecurityDescriptor = IntPtr.Zero;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum TokenType
|
||||||
|
{
|
||||||
|
Primary = 1,
|
||||||
|
Impersonation
|
||||||
|
}
|
||||||
|
|
||||||
|
[Flags]
|
||||||
|
public enum CreateProcessFlags : uint
|
||||||
|
{
|
||||||
|
None = 0,
|
||||||
|
CreateBreakawayFromJob = 0x01000000,
|
||||||
|
CreateDefaultErrorMode = 0x04000000,
|
||||||
|
CreateNewConsole = 0x00000010,
|
||||||
|
CreateNewProcessGroup = 0x00000200,
|
||||||
|
CreateNoWindow = 0x08000000,
|
||||||
|
CreateProtectedProcess = 0x00040000,
|
||||||
|
CreatePreserveCodeAuthzLevel = 0x02000000,
|
||||||
|
CreateSeparateWowVdm = 0x00000800,
|
||||||
|
CreateSharedWowVdm = 0x00001000,
|
||||||
|
CreateSuspended = 0x00000004,
|
||||||
|
CreateUnicodeEnvironment = 0x00000400,
|
||||||
|
DebugOnlyThisProcess = 0x00000002,
|
||||||
|
DebugProcess = 0x00000001,
|
||||||
|
DetachedProcess = 0x00000008,
|
||||||
|
ExtendedStartupInfoPresent = 0x00080000,
|
||||||
|
InheritParentAffinity = 0x00010000
|
||||||
|
}
|
||||||
|
|
||||||
|
[Flags]
|
||||||
|
public enum ProcessAccess
|
||||||
|
{
|
||||||
|
AllAccess = CreateThread | DuplicateHandle | QueryInformation | SetInformation | Terminate | VmOperation | VmRead | VmWrite | Synchronize,
|
||||||
|
CreateThread = 0x2,
|
||||||
|
DuplicateHandle = 0x40,
|
||||||
|
QueryInformation = 0x400,
|
||||||
|
SetInformation = 0x200,
|
||||||
|
Terminate = 0x1,
|
||||||
|
VmOperation = 0x8,
|
||||||
|
VmRead = 0x10,
|
||||||
|
VmWrite = 0x20,
|
||||||
|
Synchronize = 0x100000
|
||||||
|
}
|
||||||
|
|
||||||
|
// Obtains user token
|
||||||
|
[DllImport("advapi32.dll", SetLastError = true)]
|
||||||
|
public static extern bool LogonUser(string username, string domain, string password, LogonType dwLogonType, LogonProvider dwLogonProvider, out IntPtr phToken);
|
||||||
|
|
||||||
|
// Closes open handles returned by LogonUser
|
||||||
|
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
|
||||||
|
public extern static bool CloseHandle(IntPtr handle);
|
||||||
|
|
||||||
|
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
|
||||||
|
public static extern bool SetHandleInformation(SafeFileHandle hObject, int dwMask, uint dwFlags);
|
||||||
|
|
||||||
|
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
|
||||||
|
public static extern SafeFileHandle GetStdHandle(int nStdHandle);
|
||||||
|
|
||||||
|
// Creates duplicate token handle.
|
||||||
|
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
|
||||||
|
public extern static bool DuplicateToken(IntPtr existingTokenHandle, SecurityImpersonationLevel impersonationLevel, ref IntPtr duplicateTokenHandle);
|
||||||
|
|
||||||
|
[DllImport("advapi32.dll", EntryPoint = "DuplicateTokenEx", SetLastError = true)]
|
||||||
|
public static extern bool DuplicateTokenEx(
|
||||||
|
IntPtr hExistingToken,
|
||||||
|
TokenAccess dwDesiredAccess,
|
||||||
|
SecurityAttributes lpThreadAttributes,
|
||||||
|
SecurityImpersonationLevel impersonationLevel,
|
||||||
|
TokenType dwTokenType,
|
||||||
|
out IntPtr phNewToken);
|
||||||
|
|
||||||
|
[DllImport("advapi32")]
|
||||||
|
public static extern bool OpenProcessToken(
|
||||||
|
IntPtr processHandle, // handle to process
|
||||||
|
TokenAccess desiredAccess, // desired access to process
|
||||||
|
ref IntPtr tokenHandle // handle to open access token
|
||||||
|
);
|
||||||
|
|
||||||
|
[DllImport("advapi32.DLL")]
|
||||||
|
public static extern bool ImpersonateLoggedOnUser(IntPtr hToken); // handle to token for logged-on user
|
||||||
|
|
||||||
|
[DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
|
||||||
|
public static extern bool CreateProcessAsUserW(IntPtr token, [MarshalAs(UnmanagedType.LPTStr)] string lpApplicationName, [MarshalAs(UnmanagedType.LPTStr)] string lpCommandLine, IntPtr lpProcessAttributes, IntPtr lpThreadAttributes, bool bInheritHandles, CreateProcessFlags dwCreationFlags, IntPtr lpEnvironment, [MarshalAs(UnmanagedType.LPTStr)] string lpCurrentDirectory, [In] StartupInfo lpStartupInfo, out ProcessInformation lpProcessInformation);
|
||||||
|
|
||||||
|
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
|
||||||
|
public static extern uint GetPriorityClass(IntPtr handle);
|
||||||
|
|
||||||
|
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
|
||||||
|
public static extern bool SetPriorityClass(IntPtr handle, uint priorityClass);
|
||||||
|
|
||||||
|
[DllImport("kernel32.dll", SetLastError = true)]
|
||||||
|
[return: MarshalAs(UnmanagedType.Bool)]
|
||||||
|
public static extern bool GetExitCodeProcess(IntPtr hProcess, out uint lpExitCode);
|
||||||
|
|
||||||
|
[DllImport("kernel32.dll", SetLastError = true)]
|
||||||
|
[return: MarshalAs(UnmanagedType.Bool)]
|
||||||
|
public static extern bool TerminateProcess(IntPtr hProcess, uint uExitCode);
|
||||||
|
|
||||||
|
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
|
||||||
|
public static extern bool CreatePipe(out SafeFileHandle hReadPipe, out SafeFileHandle hWritePipe, SecurityAttributes lpPipeAttributes, int nSize);
|
||||||
|
|
||||||
|
[DllImport("kernel32.dll", CharSet = CharSet.Ansi, SetLastError = true)]
|
||||||
|
public static extern bool DuplicateHandle(IntPtr hSourceProcessHandle, SafeFileHandle hSourceHandle, IntPtr hTargetProcess, out SafeFileHandle targetHandle, int dwDesiredAccess, bool bInheritHandle, int dwOptions);
|
||||||
|
|
||||||
|
[DllImport("kernel32.dll", CharSet = CharSet.Ansi, SetLastError = true)]
|
||||||
|
public static extern IntPtr GetCurrentProcess();
|
||||||
|
|
||||||
|
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
|
||||||
|
public static extern IntPtr OpenProcess(ProcessAccess dwDesiredAccess, [MarshalAs(UnmanagedType.Bool)] bool bInheritHandle, int dwProcessId);
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public struct Message
|
||||||
|
{
|
||||||
|
public IntPtr hwnd;
|
||||||
|
public int message;
|
||||||
|
public IntPtr wParam;
|
||||||
|
public IntPtr lParam;
|
||||||
|
public int time;
|
||||||
|
public int pt_x;
|
||||||
|
public int pt_y;
|
||||||
|
}
|
||||||
|
|
||||||
|
public delegate int WndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
|
||||||
|
public struct WindowClass
|
||||||
|
{
|
||||||
|
public uint style;
|
||||||
|
public WndProc lpfnWndProc;
|
||||||
|
public int cbClsExtra;
|
||||||
|
public int cbWndExtra;
|
||||||
|
public IntPtr hInstance;
|
||||||
|
public IntPtr hIcon;
|
||||||
|
public IntPtr hCursor;
|
||||||
|
public IntPtr hbrBackground;
|
||||||
|
public string lpszMenuName;
|
||||||
|
public string lpszClassName;
|
||||||
|
}
|
||||||
|
|
||||||
|
#region WndProc message constants
|
||||||
|
|
||||||
|
public const int WM_QUIT = 0x0012;
|
||||||
|
public const int WM_POWERBROADCAST = 0x0218;
|
||||||
|
public const int BROADCAST_QUERY_DENY = 0x424D5144;
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
[DllImport("user32", CharSet = CharSet.Auto)]
|
||||||
|
public static extern int RegisterClass(ref WindowClass windowClass);
|
||||||
|
|
||||||
|
[DllImport("user32.dll")]
|
||||||
|
public static extern int DefWindowProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);
|
||||||
|
|
||||||
|
[DllImport("user32.dll", CharSet = CharSet.Ansi, ExactSpelling = true)]
|
||||||
|
public static extern bool GetMessageA([In, Out] ref Message message, IntPtr hWnd, int uMsgFilterMin, int uMsgFilterMax);
|
||||||
|
|
||||||
|
[DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
|
||||||
|
public static extern bool TranslateMessage([In, Out] ref Message message);
|
||||||
|
|
||||||
|
[DllImport("user32.dll", CharSet = CharSet.Ansi, ExactSpelling = true)]
|
||||||
|
public static extern IntPtr DispatchMessageA([In] ref Message message);
|
||||||
|
|
||||||
|
[DllImport("user32.dll", CharSet = CharSet.Auto)]
|
||||||
|
public static extern IntPtr CreateWindowEx(uint dwExStyle, string className, string windowName, uint dwStyle, int x, int y,
|
||||||
|
int width, int height, IntPtr hWndParent, IntPtr hMenu, IntPtr hInstance, IntPtr lpParam);
|
||||||
|
|
||||||
|
[DllImport("user32.dll", CharSet = CharSet.Auto)]
|
||||||
|
public static extern bool DestroyWindow(IntPtr hWnd);
|
||||||
|
|
||||||
|
[DllImport("user32.dll")]
|
||||||
|
public static extern bool PostThreadMessage(uint idThread, uint msg, IntPtr wParam, IntPtr lParam);
|
||||||
|
|
||||||
|
[DllImport("kernel32.dll")]
|
||||||
|
public static extern uint GetCurrentThreadId();
|
||||||
|
|
||||||
|
#region FindWindow / BringToFront
|
||||||
|
|
||||||
|
public enum ShowWindowFlags
|
||||||
|
{
|
||||||
|
Hide = 0,
|
||||||
|
ShowNormal = 1,
|
||||||
|
ShowMinimized = 2,
|
||||||
|
ShowMaximized = 3,
|
||||||
|
ShowNoActivate = 4,
|
||||||
|
Show = 5,
|
||||||
|
Minimize = 6,
|
||||||
|
ShowMinNoActivate = 7,
|
||||||
|
ShowNotActivated = 8,
|
||||||
|
Restore = 9,
|
||||||
|
ShowDefault = 10,
|
||||||
|
ForceMinimize = 11
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Interop
|
||||||
|
|
||||||
|
[DllImport("user32")]
|
||||||
|
private static extern bool AttachThreadInput(uint nThreadId, int nThreadIdTo, bool bAttach);
|
||||||
|
|
||||||
|
[DllImport("user32")]
|
||||||
|
private static extern bool BringWindowToTop(IntPtr hWnd);
|
||||||
|
|
||||||
|
[DllImport("user32")]
|
||||||
|
private static extern IntPtr GetForegroundWindow();
|
||||||
|
|
||||||
|
[DllImport("user32")]
|
||||||
|
private static extern int GetWindowThreadProcessId(IntPtr hWnd, out int unused);
|
||||||
|
|
||||||
|
[DllImport("user32")]
|
||||||
|
private static extern bool SetFocus(IntPtr hWnd);
|
||||||
|
|
||||||
|
[DllImport("user32", SetLastError = true)]
|
||||||
|
public static extern bool SetForegroundWindow(IntPtr _hwnd);
|
||||||
|
|
||||||
|
[DllImport("user32", SetLastError = true)]
|
||||||
|
public static extern uint ShowWindow(IntPtr hwnd, ShowWindowFlags showCommand);
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
public static bool SetForegroundWindow(IntPtr window, bool force)
|
||||||
|
{
|
||||||
|
IntPtr windowForeground = GetForegroundWindow();
|
||||||
|
|
||||||
|
if (window == windowForeground)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool setAttempt = SetForegroundWindow(window);
|
||||||
|
|
||||||
|
if (force == false || setAttempt)
|
||||||
|
{
|
||||||
|
return setAttempt;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (windowForeground == IntPtr.Zero)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we don't attach successfully to the windows thread then we're out of options
|
||||||
|
int processId;
|
||||||
|
int fgWindowPID = GetWindowThreadProcessId(windowForeground, out processId);
|
||||||
|
|
||||||
|
if (fgWindowPID == -1)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we don't attach successfully to the windows thread then we're out of options
|
||||||
|
uint curThreadID = GetCurrentThreadId();
|
||||||
|
bool attached = AttachThreadInput(curThreadID, fgWindowPID, true);
|
||||||
|
int lastError = Marshal.GetLastWin32Error();
|
||||||
|
|
||||||
|
if (!attached)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
SetForegroundWindow(window);
|
||||||
|
BringWindowToTop(window);
|
||||||
|
SetFocus(window);
|
||||||
|
|
||||||
|
AttachThreadInput(curThreadID, fgWindowPID, false);
|
||||||
|
|
||||||
|
// we've done all that we can so base our return value on whether we have succeeded or not
|
||||||
|
return (GetForegroundWindow() == window);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion FindWindow / BringToFront
|
||||||
|
}
|
||||||
|
}
|
1753
DisplayMagician/Processes/ProcessUtils.cs
Normal file
1753
DisplayMagician/Processes/ProcessUtils.cs
Normal file
File diff suppressed because it is too large
Load Diff
68
DisplayMagician/Processes/SingleInstanceHelper.cs
Normal file
68
DisplayMagician/Processes/SingleInstanceHelper.cs
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
using System;
|
||||||
|
using System.Security.AccessControl;
|
||||||
|
using System.Security.Principal;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
|
namespace DisplayMagician.Processes
|
||||||
|
{
|
||||||
|
public class SingleInstanceHelper
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Windows message id to bring MP2-Client to front.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Ideally this field should be kept within it's parent application, not within <see cref="MediaPortal.Utilities"/>.
|
||||||
|
/// It has been added here to keep <c>MediaPortal.Client</c> and <c>SkinEngine</c> projects independent from each other.
|
||||||
|
/// </remarks>
|
||||||
|
public static readonly uint SHOW_MP2_CLIENT_MESSAGE = WindowsAPI.RegisterWindowMessage("SHOW_MP2_CLIENT_MESSAGE");
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Windows message id to bring MP2-ServiceMonitor to front.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Ideally this field should be kept within it's parent application, not within <see cref="MediaPortal.Utilities"/>.
|
||||||
|
/// It has been added here to be consistent to <c>MediaPortal.Client</c>.
|
||||||
|
/// </remarks>
|
||||||
|
public static readonly uint SHOW_MP2_SERVICEMONITOR_MESSAGE = WindowsAPI.RegisterWindowMessage("SHOW_MP2_SERVICEMONITOR_MESSAGE");
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Check if an application is running or not
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>returns true if already running</returns>
|
||||||
|
public static bool IsAlreadyRunning(string MUTEX_ID, out Mutex mutex)
|
||||||
|
{
|
||||||
|
// Allow only one instance
|
||||||
|
mutex = new Mutex(false, MUTEX_ID);
|
||||||
|
|
||||||
|
var allowEveryoneRule = new MutexAccessRule(new SecurityIdentifier(WellKnownSidType.WorldSid, null),
|
||||||
|
MutexRights.FullControl, AccessControlType.Allow);
|
||||||
|
var securitySettings = new MutexSecurity();
|
||||||
|
securitySettings.AddAccessRule(allowEveryoneRule);
|
||||||
|
mutex.SetAccessControl(securitySettings);
|
||||||
|
|
||||||
|
bool hasHandle = false;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Check if we can start the application
|
||||||
|
hasHandle = mutex.WaitOne(500, false);
|
||||||
|
}
|
||||||
|
catch (AbandonedMutexException)
|
||||||
|
{
|
||||||
|
// The mutex was abandoned in another process, it will still get aquired
|
||||||
|
hasHandle = true;
|
||||||
|
}
|
||||||
|
return !hasHandle;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Broadcasts a <param name="messageId">message</param> through the system to inform an existing instance to show up.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="messageId">The id to a static windows message, which should be broadcasted.</param>
|
||||||
|
public static void SwitchToCurrentInstance(uint messageId)
|
||||||
|
{
|
||||||
|
// Send our Win32 message to make the currently running instance
|
||||||
|
// Jump on top of all the other windows
|
||||||
|
WindowsAPI.PostMessage((IntPtr)WindowsAPI.HWND_BROADCAST, messageId, IntPtr.Zero, IntPtr.Zero);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
396
DisplayMagician/Processes/WindowsAPI.cs
Normal file
396
DisplayMagician/Processes/WindowsAPI.cs
Normal file
@ -0,0 +1,396 @@
|
|||||||
|
using System;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace DisplayMagician.Processes
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// For calls to the Windows API, this class should be used instead of directly using
|
||||||
|
/// the underlaying system's API. This class hides the concrete underlaying system and will
|
||||||
|
/// use different system functions depending on the underlaying system.
|
||||||
|
/// </summary>
|
||||||
|
public static class WindowsAPI
|
||||||
|
{
|
||||||
|
#region Windows API
|
||||||
|
|
||||||
|
[FlagsAttribute]
|
||||||
|
public enum EXECUTION_STATE : uint
|
||||||
|
{
|
||||||
|
ES_AWAYMODE_REQUIRED = 0x00000040,
|
||||||
|
ES_CONTINUOUS = 0x80000000,
|
||||||
|
ES_DISPLAY_REQUIRED = 0x00000002,
|
||||||
|
ES_SYSTEM_REQUIRED = 0x00000001
|
||||||
|
// Legacy flag, should not be used.
|
||||||
|
// ES_USER_PRESENT = 0x00000004
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handle to send a message to all top-level windows in the system, including disabled or invisible unowned windows,
|
||||||
|
/// overlapped windows, and pop-up windows; but the message is not sent to child windows.
|
||||||
|
/// </summary>
|
||||||
|
public const int HWND_BROADCAST = 0xffff;
|
||||||
|
|
||||||
|
[DllImport("kernel32.dll", SetLastError = true)]
|
||||||
|
public static extern EXECUTION_STATE SetThreadExecutionState(EXECUTION_STATE esFlags);
|
||||||
|
|
||||||
|
[DllImport("powrprof.dll", SetLastError = true)]
|
||||||
|
public static extern bool SetSuspendState(bool hibernate, bool forceCritical, bool disableWakeEvent);
|
||||||
|
|
||||||
|
[DllImport("user32.dll", SetLastError = true)]
|
||||||
|
private static extern bool SystemParametersInfo(uint uiAction, bool uiParam, IntPtr pvParam, uint fWinIni);
|
||||||
|
|
||||||
|
[DllImport("user32.dll", SetLastError = true)]
|
||||||
|
private static extern bool SystemParametersInfo(uint uiAction, uint uiParam, ref bool result, uint fWinIni);
|
||||||
|
|
||||||
|
#region ExitWindowsEx
|
||||||
|
|
||||||
|
// Source: http://ithoughthecamewithyou.com/post/Reboot-computer-in-C-NET.aspx
|
||||||
|
public static void ExitWindowsEx(EXIT_WINDOWS exitMode)
|
||||||
|
{
|
||||||
|
IntPtr tokenHandle = IntPtr.Zero;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Get process token
|
||||||
|
if (!OpenProcessToken(System.Diagnostics.Process.GetCurrentProcess().Handle,
|
||||||
|
TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES,
|
||||||
|
out tokenHandle))
|
||||||
|
{
|
||||||
|
throw new Win32Exception(Marshal.GetLastWin32Error(),
|
||||||
|
"Failed to open process token handle");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lookup the shutdown privilege
|
||||||
|
TOKEN_PRIVILEGES tokenPrivs = new TOKEN_PRIVILEGES();
|
||||||
|
tokenPrivs.PrivilegeCount = 1;
|
||||||
|
tokenPrivs.Privileges = new LUID_AND_ATTRIBUTES[1];
|
||||||
|
tokenPrivs.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
|
||||||
|
|
||||||
|
if (!LookupPrivilegeValue(null,
|
||||||
|
SE_SHUTDOWN_NAME,
|
||||||
|
out tokenPrivs.Privileges[0].Luid))
|
||||||
|
{
|
||||||
|
throw new Win32Exception(Marshal.GetLastWin32Error(),
|
||||||
|
"Failed to open lookup shutdown privilege");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the shutdown privilege to the process token
|
||||||
|
if (!AdjustTokenPrivileges(tokenHandle,
|
||||||
|
false,
|
||||||
|
ref tokenPrivs,
|
||||||
|
0,
|
||||||
|
IntPtr.Zero,
|
||||||
|
IntPtr.Zero))
|
||||||
|
{
|
||||||
|
throw new Win32Exception(Marshal.GetLastWin32Error(),
|
||||||
|
"Failed to adjust process token privileges");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Perform the exit operation
|
||||||
|
if (!ExitWindowsEx(exitMode,
|
||||||
|
ShutdownReason.MajorApplication |
|
||||||
|
ShutdownReason.MinorInstallation |
|
||||||
|
ShutdownReason.FlagPlanned))
|
||||||
|
{
|
||||||
|
throw new Win32Exception(Marshal.GetLastWin32Error(),
|
||||||
|
String.Format("Failed to exit the system ({0})", exitMode));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
// Close the process token
|
||||||
|
if (tokenHandle != IntPtr.Zero)
|
||||||
|
{
|
||||||
|
CloseHandle(tokenHandle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// http://msdn.microsoft.com/library/windows/desktop/aa376868
|
||||||
|
|
||||||
|
[FlagsAttribute]
|
||||||
|
public enum EXIT_WINDOWS : uint
|
||||||
|
{
|
||||||
|
// ONE of the following is required
|
||||||
|
|
||||||
|
EWX_LOGOFF = 0,
|
||||||
|
EWX_POWEROFF = 0x00000008,
|
||||||
|
EWX_REBOOT = 0x00000002,
|
||||||
|
EWX_RESTARTAPPS = 0x00000040,
|
||||||
|
EWX_SHUTDOWN = 0x00000001,
|
||||||
|
|
||||||
|
// ONE of the following is optional
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Beginning with Windows 8: You can prepare the system for a faster startup by combining the EWX_HYBRID_SHUTDOWN flag with the EWX_SHUTDOWN flag.
|
||||||
|
/// </summary>
|
||||||
|
EWX_HYBRID_SHUTDOWN = 0x00400000,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This flag has no effect if terminal services is enabled. Otherwise, the system does not send the WM_QUERYENDSESSION message.
|
||||||
|
/// This can cause applications to lose data. Therefore, you should only use this flag in an emergency.
|
||||||
|
/// </summary>
|
||||||
|
EWX_FORCE = 0x00000004,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Forces processes to terminate if they do not respond to the WM_QUERYENDSESSION or WM_ENDSESSION message within the timeout interval.
|
||||||
|
/// For more information, see the http://msdn.microsoft.com/library/windows/desktop/aa376868.
|
||||||
|
/// </summary>
|
||||||
|
EWX_FORCEIFHUNG = 0x00000010,
|
||||||
|
}
|
||||||
|
|
||||||
|
[FlagsAttribute]
|
||||||
|
private enum ShutdownReason : uint
|
||||||
|
{
|
||||||
|
MajorApplication = 0x00040000,
|
||||||
|
MajorHardware = 0x00010000,
|
||||||
|
MajorLegacyApi = 0x00070000,
|
||||||
|
MajorOperatingSystem = 0x00020000,
|
||||||
|
MajorOther = 0x00000000,
|
||||||
|
MajorPower = 0x00060000,
|
||||||
|
MajorSoftware = 0x00030000,
|
||||||
|
MajorSystem = 0x00050000,
|
||||||
|
|
||||||
|
MinorBlueScreen = 0x0000000F,
|
||||||
|
MinorCordUnplugged = 0x0000000b,
|
||||||
|
MinorDisk = 0x00000007,
|
||||||
|
MinorEnvironment = 0x0000000c,
|
||||||
|
MinorHardwareDriver = 0x0000000d,
|
||||||
|
MinorHotfix = 0x00000011,
|
||||||
|
MinorHung = 0x00000005,
|
||||||
|
MinorInstallation = 0x00000002,
|
||||||
|
MinorMaintenance = 0x00000001,
|
||||||
|
MinorMMC = 0x00000019,
|
||||||
|
MinorNetworkConnectivity = 0x00000014,
|
||||||
|
MinorNetworkCard = 0x00000009,
|
||||||
|
MinorOther = 0x00000000,
|
||||||
|
MinorOtherDriver = 0x0000000e,
|
||||||
|
MinorPowerSupply = 0x0000000a,
|
||||||
|
MinorProcessor = 0x00000008,
|
||||||
|
MinorReconfig = 0x00000004,
|
||||||
|
MinorSecurity = 0x00000013,
|
||||||
|
MinorSecurityFix = 0x00000012,
|
||||||
|
MinorSecurityFixUninstall = 0x00000018,
|
||||||
|
MinorServicePack = 0x00000010,
|
||||||
|
MinorServicePackUninstall = 0x00000016,
|
||||||
|
MinorTermSrv = 0x00000020,
|
||||||
|
MinorUnstable = 0x00000006,
|
||||||
|
MinorUpgrade = 0x00000003,
|
||||||
|
MinorWMI = 0x00000015,
|
||||||
|
|
||||||
|
FlagUserDefined = 0x40000000,
|
||||||
|
FlagPlanned = 0x80000000
|
||||||
|
}
|
||||||
|
|
||||||
|
// Everything from here on is from pinvoke.net &
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
private struct LUID
|
||||||
|
{
|
||||||
|
public uint LowPart;
|
||||||
|
public int HighPart;
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
private struct LUID_AND_ATTRIBUTES
|
||||||
|
{
|
||||||
|
public LUID Luid;
|
||||||
|
public UInt32 Attributes;
|
||||||
|
}
|
||||||
|
|
||||||
|
private struct TOKEN_PRIVILEGES
|
||||||
|
{
|
||||||
|
public UInt32 PrivilegeCount;
|
||||||
|
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)]
|
||||||
|
public LUID_AND_ATTRIBUTES[] Privileges;
|
||||||
|
}
|
||||||
|
|
||||||
|
private const UInt32 TOKEN_QUERY = 0x0008;
|
||||||
|
private const UInt32 TOKEN_ADJUST_PRIVILEGES = 0x0020;
|
||||||
|
private const UInt32 SE_PRIVILEGE_ENABLED = 0x00000002;
|
||||||
|
private const string SE_SHUTDOWN_NAME = "SeShutdownPrivilege";
|
||||||
|
|
||||||
|
// Note: http://msdn.microsoft.com/library/windows/desktop/aa376873
|
||||||
|
// The ExitWindowsEx function returns as soon as it has initiated the shutdown process.
|
||||||
|
// The shutdown or logoff then proceeds asynchronously.
|
||||||
|
// The function is designed to stop all processes in the caller's logon session.
|
||||||
|
// Therefore, if you are not the interactive user, the function can succeed without actually shutting down the computer.
|
||||||
|
// If you are not the interactive user, use the InitiateSystemShutdown or InitiateSystemShutdownEx function.
|
||||||
|
[DllImport("user32.dll", SetLastError = true)]
|
||||||
|
[return: MarshalAs(UnmanagedType.Bool)]
|
||||||
|
private static extern bool ExitWindowsEx(EXIT_WINDOWS uFlags, ShutdownReason dwReason);
|
||||||
|
|
||||||
|
[DllImport("advapi32.dll", SetLastError = true)]
|
||||||
|
[return: MarshalAs(UnmanagedType.Bool)]
|
||||||
|
private static extern bool OpenProcessToken(IntPtr ProcessHandle,
|
||||||
|
UInt32 DesiredAccess,
|
||||||
|
out IntPtr TokenHandle);
|
||||||
|
|
||||||
|
[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
|
||||||
|
[return: MarshalAs(UnmanagedType.Bool)]
|
||||||
|
private static extern bool LookupPrivilegeValue(string lpSystemName,
|
||||||
|
string lpName,
|
||||||
|
out LUID lpLuid);
|
||||||
|
|
||||||
|
[DllImport("kernel32.dll", SetLastError = true)]
|
||||||
|
[return: MarshalAs(UnmanagedType.Bool)]
|
||||||
|
private static extern bool CloseHandle(IntPtr hObject);
|
||||||
|
|
||||||
|
[DllImport("advapi32.dll", SetLastError = true)]
|
||||||
|
[return: MarshalAs(UnmanagedType.Bool)]
|
||||||
|
private static extern bool AdjustTokenPrivileges(IntPtr TokenHandle,
|
||||||
|
[MarshalAs(UnmanagedType.Bool)] bool DisableAllPrivileges,
|
||||||
|
ref TOKEN_PRIVILEGES NewState,
|
||||||
|
UInt32 Zero,
|
||||||
|
IntPtr Null1,
|
||||||
|
IntPtr Null2);
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Places (posts) a message in the message queue associated with the thread that created the specified window and returns
|
||||||
|
/// without waiting for the thread to process the message.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="hwnd">A handle to the window whose window procedure is to receive the message.</param>
|
||||||
|
/// <param name="msg">The message to be posted.</param>
|
||||||
|
/// <param name="wparam">Additional message-specific information.</param>
|
||||||
|
/// <param name="lparam">Additional message-specific information.</param>
|
||||||
|
/// <returns><c>true</c>, if the function succeeds, else <c>false</c>.</returns>
|
||||||
|
[DllImport("user32.dll")]
|
||||||
|
public static extern bool PostMessage(IntPtr hwnd, uint msg, IntPtr wparam, IntPtr lparam);
|
||||||
|
|
||||||
|
[DllImport("user32.dll", SetLastError = true)]
|
||||||
|
public static extern bool DestroyWindow(IntPtr hWnd);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Registers a window message.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="lpString">Name of the message to be registered.</param>
|
||||||
|
/// <returns>If the message is successfully registered, the return value is a message identifier in the range 0xC000 through 0xFFFF.
|
||||||
|
/// If the function fails, the return value is zero. To get extended error information, call <see cref="Marshal.GetLastWin32Error"/>.</returns>
|
||||||
|
[DllImport("user32.dll", EntryPoint = "RegisterWindowMessageW")]
|
||||||
|
public static extern uint RegisterWindowMessage([MarshalAs(UnmanagedType.LPWStr)] string lpString);
|
||||||
|
|
||||||
|
[DllImport("user32.dll", EntryPoint = "CreateWindowExW", SetLastError = true)]
|
||||||
|
public static extern IntPtr CreateWindowEx(int dwExStyle, [MarshalAs(UnmanagedType.LPWStr)] string lpClassName,
|
||||||
|
[MarshalAs(UnmanagedType.LPWStr)] string lpWindowName, int dwStyle, int x, int y,
|
||||||
|
int nWidth, int nHeight, uint hWndParent, int hMenu, int hInstance,
|
||||||
|
int lpParam);
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
public const string AUTOSTART_REGISTRY_KEY = @"Software\Microsoft\Windows\Currentversion\Run";
|
||||||
|
|
||||||
|
public const int S_OK = 0x0;
|
||||||
|
public const int S_FALSE = 0x1;
|
||||||
|
|
||||||
|
public const int MAX_PATH = 260;
|
||||||
|
|
||||||
|
public const uint SPI_GETSCREENSAVEACTIVE = 0x0010;
|
||||||
|
public const uint SPI_SETSCREENSAVEACTIVE = 0x0011;
|
||||||
|
|
||||||
|
public static bool ScreenSaverEnabled
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
bool result = false;
|
||||||
|
SystemParametersInfo(SPI_GETSCREENSAVEACTIVE, 0, ref result, 0);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
set { SystemParametersInfo(SPI_SETSCREENSAVEACTIVE, value, IntPtr.Zero, 0); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns a string which contains the name and version of the operating system.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Operating system name and version.</returns>
|
||||||
|
public static string GetOsVersionString()
|
||||||
|
{
|
||||||
|
OperatingSystem os = Environment.OSVersion;
|
||||||
|
return os.Platform + "/" + os.Version;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the path of the given system's special folder.
|
||||||
|
/// <remarks>
|
||||||
|
/// If the requested special folder is an user folder, and the caller operates as local service, this method won't resolve the folder.
|
||||||
|
/// </remarks>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="folder">Folder to retrieve.</param>
|
||||||
|
/// <param name="folderPath">Will be set to the folder path if the result value is <c>true</c>.</param>
|
||||||
|
/// <returns><c>true</c>, if the specified special folder could be retrieved. Else <c>false</c>
|
||||||
|
/// will be returned.</returns>
|
||||||
|
public static bool GetSpecialFolder(Environment.SpecialFolder folder, out string folderPath)
|
||||||
|
{
|
||||||
|
folderPath = null;
|
||||||
|
switch (folder)
|
||||||
|
{
|
||||||
|
case Environment.SpecialFolder.MyMusic:
|
||||||
|
case Environment.SpecialFolder.MyPictures:
|
||||||
|
case Environment.SpecialFolder.MyVideos:
|
||||||
|
folderPath = Environment.GetFolderPath(folder);
|
||||||
|
return !string.IsNullOrWhiteSpace(folderPath);
|
||||||
|
default:
|
||||||
|
throw new NotImplementedException(string.Format("The handling for special folder '{0}' isn't implemented yet", folder));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* /// <summary>
|
||||||
|
/// Adds the application with the specified <paramref name="applicationPath"/> to the autostart
|
||||||
|
/// registry key. The application will be automatically started the next system startup.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="applicationPath">Path of the application to be auto-started.</param>
|
||||||
|
/// <param name="registerName">The name used in the registry as key for the autostart value.</param>
|
||||||
|
/// <param name="user">If set to <c>true</c>, the autostart application will be added to the HCKU
|
||||||
|
/// registry hive, else it will be added to the HKLM hive.</param>
|
||||||
|
/// <exception cref="EnvironmentException">If the appropriate registry key cannot accessed.</exception>
|
||||||
|
public static void AddAutostartApplication(string applicationPath, string registerName, bool user)
|
||||||
|
{
|
||||||
|
RegistryKey root = user ? Registry.CurrentUser : Registry.LocalMachine;
|
||||||
|
// open RegisteryKey with write access
|
||||||
|
using (RegistryKey key = root.OpenSubKey(AUTOSTART_REGISTRY_KEY, true))
|
||||||
|
{
|
||||||
|
if (key == null)
|
||||||
|
throw new EnvironmentException(@"Unable to access/create registry key '{0}\{1}'",
|
||||||
|
user ? "HKCU" : "HKLM", AUTOSTART_REGISTRY_KEY);
|
||||||
|
key.SetValue(registerName, applicationPath, RegistryValueKind.ExpandString);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Removes an application from the autostart registry key.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="registerName">The name used in the registry as key for the autostart value.</param>
|
||||||
|
/// <param name="user">If set to <c>true</c>, the autostart application will be removed from the HCKU
|
||||||
|
/// registry hive, else it will be removed from the HKLM hive.</param>
|
||||||
|
/// <exception cref="EnvironmentException">If the appropriate registry key cannot accessed.</exception>
|
||||||
|
public static void RemoveAutostartApplication(string registerName, bool user)
|
||||||
|
{
|
||||||
|
RegistryKey root = user ? Registry.CurrentUser : Registry.LocalMachine;
|
||||||
|
// open RegisteryKey with write access
|
||||||
|
using (RegistryKey key = root.OpenSubKey(AUTOSTART_REGISTRY_KEY, true))
|
||||||
|
{
|
||||||
|
if (key == null)
|
||||||
|
throw new EnvironmentException(@"Unable to access registry key '{0}\{1}'",
|
||||||
|
user ? "HKCU" : "HKLM", AUTOSTART_REGISTRY_KEY);
|
||||||
|
key.DeleteValue(registerName, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the application path for the application registered to be autostarted with the
|
||||||
|
/// specified <paramref name="registerName"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="registerName">The name used in the registry as key for the autostart value.</param>
|
||||||
|
/// <param name="user">If set to <c>true</c>, the autostart application path will be searched in the HCKU
|
||||||
|
/// registry hive, else it will be searched in the HKLM hive.</param>
|
||||||
|
/// <returns>Application path registered to be autostarted with the specified
|
||||||
|
/// <paramref name="registerName"/>.</returns>
|
||||||
|
public static string GetAutostartApplicationPath(string registerName, bool user)
|
||||||
|
{
|
||||||
|
RegistryKey root = user ? Registry.CurrentUser : Registry.LocalMachine;
|
||||||
|
using (RegistryKey key = root.OpenSubKey(AUTOSTART_REGISTRY_KEY))
|
||||||
|
return key == null ? null : key.GetValue(registerName) as string;
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
}
|
@ -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.280")]
|
[assembly: AssemblyVersion("2.1.0.281")]
|
||||||
[assembly: AssemblyFileVersion("2.1.0.280")]
|
[assembly: AssemblyFileVersion("2.1.0.281")]
|
||||||
[assembly: NeutralResourcesLanguageAttribute( "en" )]
|
[assembly: NeutralResourcesLanguageAttribute( "en" )]
|
||||||
[assembly: CLSCompliant(true)]
|
[assembly: CLSCompliant(true)]
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
using AudioSwitcher.AudioApi;
|
using AudioSwitcher.AudioApi;
|
||||||
using AudioSwitcher.AudioApi.CoreAudio;
|
using AudioSwitcher.AudioApi.CoreAudio;
|
||||||
using DisplayMagician.GameLibraries;
|
using DisplayMagician.GameLibraries;
|
||||||
|
using DisplayMagician.Processes;
|
||||||
using DisplayMagician.InterProcess;
|
using DisplayMagician.InterProcess;
|
||||||
using DisplayMagicianShared;
|
using DisplayMagicianShared;
|
||||||
using Microsoft.Toolkit.Uwp.Notifications;
|
using Microsoft.Toolkit.Uwp.Notifications;
|
||||||
@ -1921,8 +1922,9 @@ namespace DisplayMagician
|
|||||||
uint processID = 0;
|
uint processID = 0;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
ProcessUtils.PROCESS_INFORMATION processInfo;
|
//Processes.PROCESS_INFORMATION processInfo;
|
||||||
if (ProcessUtils.CreateProcessWithPriority(stopProg.Executable, stopProg.Arguments, ProcessUtils.TranslatePriorityToClass(stopProg.ProcessPriority), out processInfo))
|
//if (ProcessUtils.CreateProcessWithPriority(stopProg.Executable, stopProg.Arguments, TranslatePriorityToClass(stopProg.ProcessPriority), out processInfo))
|
||||||
|
if (ProcessUtils.StartProcess(stopProg.Executable, stopProg.Arguments, TranslatePriorityToClass(stopProg.ProcessPriority), out processInfo))
|
||||||
{
|
{
|
||||||
logger.Trace($"ShortcutRepository/RunShortcut: Successfully started Stop Program {stopProg.Executable} {stopProg.Arguments}");
|
logger.Trace($"ShortcutRepository/RunShortcut: Successfully started Stop Program {stopProg.Executable} {stopProg.Arguments}");
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user