I have a program that needs to run as a normal user most of the time, but once in a while I need to stop and start a service. How do I go about making a program that runs as a normal user most of the time but elevates into administrator mode for some function?
You can't elevate a process once its running but you could either :-
Restart the process as elevated
private void elevateCurrentProcess()
{
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.UseShellExecute = true;
startInfo.WorkingDirectory = Environment.CurrentDirectory;
startInfo.FileName = Application.ExecutablePath;
startInfo.Verb = "runas";
try
{
Process p = Process.Start(startInfo);
}
catch
{
// User didn't allow UAC
return;
}
Application.Exit();
}
This method means that your process continues to run elevated and no more UAC promopts - both a good and a bad thing, depends upon your audience.
Put the code that requires elevation into a seperate exe
Set the manifest as requireAdministrator and start it as a separate process. See this sample code
This method means a UAC prompt every time you run the operation.
Best method depends upon your audience (admin types or not) and frequency of the elevated operation.
As far as I know, you need to start a seperate process that runs as the administrator. You can't elevate a process once it's already been started.
See this question.
You need to use what is referred to as Impersonation..
[http://support.microsoft.com/kb/306158][1]
The above shows how it would be accomplished for an ASP.Net app, but the code is probably near identical for your needs.
Related
I'm trying to kill some processes by their names (specific names that I already know) In C#. I find them and kill them with Process.Kill(), but sometimes on some processes i get 'access denied'. I assume it is because I'm not running them as an admin.
I created a batch that does the same, and if I run it as an admin it kills them all, otherwise not.
I can run the batch as an admin via the c# code, i.e:
var psi = new ProcessStartInfo();
psi.Verb = "runas"; //This suppose to run the command as administrator
//Then run a process with this psi
My question is, is this really a way to solve the access problem? Is there a better way? If I run my C# code as an admin, does Process.Kill() suppose to have the same result as doing it with the batch file?
What you are talking about are Elevated rights.
You need the programm that finds the programms and sends out the kills to always run Elevated. The most reliable way to do that, is to add this requirement to the Programm Manifest. It is something the UAC will read to help you.
The second most reliable way is to check if you got the rights. And if not, have the programm try to (re)start itself elevated. I did write some sample code for this a while back:
using System;
using System.Diagnostics;
using System.IO;
namespace RunAsAdmin
{
class Program
{
static void Main(string[] args)
{
/*Note: Running a batch file (.bat) or similar script file as admin
Requires starting the interpreter as admin and handing it the file as Parameter
See documentation of Interpreting Programm for details */
//Just getting the Absolute Path for Notepad
string windir = Environment.GetFolderPath(Environment.SpecialFolder.Windows);
string FullPath = Path.Combine(windir, #"system32\notepad.exe");
//The real work part
//This is the programm to run
ProcessStartInfo startInfo = new ProcessStartInfo(FullPath);
//This tells it should run Elevated
startInfo.Verb = "runas";
//And that gives the order
//From here on it should be 100% identical to the Run Dialog (Windows+R), except for the part with the Elevation
System.Diagnostics.Process.Start(startInfo);
}
}
}
Note that regardless how you try to elevate, Eleveation can fail in very rare OS settings.
I am trying to print all running processes in C#, similarly to how tasklist or ps does. I tried it with the following code:
foreach(Process process in Process.GetProcesses()) {
if(!process.HasExited) {
Console.WriteLine(process.ProcessName);
}
}
But with it, I run into some issues on Windows (did not try it on Linux):
When I execute the above code with user-privileges, it only prints processes started by this user. If I run it as administrator, all processes are printed.
When I open a cmd window with user privileges and list the processes (i.e. execute tasklist), all processes are printed, even those started by the system / by an administrator account.
Is there a way to get all processes, even those started by a higher privileged user, without requiring the program to be run as administrator?
One solution came to my mind:
System.Diagnostics.Process.Start("CMD.exe", "tasklist > C:\file.txt");
String[] processes = File.ReadAllLines(#"C:\file.txt");
But this is really hacky and does not work on linux.
You can't. You can't let the same process run parts in elevated and in user mode. That's just the way they built it, mainly for security reasons. You can't just bypass it.
Enough about what you can't. What can you do? You could start a second (different) program that runs in elevated mode, or you could restart your current application when there is a section you need elevated privileges. You need Process.Start for this and you will have to set the ProcessStartInfo.Verb to "runas":
ProcessStartInfo startInfo = new ProcessStartInfo(exePath);
startInfo.Verb = "runas";
Process.Start(startInfo);
After some testing I found out, that administrator processes will show in the Process.GetProcesses() list but the boolean HasExited of admin privileged processes is always true (even if it hasn't closed)
So I just looped through the list without checking for process.HasExited
foreach(Process process in Process.GetProcesses()) {
if(!process.HasExited) { //running process with user privileges
//do some stuff here
}
else { //closed process or administrator process
//do some stuff here
}
}
And for the case that the process has actually stopped, I wrap it inside a try-catch block.
As this solution does not seem optimal to me, I am leaving this question open.
I trying to make a program which connect to a game ("quick connect").
When i start the application as an administrator and connect to the game, it have weird issues (e.g. shortkeys doesn't respond for the ingame keys and the game doesn't load some model files), but when i start without admin rights it works fine, however i need the admin rights for other purposes (file operations).
I tried to rerun the program with the runas commandline command, the app had no admin rights, but the game still doesn't work right.
// OnClick event
ProcessStartInfo proc = new ProcessStartInfo("cmd.exe", "/c runas /trustlevel:0x20000 \"myapp.exe rerun\"");
proc.WorkingDirectory = Application.StartupPath;
proc.CreateNoWindow = true;
proc.UseShellExecute = false;
Process.Start(proc);
// Program.cs
if(!admin && args[0].Equals("rerun"))
{
ProcessStartInfo proc = new ProcessStartInfo("cmd.exe", "/c game.exe ipAndPortToConnect");
proc.WorkingDirectory = Application.StartupPath;
proc.CreateNoWindow = true;
proc.UseShellExecute = false;
Process.Start(proc);
}
I tried many combinations but none of them worked.
I can't figure out what the hell causing it...
If you check the Task Manager, you'll see the game still runs as a different user, namely Administrator. This most likely means the game saves some information in the user profile, for example under AppSettings (like hotkeys in configuration files), or game modifications that are stored in AppData, which could cause the issues you mentioned.
The user who 'reruns' your program is still the elevated user, albeit with less permissions, and that user launches the game. You cannot "unimpersonate" a process properly to the point the OS thinks it runs as the logged in user, so you'll have to do something else.
You can either see if you can move the user-specific resources to a path relative to the game, not the user, or start the process by any other means but directly from yours, for example using a Scheduled Task.
But why didn't you update your existing question?
I need to run "manage-bde" shell command from C# code.
The main application process is already running as administrator and is Elevated.
I used code from : UAC self-elevation example on MS website for confirming the app process is elevated.
(http://code.msdn.microsoft.com/windowsdesktop/CSUACSelfElevation-644673d3)
However, when I try to run manage-bde from the C# code, I get "System can't find file specified".
Process p = new Process();
p.StartInfo.FileName = "C:\\Windows\\System32\\manage-bde.exe";
p.StartInfo.UseShellExecute = true;
p.Start();
As a workaround, I tried to create a batch file that runs the command.
string batchFileName = DateTime.Now.Ticks + ".bat";
StreamWriter writer = new StreamWriter(batchFileName);
writer.WriteLine("manage-bde");
writer.Flush();
writer.Close();
Process p = new Process();
p.StartInfo.FileName = batchFileName;
p.StartInfo.UseShellExecute = true;
p.Start();
The batch file is written , and executed successfully; However, the command "manage-bde" is not recognized.
I changed the code to use the verb "runas" and use admin password and that works, but I want the batch file to work without the need for providing the admin password. The current logged in user is already administrator on the computer but the batch file is not getting executed with the existing admin privileges . I need the batch file to execute and manage-bde to run successfully.
Your help or advice will be very highly appreciated :)
ps: some commands other than manage-bde work fine without need for admin runas.
The reason of the behavior I encountered was the Windows File System Redirector.
In most cases, whenever a 32-bit application attempts to access %windir%\System32, the access is redirected to %windir%\SysWOW64
https://msdn.microsoft.com/en-us/library/windows/desktop/aa384187%28v=vs.85%29.aspx
My application build was 32 bits. Whenever it tried to access System32 windows automatically redirected it to SysWow64 which does not contain "manage-bde.exe". I changed the build to 64 bits and then the application could access manage-bde.exe from System32
Even if you're running as the Administrator user, you're not fully elevated if UAC is running. Meaning that you'll have either the UAC prompt come up or you'll be prompted for a password.
The only real way you could get around that is to run your application elevated first, or to write a service that runs with elevated permissions to start your new process.
The alternative of course is to disable UAC, but that is undesirable in most situations.
I have a winforms app that installs other apps in a loop. This works properly on an administrator account in Windows 7, but I have serious issues in a standard account - the app requires elevation in order to write to "Program Files(x86)" folder.
Therefore I am trying to ask for elevation for a specific method (the one that runs the installers) in a winforms c# app, using this code:
[System.Security.Permissions.PrincipalPermission(System.Security.Permissions.SecurityAction.Demand, Role = #"BUILTIN\Administrators")]
After receiving an error, I learned from the web that before calling the method which carries the above attribute, I need to write this:
AppDomain.CurrentDomain.SetPrincipalPolicy(PrincipalPolicy.WindowsPrincipal);
I did this, and the method still throws the following error:
Request for principal permission failed.
Step by step debugging passes the SetPrincipalPolicy line but, when it reaches the method with the Demand atribute, it just throws the same error, as if the SetPrincipalPolicy never existed.
Am I doing something wrong in setting the Demand attribute properly?
Thank you in advance.
LATER EDIT: as requested here is the code that is supposed to trigger the elevation request when installing the app silently (but does not work):
WindowsPrincipal principal = new WindowsPrincipal(WindowsIdentity.GetCurrent());
bool hasAdministrativeRight = principal.IsInRole(WindowsBuiltInRole.Administrator);
if (!hasAdministrativeRight)
{
ProcessStartInfo psi = new ProcessStartInfo(file);
psi.WindowStyle = ProcessWindowStyle.Hidden;
psi.UseShellExecute = true;
psi.Verb = "runas";
//psi.CreateNoWindow = true;
psi.Arguments = modifiers;
try
{
using (Process process = Process.Start(psi))
{
process.WaitForExit();
if (process.HasExited)
return process.ExitCode;
}
}
catch (Win32Exception wex)
{
}
}
What I need, is for that process to pop a dialog asking for username and password for admin, if the app was ran under a Windows Standard User. Only the process started programmatically above should run as admin, the main app itself can remain as a standard user.
This is just not the way UAC works. It is process based, the user only ever gets the "please let me mess with your machine" prompt when you start a new process. With the proper incantation of "I need the user's consent to mess with the machine, please say Yes" signal embedded in the program. Which you do by this answer.
Death to the idea of making it method based. Unreasonable to a programmer, makes sense to a user. User wins.
You can either force your app to always run as an admin. This is how you do that. It is not recommended however for your app to need admin privileges to run.
If you start a Process to run the installer, you can check here how to run the process as an admin.
A third option which Visual Studio uses is that when you do something where you need admin privileges you are prompted to restart the app and it then restarts the app as an admin and you can perform the tasks. Just use the code from the second way to start your app.
The method you've posted to run as admin will check if the user is admin and then start the process as an admin. If the user doesn't have admin rights the app won't even start. A better solution is to always try to run the process as an admin. Then the user will get an UAC prompt with password and username, which an admin can fill in.
public static int RunAsAdmin(string fileName)
{
ProcessStartInfo psi = new ProcessStartInfo(fileName);
psi.WindowStyle = ProcessWindowStyle.Hidden;
psi.UseShellExecute = true;
psi.Verb = "runas";
psi.Arguments = modifiers;
try
{
using (Process process = Process.Start(psi))
{
process.WaitForExit();
if (process.HasExited)
return process.ExitCode;
}
}
catch (Win32Exception wex)
{
}
return 0;
}