public static void RunAsDesktopUser(string fileName, string args) { if (string.IsNullOrWhiteSpace(fileName)) { throw new ArgumentException("Value cannot be null or whitespace.", nameof(fileName)); } // To start process as shell user you will need to carry out these steps: // 1. Enable the SeIncreaseQuotaPrivilege in your current token // 2. Get an HWND representing the desktop shell (GetShellWindow) // 3. Get the Process ID(PID) of the process associated with that window(GetWindowThreadProcessId) // 4. Open that process(OpenProcess) // 5. Get the access token from that process (OpenProcessToken) // 6. Make a primary token with that token(DuplicateTokenEx) // 7. Start the new process with that primary token(CreateProcessWithTokenW) var hProcessToken = IntPtr.Zero; // Enable SeIncreaseQuotaPrivilege in this process. (This won't work if current process is not elevated.) try { var process = Unmanaged.GetCurrentProcess(); if (!Unmanaged.OpenProcessToken(process, 0x0020, ref hProcessToken)) { return; } var tkp = new TOKEN_PRIVILEGES { PrivilegeCount = 1, Privileges = new LUID_AND_ATTRIBUTES[1] }; if (!Unmanaged.LookupPrivilegeValue(null, "SeIncreaseQuotaPrivilege", ref tkp.Privileges[0].Luid)) { return; } tkp.Privileges[0].Attributes = 0x00000002; if (!Unmanaged.AdjustTokenPrivileges(hProcessToken, false, ref tkp, 0, IntPtr.Zero, IntPtr.Zero)) { return; } } finally { Unmanaged.CloseHandle(hProcessToken); } // Get an HWND representing the desktop shell. // CAVEATS: This will fail if the shell is not running (crashed or terminated), or the default shell has been // replaced with a custom shell. This also won't return what you probably want if Explorer has been terminated and // restarted elevated. var hwnd = Unmanaged.GetShellWindow(); if (hwnd == IntPtr.Zero) { return; } var hShellProcess = IntPtr.Zero; var hShellProcessToken = IntPtr.Zero; var hPrimaryToken = IntPtr.Zero; try { // Get the PID of the desktop shell process. uint dwPID; if (Unmanaged.GetWindowThreadProcessId(hwnd, out dwPID) == 0) { return; } // Open the desktop shell process in order to query it (get the token) hShellProcess = Unmanaged.OpenProcess(ProcessAccessFlags.QueryInformation, false, dwPID); if (hShellProcess == IntPtr.Zero) { return; } // Get the process token of the desktop shell. if (!Unmanaged.OpenProcessToken(hShellProcess, 0x0002, ref hShellProcessToken)) { return; } var dwTokenRights = 395U; // Duplicate the shell's process token to get a primary token. // Based on experimentation, this is the minimal set of rights required for CreateProcessWithTokenW (contrary to current documentation). if (!Unmanaged.DuplicateTokenEx(hShellProcessToken, dwTokenRights, IntPtr.Zero, SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation, TOKEN_TYPE.TokenPrimary, out hPrimaryToken)) { return; } // Start the target process with the new token. var si = new STARTUPINFO(); var pi = new PROCESS_INFORMATION(); if (!Unmanaged.CreateProcessWithTokenW(hPrimaryToken, 0, fileName, "\"" + fileName + "\" \"" + args + "\"", 0, IntPtr.Zero, Path.GetDirectoryName(fileName), ref si, out pi)) { return; } } finally { Unmanaged.CloseHandle(hShellProcessToken); Unmanaged.CloseHandle(hPrimaryToken); Unmanaged.CloseHandle(hShellProcess); } }