#define TRACE using System; using System.Diagnostics; using System.Runtime.InteropServices; using System.Text; namespace ArchestrAServices.Common; public static class DetachedProcess { private static class NativeMethods { public struct STARTUPINFO { public int cb; public string lpReserved; public string lpDesktop; public string lpTitle; public int dwX; public int dwY; public int dwXSize; public int dwXCountChars; public int dwYCountChars; public int dwFillAttribute; public int dwFlags; public short wShowWindow; public short cbReserved2; public IntPtr lpReserved2; public IntPtr hStdInput; public IntPtr hStdOutput; public IntPtr hStdError; } public struct STARTUPINFOEX { public STARTUPINFO StartupInfo; public IntPtr lpAttributeList; } public struct PROCESS_INFORMATION { public IntPtr hProcess; public IntPtr hThread; public int dwProcessID; public int dwThreadID; } public struct SECURITY_ATTRIBUTES { public int Length; public IntPtr lpSecurityDescriptor; public bool bInheritHandle; } public const uint ZERO_FLAG = 0u; public const uint CREATE_BREAKAWAY_FROM_JOB = 16777216u; public const uint CREATE_DEFAULT_ERROR_MODE = 67108864u; public const uint CREATE_NEW_CONSOLE = 16u; public const uint CREATE_NEW_PROCESS_GROUP = 512u; public const uint CREATE_NO_WINDOW = 134217728u; public const uint CREATE_PROTECTED_PROCESS = 262144u; public const uint CREATE_PRESERVE_CODE_AUTHZ_LEVEL = 33554432u; public const uint CREATE_SEPARATE_WOW_VDM = 4096u; public const uint CREATE_SHARED_WOW_VDM = 4096u; public const uint CREATE_SUSPENDED = 4u; public const uint CREATE_UNICODE_ENVIRONMENT = 1024u; public const uint DEBUG_ONLY_THIS_PROCESS = 2u; public const uint DEBUG_PROCESS = 1u; public const uint DETACHED_PROCESS = 8u; public const uint EXTENDED_STARTUPINFO_PRESENT = 524288u; public const uint INHERIT_PARENT_AFFINITY = 65536u; public const uint PROC_THREAD_ATTRIBUTE_HANDLE_LIST = 131074u; public const uint PROC_THREAD_ATTRIBUTE_PARENT_PROCESS = 131072u; public const uint FILE_ACCESS_WRITE = 1073741824u; public const int STARTF_USESTDHANDLES = 256; [DllImport("kernel32.dll", BestFitMapping = false, CharSet = CharSet.Auto, SetLastError = true)] public static extern bool CreateProcess([MarshalAs(UnmanagedType.LPTStr)] string lpApplicationName, StringBuilder lpCommandLine, ref SECURITY_ATTRIBUTES lpProcessAttributes, ref SECURITY_ATTRIBUTES lpThreadAttributes, bool bInheritHandles, uint dwCreationFlags, IntPtr lpEnvironment, [MarshalAs(UnmanagedType.LPTStr)] string lpCurrentDirectory, [In] ref STARTUPINFOEX lpStartupInfo, out PROCESS_INFORMATION lpProcessInformation); [DllImport("kernel32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool InitializeProcThreadAttributeList(IntPtr lpAttributeList, int dwAttributeCount, int dwFlags, ref IntPtr lpSize); [DllImport("kernel32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool DeleteProcThreadAttributeList(IntPtr lpAttributeList); [DllImport("kernel32.dll", SetLastError = true)] public static extern bool CloseHandle(IntPtr hHandle); } public static Process Start(string executablePath, string commandLine) { SvcTrace.DiagDiagnostics.TraceEvent(TraceEventType.Information, 0, "Starting detached process {0} {1} with command '{2}'", Environment.CurrentDirectory, executablePath, commandLine); NativeMethods.STARTUPINFOEX lpStartupInfo = default(NativeMethods.STARTUPINFOEX); lpStartupInfo.StartupInfo.cb = Marshal.SizeOf((object)lpStartupInfo); lpStartupInfo.lpAttributeList = IntPtr.Zero; try { IntPtr lpSize = IntPtr.Zero; if (NativeMethods.InitializeProcThreadAttributeList(IntPtr.Zero, 1, 0, ref lpSize) || lpSize == IntPtr.Zero) { return null; } IntPtr intPtr = Marshal.AllocHGlobal(lpSize); if (!NativeMethods.InitializeProcThreadAttributeList(intPtr, 1, 0, ref lpSize)) { Marshal.FreeHGlobal(intPtr); return null; } lpStartupInfo.lpAttributeList = intPtr; NativeMethods.SECURITY_ATTRIBUTES lpProcessAttributes = default(NativeMethods.SECURITY_ATTRIBUTES); lpProcessAttributes.Length = Marshal.SizeOf((object)lpProcessAttributes); lpProcessAttributes.bInheritHandle = true; StringBuilder lpCommandLine = new StringBuilder(commandLine); if (NativeMethods.CreateProcess(executablePath, lpCommandLine, ref lpProcessAttributes, ref lpProcessAttributes, bInheritHandles: false, 8u, IntPtr.Zero, null, ref lpStartupInfo, out var lpProcessInformation)) { NativeMethods.CloseHandle(lpProcessInformation.hProcess); NativeMethods.CloseHandle(lpProcessInformation.hThread); return Process.GetProcessById(lpProcessInformation.dwProcessID); } int lastWin32Error = Marshal.GetLastWin32Error(); SvcTrace.DiagDiagnostics.TraceEvent(TraceEventType.Warning, 0, "Starting detached process {0} failed with error {1}", executablePath, lastWin32Error); return null; } catch (Exception ex) { SvcTrace.DiagControl.TraceEvent(TraceEventType.Warning, 0, "Detached process start failed: {0}", ex.Message); return null; } finally { if (lpStartupInfo.lpAttributeList != IntPtr.Zero) { NativeMethods.DeleteProcThreadAttributeList(lpStartupInfo.lpAttributeList); Marshal.FreeHGlobal(lpStartupInfo.lpAttributeList); } } } }