/// <summary>Starts the limited process.</summary> /// <param name="filename">The file to start as a limited process.</param> /// <param name="arguments">The arguments to pass to the process.</param> /// <param name="fallback">Fallback on Process.Start() if this method fails (for whatever reason). We use a simple Process.Start(filename, arguments) call -- we don't pass in all the details.</param> /// <param name="waitForExit">Whether to wait for the execution of the file to finish before continuing.</param> /// <param name="windowStyle">How to show the started process.</param> /// <returns>The exit code if waitForExit = true, otherwise it returns 0.</returns> public static uint Start(string filename, string arguments = null, bool fallback = true, bool waitForExit = false, ProcessWindowStyle windowStyle = ProcessWindowStyle.Normal) { bool processCreated = false; uint exitCode = 0; string errorDetails = null; int errorCode = 0; if (VistaTools.AtLeastVista() && VistaTools.IsUserAnAdmin()) { // early exit for files that don't exist if (!File.Exists(filename)) { throw new Exception("The system cannot find the file specified"); } // Get window handle representing the desktop shell. This might not work if there is no shell window, or when // using a custom shell. Also note that we're assuming that the shell is not running elevated. IntPtr hShellWnd = GetShellWindow(); int dwShellPID = 0; // If we're falling back then don't throw an error -- // just fall through to the end of function where Process.Start() is called. if (hShellWnd == IntPtr.Zero) { if (!fallback) { throw new Exception("Unable to locate shell window; you might be using a custom shell"); } } else { // Get the ID of the desktop shell process. GetWindowThreadProcessId(hShellWnd, out dwShellPID); } if (dwShellPID != 0) { //Open the desktop shell process in order to get the process token. // PROCESS_QUERY_INFORMATION = 0x00000400 IntPtr hShellProcess = OpenProcess(0x0400, false, dwShellPID); if (hShellProcess != IntPtr.Zero) { IntPtr hShellProcessToken; //Get the process token of the desktop shell. //TOKEN_DUPLICATE = 0x0002 if (OpenProcessToken(hShellProcess, 0x2, out hShellProcessToken)) { IntPtr hPrimaryToken; //Duplicate the shell's process token to get a primary token. //TOKEN_QUERY = 0x0008 //TOKEN_ASSIGN_PRIMARY = 0x0001 //TOKEN_DUPLICATE = 0x0002 //TOKEN_ADJUST_DEFAULT = 0x0080 //TOKEN_ADJUST_SESSIONID = 0x0100; const uint dwTokenRights = 0x0008 | 0x0001 | 0x0002 | 0x0080 | 0x0100; //SecurityImpersonation = 2 //TokenPrimary = 1 if (DuplicateTokenEx(hShellProcessToken, dwTokenRights, IntPtr.Zero, 2, 1, out hPrimaryToken)) { //Start the target process with the new token. PROCESS_INFORMATION pi; STARTUPINFO si = new STARTUPINFO(); if (windowStyle != ProcessWindowStyle.Normal) { // set how we'll be starting the process si.dwFlags = STARTF_USESHOWWINDOW; switch (windowStyle) { case ProcessWindowStyle.Hidden: si.wShowWindow = SW_HIDE; break; case ProcessWindowStyle.Maximized: si.wShowWindow = SW_MAXIMIZE; break; case ProcessWindowStyle.Minimized: si.wShowWindow = SW_MINIMIZE; break; } } si.cb = Marshal.SizeOf(si); if (filename.EndsWith(".exe")) { // build the arguments string // filenames must be quoted or else the commandline args get blown if (string.IsNullOrEmpty(arguments)) { arguments = "\"" + filename + "\""; } else { arguments = "\"" + filename + "\" " + arguments; } } else // *.bat and *.cmd files { string system32 = Environment.GetFolderPath(Environment.SpecialFolder.System); if (string.IsNullOrEmpty(arguments)) { arguments = "\"" + system32 + "\\cmd.exe\" /c \"" + filename + "\""; } else { arguments = "\"" + system32 + "\\cmd.exe\" /c \"\"" + filename + "\" " + arguments + "\""; } filename = system32 + "\\cmd.exe"; } processCreated = CreateProcessWithTokenW(hPrimaryToken, 0, filename, arguments, 0, IntPtr.Zero, Path.GetDirectoryName(filename), ref si, out pi); if (processCreated) { // wait for the process to finish executing // then get the exit code if (waitForExit) { if (WaitForSingleObject(pi.hProcess, INFINITE) == WAIT_FAILED) { // handle wait failure errorCode = Marshal.GetLastWin32Error(); errorDetails = "WaitForSingleObject() failed with the error code " + errorCode + "."; } else if (!GetExitCodeProcess(pi.hProcess, out exitCode)) { // getting the exit code failed errorCode = Marshal.GetLastWin32Error(); errorDetails = "GetExitCodeProcess() failed with the error code " + errorCode + "."; } } // cleanup the process handles CloseHandle(pi.hProcess); CloseHandle(pi.hThread); } else if (!fallback) { // if we're not falling back to regular old "Process.Start()", // then throw an error with enough info that the user can do something about it. errorCode = Marshal.GetLastWin32Error(); errorDetails = "CreateProcessWithTokenW() failed with the error code " + errorCode + "."; } CloseHandle(hPrimaryToken); } else if (!fallback) { errorCode = Marshal.GetLastWin32Error(); errorDetails = "DuplicateTokenEx() on the desktop shell process failed with the error code " + errorCode + "."; } CloseHandle(hShellProcessToken); } else if (!fallback) { errorCode = Marshal.GetLastWin32Error(); errorDetails = "OpenProcessToken() on the desktop shell process failed with the error code " + errorCode + "."; } CloseHandle(hShellProcess); } else if (!fallback) { errorCode = Marshal.GetLastWin32Error(); errorDetails = "OpenProcess() on the desktop shell process failed with the error code " + errorCode + "."; } } else if (!fallback) { errorDetails = "Unable to get the window thread process ID of the desktop shell process."; } } // the process failed to be created for any number of reasons // just create it using the regular method if (!processCreated) { if (fallback) { Process.Start(filename, arguments); } else // not falling back and the process failed to execute { throw new ExternalException("Failed to start as a limited process. " + errorDetails, errorCode); } } return(exitCode); }
public static uint Start(string filename, string arguments = null, bool fallback = true, bool waitForExit = false, ProcessWindowStyle windowStyle = ProcessWindowStyle.Normal) { bool flag = false; uint lpExitCode = 0u; string str = null; int num = 0; if (VistaTools.AtLeastVista() && VistaTools.IsUserAnAdmin()) { if (!File.Exists(filename)) { throw new Exception("The system cannot find the file specified"); } IntPtr shellWindow = GetShellWindow(); int lpdwProcessId = 0; if (shellWindow == IntPtr.Zero) { if (!fallback) { throw new Exception("Unable to locate shell window; you might be using a custom shell"); } } else { GetWindowThreadProcessId(shellWindow, out lpdwProcessId); } if (lpdwProcessId != 0) { IntPtr intPtr = OpenProcess(1024, bInheritHandle: false, lpdwProcessId); if (intPtr != IntPtr.Zero) { if (OpenProcessToken(intPtr, 2u, out IntPtr TokenHandle)) { if (DuplicateTokenEx(TokenHandle, 395u, IntPtr.Zero, 2, 1, out IntPtr phNewToken)) { STARTUPINFO lpStartupInfo = default(STARTUPINFO); if (windowStyle != 0) { lpStartupInfo.dwFlags = 1u; switch (windowStyle) { case ProcessWindowStyle.Hidden: lpStartupInfo.wShowWindow = 0; break; case ProcessWindowStyle.Maximized: lpStartupInfo.wShowWindow = 3; break; case ProcessWindowStyle.Minimized: lpStartupInfo.wShowWindow = 6; break; } } lpStartupInfo.cb = Marshal.SizeOf((object)lpStartupInfo); if (filename.EndsWith(".exe")) { arguments = ((!string.IsNullOrEmpty(arguments)) ? ("\"" + filename + "\" " + arguments) : ("\"" + filename + "\"")); } else { string folderPath = Environment.GetFolderPath(Environment.SpecialFolder.System); arguments = ((!string.IsNullOrEmpty(arguments)) ? ("\"" + folderPath + "\\cmd.exe\" /c \"\"" + filename + "\" " + arguments + "\"") : ("\"" + folderPath + "\\cmd.exe\" /c \"" + filename + "\"")); filename = folderPath + "\\cmd.exe"; } flag = CreateProcessWithTokenW(phNewToken, 0u, filename, arguments, 0u, IntPtr.Zero, Path.GetDirectoryName(filename), ref lpStartupInfo, out PROCESS_INFORMATION lpProcessInformation); if (flag) { if (waitForExit) { if (WaitForSingleObject(lpProcessInformation.hProcess, uint.MaxValue) == uint.MaxValue) { num = Marshal.GetLastWin32Error(); str = "WaitForSingleObject() failed with the error code " + num + "."; } else if (!GetExitCodeProcess(lpProcessInformation.hProcess, out lpExitCode)) { num = Marshal.GetLastWin32Error(); str = "GetExitCodeProcess() failed with the error code " + num + "."; } } CloseHandle(lpProcessInformation.hProcess); CloseHandle(lpProcessInformation.hThread); } else if (!fallback) { num = Marshal.GetLastWin32Error(); str = "CreateProcessWithTokenW() failed with the error code " + num + "."; } CloseHandle(phNewToken); } else if (!fallback) { num = Marshal.GetLastWin32Error(); str = "DuplicateTokenEx() on the desktop shell process failed with the error code " + num + "."; } CloseHandle(TokenHandle); } else if (!fallback) { num = Marshal.GetLastWin32Error(); str = "OpenProcessToken() on the desktop shell process failed with the error code " + num + "."; } CloseHandle(intPtr); } else if (!fallback) { num = Marshal.GetLastWin32Error(); str = "OpenProcess() on the desktop shell process failed with the error code " + num + "."; } } else if (!fallback) { str = "Unable to get the window thread process ID of the desktop shell process."; } } if (!flag) { if (!fallback) { throw new ExternalException("Failed to start as a limited process. " + str, num); } Process.Start(filename, arguments); } return(lpExitCode); }