I am using the following code within a program that does not have elevated privileges
ProcessStartInfo pInfo = new ProcessStartInfo();
pInfo.FileName = fileToExcecute;
pInfo.UseShellExecute = false;
pInfo.RedirectStandardOutput = false;
pInfo.RedirectStandardError = false;
pInfo.CreateNoWindow = true;
if (runAsAdministrator)
pInfo.Verb = "runas";
Process p = Process.Start(pInfo);
The end user is asked to select whether they wish to run the program in elevated mode or not. The above however is not starting the program 'As Administrator' when runAsAdministrator is true. I have ran the 'fileToExcute' manually 'As Administrator' and it prompts to make changes to the computer.
I then added a manifest to the 'fileToExecute' to run with elevated privileges every time and when running that program directly I am properly prompted to confirm permission to make changes to the computer. When I run the above program that uses the above code, I get:
System.ComponentModel.Win32Exception (0x80004005):
The requested operation requires elevation at System.Diagnostics.Process.StartWithCreateProcess(ProcessStartInfo startInfo)
at System.Diagnostics.Process.Start() at System.Diagnostics.Process.Start(ProcessStartInfo startInfo) at
#HLg.#ic.#zNg(String #ANg) in #pOg:line 135
I really want the first option to work. I've tried everything and can't work out why the first option is not working.
You can't combine the Verb property with UseShellExecute = false, since verbs rely on that functionality. Set UseShellExecute to true and it should work.
I changed my code and did not use the pInfo configuration options. I just changed everything to p.verb = "runas" etc and it is now working.
Related
I know you can create a manifest file to specify access level administrator for the whole application. But is it possible to only require it for a specific form?
No, this is not possible.
What you can do: Let your process run without elevation.
When you discover that elevation is required but your process does not run elevated, restart the process (Process.Start) with the "runas" verb and maybe some command line option that you can evaluate in the newly started process to immediately open the form.
if (!RunningElevated())
{
// restart as elevated process
ProcessStartInfo psi = new ProcessStartInfo
{
UseShellExecute = true,
Verb = "runas",
WorkingDirectory = Environment.CurrentDirectory,
FileName = Assembly.GetExecutingAssembly().Location,
Argument = "--open MyForm" // has to be evaluated on the startup code
};
var process = Process.Start(psi);
if (process != null)
Application.Current.Shutdown(0); // this is for WPF
}
When i try to run this function it keep crashing when the process starts.
public static void MapDestinationToSource (string destination, string source)
{
System.Diagnostics.Process proc = new System.Diagnostics.Process ();
// set the file to execute
proc.StartInfo.FileName = "mklink";
proc.StartInfo.Arguments = $"/D \"{source}\" \"{destination}\"";
// Redirect the output stream of the child process.
proc.StartInfo.UseShellExecute = true;
//proc.StartInfo.RedirectStandardOutput = true;
// start the process
proc.Start ();
// Do not wait for the child process to exit before
// reading to the end of its redirected stream.
// p.WaitForExit();
// Read the output stream first and then wait.
string output = proc.StandardOutput.ReadToEnd ();
proc.WaitForExit ();
}
Exception:
System.ComponentModel.Win32Exception occurred
HResult=0x80004005
Message=The system cannot find the file specified
Source=System
StackTrace:
at System.Diagnostics.Process.StartWithShellExecuteEx(ProcessStartInfo startInfo)
at System.Diagnostics.Process.Start()
at GameCloud.StorageLibrary.MapDestinationToSource(String destination, String source) in D:\Development\code\sample\server\ClientAgent\ClientAgent\StorageLibrary.cs:line 130
at GameCloud.Program.Main(String[] args) in D:\Development\code\sample\server\ClientAgent\ClientAgent\Program.cs:line 135
When i execute the command on command line, it works. But it does not when in the code. I already set the security policies to allow the current user to execute the mklink command without elevated access.
If you are trying to execute executable programs (bob.exe) check out my striked out answer below.
Since you are trying to run mklink which is built-in to cmd then you need to use:
proc.StartInfo.FileName = "cmd.exe";
proc.StartInfo.Arguments = $"/C mklink /D \"{source}\" \"{destination}\"";
The docs state:
When UseShellExecute is true, the WorkingDirectory property specifies
the location of the executable. If WorkingDirectory is an empty
string, it is assumed that the current directory contains the
executable.
and then:
When UseShellExecute is false, the FileName property can be either a
fully qualified path to the executable, or a simple executable name
that the system will attempt to find within folders specified by the
PATH environment variable.
As such, you need to either set UseShellExecute to false (so that your PATH is used to find the executable) or set WorkingDirectory to the folder that contains the executable.
I'm trying to run the Windows System Assessment Tool (winsat.exe) using the following code:
System.Diagnostics.Process WinSPro =
new System.Diagnostics.Process();
System.Diagnostics.ProcessStartInfo WinSSInfo =
new System.Diagnostics.ProcessStartInfo();
WinSSInfo.FileName = "cmd.exe";
WinSSInfo.Arguments = "/k winsat.exe";
WinSPro.StartInfo = WinSSInfo;
WinSPro.Start();
This code works if I only call cmd.exe, and even if I call regedit.exe it still works.
However, when I try to call winsat.exe as a argument of cmd.exe, it fails.
The command prompt shows this:
'winsat.exe' is not recognized as an internal or external command,
operable program or batch file.
I tried several ways to call winsat.exe:
Call it directly by assigning "winsat.exe" to ProcessStartInfo.FileName. It fails with a Win32Exception: The system cannot find the file specified
As above, using the full path - #"c:\windows\system32\winsat.exe". It fails with the same error.
Run the code as the System Administrator. It still fails.
Call winsat.exe as in the coded example. It failed as I explained earlier.
It's interesting that the command prompt launched from the code can only see .dll files in c:\windows\system32.
Does anyone have any idea why winsat.exe cannot be launched through System.Diagnostics.Process? Are there any limitations which I've misunderstood?
Thanks,
Rex
winsat.exe is redirected using Windows-on Windows 64-bit redirection. What's happening is that your launch request (from a 32-bit process) is being redirected to %windir%\SysWOW64\winsat.exe. Since there's no 32-bit version of this particular executable on 64-bit installs, the launch fails. To bypass this process and allow your 32-bit process to access the native (64-bit) path, you can reference %windir%\sysnative instead:
Process WinSPro = new Process();
ProcessStartInfo WinSSInfo = new ProcessStartInfo();
WinSSInfo.FileName = #"c:\windows\sysnative\winsat.exe";
WinSPro.StartInfo = WinSSInfo;
WinSPro.Start();
Alternatively, if you build your program as x64, you can leave the path as c:\windows\system32.
Note that it's best to use Environment.GetFolderPath to get the path to the windows directory, just in case the OS is installed in a non-standard location:
WinSSInfo.FileName = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.Windows),
#"sysnative\winsat.exe");
Based on Simon MᶜKenzie's answer, and the link he provided (thanks to soyuz for his comment) I wrote method that should work in either cases (to just copy/paste the code):
public static string GetSystem32DirectoryPath()
{
string winDir = Environment.GetFolderPath(Environment.SpecialFolder.Windows);
string system32Directory = Path.Combine(winDir, "system32");
if (Environment.Is64BitOperatingSystem && !Environment.Is64BitProcess)
{
// For 32-bit processes on 64-bit systems, %windir%\system32 folder
// can only be accessed by specifying %windir%\sysnative folder.
system32Directory = Path.Combine(winDir, "sysnative");
}
return system32Directory;
}
and code to launch the process:
var pi = new ProcessStartInfo
{
FileName = Path.Combine(GetSystem32DirectoryPath(), "winsat.exe"),
CreateNoWindow = true,
UseShellExecute = false
};
Process.Start(pi);
I've an exe which runs a process to open the DeviceManager. But unfortunately, it asks for a confirmation to provide 'yes' or 'No' which waits for user input for long time and does not continue with execution.
How to get rid of this? I do not want to provide a confirmation again as I do not want to pause the EXE run with this.
StartInfo.CreateWindow = false would not hold for this as it just for starting in another cmd window.
Code below:
Process p = new Process();
p.StartInfo.FileName = "devcon.exe";
p.StartInfo.CreateNoWindow = false;
p.Start();
The messagebox you are seeing is UAC (User Account Control) which was implemented since Vista.
To bypass the box you might be able to try providing the credentials programmatically before launching the process, I can't test but something like this:
var processInfo = new ProcessStartInfo
{
FileName = "devcon.exe",
UserName = "Administrator",
Domain = "yourdomain or leave blank",
Password = adminpassword,
UseShellExecute = false,
};
Process.Start(processInfo);
Otherwise the user will have to have admin rights, or the password!
The other option would be to disable UAC. However that wouldn't allow the user to do anything they couldn't do normally, it will probably tell you that you can't make any changes without the process running as admin.
The parent process should be run by administrator account.
Also show all code please.
I would like to mimic the Run command in Windows in my program. In other words, I would like to give the user the ability to "run" an arbitrary piece of text exactly as would happen if they typed it into the run box.
While System.Diagnostics.Process.Start() gets me close, I can't seem to get certain things like environment variables such as %AppData% working. I just keep getting the message "Windows cannot find '%AppData%'..."
You can use the Environment.ExpandEnvironmentVariables method to turn %AppData% into whatever it actually corresponds to.
Depending on what you're trying to do, you could also call CMD.EXE, which will expand your environment variables automatically. The example below will do a DIR of your %appdata% folder, and redirect the stdOut to the debug:
StreamReader stdOut;
Process proc1 = new Process();
ProcessStartInfo psi = new ProcessStartInfo("CMD.EXE", "/C dir %appdata%");
psi.RedirectStandardOutput = true;
psi.UseShellExecute = false;
proc1.StartInfo = psi;
proc1.Start();
stdOut = proc1.StandardOutput;
System.Diagnostics.Debug.Write(stdOut.ReadToEnd());