1

I have a Visual studio Application that runs a series of Cmd commands using the following function

public static void AdminEx(string command) //Runs an Administrative Windows Command
    {
        var proc = new System.Diagnostics.ProcessStartInfo();
        proc.UseShellExecute = true;
        proc.WorkingDirectory = @"C:\Windows\System32";
        proc.FileName = @"C:\Windows\System32\cmd.exe";
        proc.Verb = "runas";
        proc.Arguments = "/c " + command;
        proc.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
        var p = System.Diagnostics.Process.Start(proc);
        p.WaitForExit();
    }

We recently updated our Code and converted to Powershell. How would I go about changing this function to accommodate for the new code. Is this still the most efficient way to go???

mklement0
  • 382,024
  • 64
  • 607
  • 775

2 Answers2

2

Just change the line:

proc.FileName = @"C:\Windows\System32\cmd.exe";

To:

proc.FileName = @"C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe";

This will make your software open powershell.exe instead of cmd.exe, I can't tell if that is the best way to go about this but I tried here and it worked for me.

  • I wouldn't recommend this method, instead you should reference the System.Management.Automation namespace. – Persistent13 Feb 20 '19 at 19:28
  • @persistent13 what is the `System.Management.Automation namespace`??? Can you explain what it does and how i could use it? - Much Appreciated – securityghost Feb 20 '19 at 22:32
  • Please see the link that elgonzo posted in a comment under your question. System.Management.Automation is PowerShell for use in C#. Instead of having to call an external process you have full programmatic control over the PowerShell instance. – Persistent13 Feb 20 '19 at 23:08
  • @Persistent13: While recommending the PowerShell SDK (API) is good pointer in general, there's an added requirement here: the need to run _as admin_ (elevated), which, AFAIK, _invariably_ involves an _external process_, so using securityghost's _child-process_ approach via the Powershell _CLI_ (with `.UseShellExecute` set to `true` and `.Verb` set to `RunAs`) may be the best (only?) approach, after all. – mklement0 Feb 20 '19 at 23:26
0

Otávio Caldonazo's helpful answer provides an effective solution, but it's worth explaining a few things:

  • Unless there are security concerns (someone having placed a nonstandard, malicious cmd.exe / powershell.exe executable in a folder listed in the %PATH% / $env:PATH environment variable that preempts the standard executables), it is simpler to use the executable file name alone, i.e., just cmd.exe / powershell.exe - this also avoids problems with non-standard Windows installations whose root is not C:\Windows.

    • Also, given that you're specifying a full path via .FileName, the .WorkingDirectory property is effectively ignored with .Verb set to runas, because .WorkingDirectory then doesn't actually set the working directory, but specifies where to find the executable, if specified by name only - however, lookups in %PATH% / $env:PATH are still performed.
  • While the /c parameter to pass a command to the shell invoked happens to work with PowerShell's CLI too, PowerShell generally use sigil - to prefix parameter names; thus,
    -c (short for -Command) is the better choice.

    • Additionally, given that PowerShell loads its $PROFILE initialization file by default (even when invoked with -Command or -File, even though that is generally undesirable), it's best to use -NoProfile -Command ... instead.
  • Generally - if you don't need elevation (running as an admin) - as an alternative to the - slow - creation of a child process via powershell.exe, consider use of the PowerShell SDK (API), which enables faster in-process execution while also enabling more fine-grained capturing of output streams (which you don't seem to be interested in in your particular scenario; while System.Diagnostics.ProcessStartInfo allows you to capture stdout and stderr output via the .RedirectStandardOutput and .RedirectStandardError properties, note that PowerShell has additional output streams).

Based on the above, I suggest doing the following:

var proc = new System.Diagnostics.ProcessStartInfo();
proc.UseShellExecute = true;
proc.Verb = "runas";
proc.FileName = @"powershell.exe";
proc.Arguments = "-NoProfile -Command " + command;
proc.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
var p = System.Diagnostics.Process.Start(proc);
p.WaitForExit();

Or, more concisely:

using System;
using System.Diagnostics;

var proc = new ProcessStartInfo()
{
  UseShellExecute = true,
  Verb = "runas",
  WindowStyle = ProcessWindowStyle.Hidden,
  FileName = "powershell.exe",
  Arguments = "-NoProfile -Command " + command
};
Process.Start(proc).WaitForExit();
mklement0
  • 382,024
  • 64
  • 607
  • 775
  • Hey, Quick question: I want to change a registry key using the above? how would i go about that? – securityghost Mar 18 '19 at 01:49
  • @securityghost: The generic `*-Item*` cmdlets allow you to work with the registry, as in [this answer](https://stackoverflow.com/a/35228704/45375), for instance. If you need further help, please create a _new_ question; feel free to ping me here once you've done so. – mklement0 Mar 18 '19 at 02:15