16

I want to allow users to run a command line utility as administrator from within my non-admin program and for my program to get the output. The utility is third-party but is distributed with my programme.

I can redirect the output of a program and I can run a program as administrator but I can't do both at the same time.

The only thing that I can get to work at the moment is using cmd.exe to redirect the output to a file, e.g.:

using System.Windows.Forms;
using System.Diagnostics;
using System.IO;
using System.Reflection;

string appDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
string utilityPath = Path.Combine(appDirectory, "tools", "utility.exe");
string tempFile = Path.GetTempFileName();

Process p = new Process();
// hide the command window
p.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
p.StartInfo.FileName = "cmd.exe";
// run the tool, redirect the output to the temp file and then close.
p.StartInfo.Arguments = " /C \"\"" + utilityPath + "\" > \"" + tempFile + "\"\"";
p.StartInfo.Verb = "runas"; // run as administrator
p.Start();
p.WaitForExit();

// get the output, delete the file and show the output to the user
string output = File.ReadAllText(tempFile);
File.Delete(tempFile);
MessageBox.Show(output);

This has two problems: 1) it uses a temporary file and 2) the UAC is for cmd.exe rather then utility.exe. There must surely be a better way to do this?

Community
  • 1
  • 1
ChrisD
  • 3,378
  • 3
  • 35
  • 40
  • If all you're interested in is how to catch the output of another process, then this is a duplicate question; see e.g. [How to capture the standard output/error of a Process.Start()?](http://stackoverflow.com/questions/3633653/how-to-capture-the-standard-output-error-of-a-process-start?rq=1). – stakx - no longer contributing Apr 01 '13 at 16:30
  • I think what I failed to make clear was the privileged difference. My application runs as standard user and the utility runs as administrator. As far as I can tell the normal way to redirect output doesn't work in this situation. – ChrisD Apr 01 '13 at 17:10

2 Answers2

4

Instead of executing through a new cmd, try executing the utility directly. And instead of redirecting to a file, redirect the standard output to read it from your program. In order to run as admin, you'll need to use the admin username and password (taken from here). You'll need to set your method as unsafe:

unsafe public static void Main(string[] args){
    Process p = new Process();
    p.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
    // set admin user and password
    p.StartInfo.UserName = "adminusername";
    char[] chArray = "adminpassword".ToCharArray();
    System.Security.SecureString str;
    fixed (char* chRef = chArray) {
        str = new System.Security.SecureString(chRef, chArray.Length);
    }
    p.StartInfo.Password = str;
    // run and redirect as usual
    p.StartInfo.FileName = utilityPath;
    p.StartInfo.UseShellExecute = false;
    p.StartInfo.RedirectStandardOutput = true;
    p.Start();
    string output = p.StandardOutput.ReadToEnd();
    Console.WriteLine(output);
    p.WaitForExit();
}
Community
  • 1
  • 1
Julián Urbano
  • 8,378
  • 1
  • 30
  • 52
  • 2
    You should use `using(Process p = new Process())` or call `p.Dispose()` after `p.WaitForExit()`. – Dour High Arch Apr 01 '13 at 16:40
  • I get the error "The Process object must have the UseShellExecute property set to false in order to redirect IO streams". So I add "p.StartInfo.UseShellExecute = false;" but then that stops it running as administrator: "The requested operation requires elevation". – ChrisD Apr 01 '13 at 17:08
  • 1
    So I'd have to ask the user for the admin username and password instead of using the UAC? And they'd have to have activated the admin account and a set up a password? – ChrisD Apr 01 '13 at 18:20
  • If there is a better way I'm afraid I don't know – Julián Urbano Apr 01 '13 at 18:44
  • I've accepted this answer because it does answer the question but on balance I think I'll stick with my original idea. Asking for an admin account is not really going to be practical. – ChrisD Apr 01 '13 at 20:31
  • Yes, I agree. Thank you anyway – Julián Urbano Apr 01 '13 at 20:32
0

This does the magic, although I haven't tested it.

It's written in C++, but a wrapper API can easily be created to be called from C# by using DllImport.

Masood Khaari
  • 2,911
  • 2
  • 23
  • 40