This question already has answers here:
Elevating process privilege programmatically?
(6 answers)
Closed 6 years ago.
I have a Visual Studio Windows app project. I've added code to download an installer update file. The installer after it has finished downloading would need administrator privileges to run. I have added a manifest file.
When user clicks on the DownloadUpdate.exe, UAC prompts the user for Admin permissions. So I assumed that all processes created and called within DownloadUpdate.exe will run in admin capacity. So I made the setup call my downloaded file with the following code:
Process p = new Process();
p.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
p.StartInfo.FileName = strFile;
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.RedirectStandardError = true;
Try this:
//Vista or higher check
if (System.Environment.OSVersion.Version.Major >= 6)
{
p.StartInfo.Verb = "runas";
}
Alternatively, go the manifest route for your application.
First of all you need to include in your project
using System.Diagnostics;
After that you could write a general method that you could use for different .exe files that you want to use. It would be like below:
public void ExecuteAsAdmin(string fileName)
{
Process proc = new Process();
proc.StartInfo.FileName = fileName;
proc.StartInfo.UseShellExecute = true;
proc.StartInfo.Verb = "runas";
proc.Start();
}
If you want to for example execute notepad.exe then all you do is you call this method:
ExecuteAsAdmin("notepad.exe");
This is a clear answer to your question:
How do I force my .NET application to run as administrator?
Summary:
Right Click on project -> Add new item -> Application Manifest File
Then in that file change a line like this:
<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
Compile and run!
var pass = new SecureString();
pass.AppendChar('s');
pass.AppendChar('e');
pass.AppendChar('c');
pass.AppendChar('r');
pass.AppendChar('e');
pass.AppendChar('t');
Process.Start("notepad", "admin", pass, "");
Works also with ProcessStartInfo:
var psi = new ProcessStartInfo
{
FileName = "notepad",
UserName = "admin",
Domain = "",
Password = pass,
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true
};
Process.Start(psi);
This works when I try it. I double-checked with two sample programs:
using System;
using System.Diagnostics;
namespace ConsoleApplication1 {
class Program {
static void Main(string[] args) {
Process.Start("ConsoleApplication2.exe");
}
}
}
using System;
using System.IO;
namespace ConsoleApplication2 {
class Program {
static void Main(string[] args) {
try {
File.WriteAllText(#"c:\program files\test.txt", "hello world");
}
catch (Exception ex) {
Console.WriteLine(ex.ToString());
Console.ReadLine();
}
}
}
}
First verified that I get the UAC bomb:
System.UnauthorizedAccessException:
Access to the path 'c:\program
files\test.txt' is denied. //
etc..
Then added a manifest to ConsoleApplication1 with the phrase:
<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
No bomb. And a file I can't easily delete :) This is consistent with several previous tests on various machines running Vista and Win7. The started program inherits the security token from the starter program. If the starter has acquired admin privileges, the started program has them as well.
Here is an example of run process as administrator without Windows Prompt
Process p = new Process();
p.StartInfo.FileName = Server.MapPath("process.exe");
p.StartInfo.Arguments = "";
p.StartInfo.UseShellExecute = false;
p.StartInfo.CreateNoWindow = true;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.Verb = "runas";
p.Start();
p.WaitForExit();
Process proc = new Process();
ProcessStartInfo info =
new ProcessStartInfo("Your Process name".exe, "Arguments");
info.WindowStyle = ProcessWindowStyle.Hidden;
info.UseShellExecute =true;
info.Verb ="runas";
proc.StartInfo = info;
proc.Start();
Use this method:
public static int RunProcessAsAdmin(string exeName, string parameters)
{
try {
System.Diagnostics.ProcessStartInfo startInfo = new System.Diagnostics.ProcessStartInfo();
startInfo.UseShellExecute = true;
startInfo.WorkingDirectory = CurrentDirectory;
startInfo.FileName = Path.Combine(CurrentDirectory, exeName);
startInfo.Verb = "runas";
//MLHIDE
startInfo.Arguments = parameters;
startInfo.ErrorDialog = true;
Process process = System.Diagnostics.Process.Start(startInfo);
process.WaitForExit();
return process.ExitCode;
} catch (Win32Exception ex) {
WriteLog(ex);
switch (ex.NativeErrorCode) {
case 1223:
return ex.NativeErrorCode;
default:
return ErrorReturnInteger;
}
} catch (Exception ex) {
WriteLog(ex);
return ErrorReturnInteger;
}
}
Hope it helps.
You probably need to set your application as an x64 app.
The IIS Snap In only works in 64 bit and doesn't work in 32 bit, and a process spawned from a 32 bit app seems to work to be a 32 bit process and the same goes for 64 bit apps.
Look at: Start process as 64 bit
Related
To simplify, i WAS making a fake os (an os which is actually an app) and i wanted to make a find file dialog that will find a .cs file and try to load it as a form at runtime
void epicFunction(string file)
{
if (file.EndsWith(".cs"))
{
//Load the form as if it was already compiled in
}
}
if you know .exe folderis very simple.
// Use ProcessStartInfo class
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.CreateNoWindow = false;
startInfo.UseShellExecute = false;
startInfo.FileName = "Test.exe";
startInfo.WindowStyle = ProcessWindowStyle.Hidden;
try
{
// Start the process with the info we specified.
// Call WaitForExit and then the using statement will close.
using (Process exeProcess = Process.Start(startInfo))
{
exeProcess.WaitForExit();
}
}
catch
{
// Log error.
}
.csproj is a VS file of your solution...
I have two simple Console APPs. Program1 calls Program2 to do simple moving of 500 files to another folder.
Program1.exe:
static void Main(string[] args)
{
System.Diagnostics.Process proc = new System.Diagnostics.Process();
proc.StartInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
proc.EnableRaisingEvents = false;
proc.StartInfo.RedirectStandardOutput = true;
proc.StartInfo.RedirectStandardInput = true;
proc.StartInfo.UseShellExecute = false;
proc.StartInfo.FileName = #"D:\Program2.exe";
proc.Start();
proc.WaitForExit();
proc.Close();
}
Program2.exe:
static void Main(string[] args)
{
string srcDic= #"D:\docs";
var DesDic= #"D:\docs2";
var pdf_files = Directory.GetFiles(srcDic, "*.pdf", SearchOption.TopDirectoryOnly);
foreach (var pdf_file in pdf_files)
{
var fileName = new DirectoryInfo(pdf_file).Name;
File.Move(pdf_file, DesDic + "\\" + fileName);
Console.WriteLine("Moving " + pdf_file);
}
Console.WriteLine("----- All files moved!");
}
Issue: When I run Program1.exe to run Program2, most of the time Program2 will be frozen and doesn't move all the files. I have to close it manually.
Note:
When I run Program2.exe without Program1, it works great and moves all the files.
I don't have Anti-Virus, and I even turned off the Windows Defender Firewall
This code is part of a big software, and I don't want to write the move code directly in Program1
Have You tried lowering the amount of files on a Test run for APP 2 ? Lower the amount of files in the Docs directory to 20 and see if it still freezes. Might also be timing issues.
Paste a Thread.Sleep(); after this step just to see, I wouldn't use this in the final product.
proc.StartInfo.UseShellExecute = false;
From an application that is not being run as administrator, I have the following code:
ProcessStartInfo proc = new ProcessStartInfo();
proc.WindowStyle = ProcessWindowStyle.Normal;
proc.FileName = myExePath;
proc.CreateNoWindow = false;
proc.UseShellExecute = false;
proc.Verb = "runas";
When I call Process.Start(proc), I do not get a pop up asking for permission to run as administrator, and the exe is not run as administrator.
I tried adding an app.manifest to the executable found at myExePath, and updated the requestedExecutionLevel to
<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
With the updated app.manifest, on the Process.Start(proc) call, I get an exception, "The requested operation requires elevation."
Why isn't the .Verb action not setting administrator privileges?
I am testing on Windows Server 2008 R2 Standard.
You must use ShellExecute. ShellExecute is the only API that knows how to launch Consent.exe in order to elevate.
Sample (.NET) Source Code
In C#, the way you call ShellExecute is to use Process.Start along with UseShellExecute = true:
private void button1_Click(object sender, EventArgs e)
{
//Public domain; no attribution required.
ProcessStartInfo info = new ProcessStartInfo(#"C:\Windows\Notepad.exe");
info.UseShellExecute = true;
info.Verb = "runas";
Process.Start(info);
}
If you want to be a good developer, you can catch when the user clicked No:
private void button1_Click(object sender, EventArgs e)
{
//Public domain; no attribution required.
const int ERROR_CANCELLED = 1223; //The operation was canceled by the user.
ProcessStartInfo info = new ProcessStartInfo(#"C:\Windows\Notepad.exe");
info.UseShellExecute = true;
info.Verb = "runas";
try
{
Process.Start(info);
}
catch (Win32Exception ex)
{
if (ex.NativeErrorCode == ERROR_CANCELLED)
MessageBox.Show("Why you no select Yes?");
else
throw;
}
}
Bonus Watching
UAC - What. How. Why.. The architecture of UAC, explaining that CreateProcess cannot do elevation, only create a process. ShellExecute is the one who knows how to launch Consent.exe, and Consent.exe is the one who checks group policy options.
When i try to run BCDEDIT from my C# application i get the following error:
'bcdedit' is not recognized as an internal or external
command,
operable program or batch file.
when i run it via elevated command line i get as expected.
i have used the following code:
Process p = new Process();
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.RedirectStandardError = true;
p.StartInfo.FileName = #"CMD.EXE";
p.StartInfo.Arguments = #"/C bcdedit";
p.Start();
string output = p.StandardOutput.ReadToEnd();
String error = p.StandardError.ReadToEnd();
p.WaitForExit();
return output;
i have also tried using
p.StartInfo.FileName = #"BCDEDIT.EXE";
p.StartInfo.Arguments = #"";
i have tried the following:
Checking path variables - they are fine.
running visual studio from elevated command prompt.
placing full path.
i am running out of ideas,
any idea as to why i am getting this error ?
all i need is the output of the command if there is another way that would work as well.
thanks
There is one explanation that makes sense:
You are executing the program on a 64 bit machine.
Your C# program is built as x86.
The bcdedit.exe file exists in C:\Windows\System32.
Although C:\Windows\System32 is on your system path, in an x86 process you are subject to the File System Redirector. Which means that C:\Windows\System32 actually resolves to C:\Windows\SysWOW64.
There is no 32 bit version of bcdedit.exe in C:\Windows\SysWOW64.
The solution is to change your C# program to target AnyCPU or x64.
If you are stuck with x86 application on both 32it/64bit Windows and You need to call bcdedit command, here is a way how to do that:
private static int ExecuteBcdEdit(string arguments, out IList<string> output)
{
var cmdFullFileName = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Windows),
Environment.Is64BitOperatingSystem && !Environment.Is64BitProcess
? #"Sysnative\cmd.exe"
: #"System32\cmd.exe");
ProcessStartInfo psi = new ProcessStartInfo(cmdFullFileName, "/c bcdedit " + arguments) { UseShellExecute = false, RedirectStandardOutput = true };
var process = new Process { StartInfo = psi };
process.Start();
StreamReader outputReader = process.StandardOutput;
process.WaitForExit();
output = outputReader.ReadToEnd().Split(new[] { Environment.NewLine }, StringSplitOptions.None).ToList();
return process.ExitCode;
}
usage:
var returnCode = ExecuteBcdEdit("/set IgnoreAllFailures", out outputForInvestigation);
Inspiration was from this thread and from How to start a 64-bit process from a 32-bit process and from http://www.samlogic.net/articles/sysnative-folder-64-bit-windows.htm
I want PDF file print into network print
Following code where used, it work fine in local host (development area) but not worked in IIS serve host
Can given to any rights issue? How to solve the issue ?
private void SendToPrinter()
{
ProcessStartInfo info = new ProcessStartInfo();
info.Verb = "print";
info.FileName = #"c:\output.pdf";
info.CreateNoWindow = true;
info.WindowStyle = ProcessWindowStyle.Hidden;
Process p = new Process();
p.StartInfo = info;
p.Start();
p.WaitForInputIdle();
System.Threading.Thread.Sleep(3000);
if (false == p.CloseMainWindow())
p.Kill();
}
It might be an authorization/security problem.
IIS (the server) runs in a context which doesn't have access to the shared printer.
Your local host has it cause it's running in your user's context.