1

I'm trying to run a process using diskpart, but to do that I need administrator privileges because I'm using a PC at my work. In order to run a Process as administrator I need Process.StartInfo.Verb = "runas" and I also need Process.StartInfo.UseShellExecute = true. With UseShellExecute set to true, I can't pass commands to standard input, but if I set it to false, I get an error saying "The requested operation requires elevation" (aka I need admin privileges). If I try passing a script to in Process.StartInfo.Arguments it doesn't seem to do anything. Here are a couple versions of the code I have tried out so far (none of them have worked):

Version 1:

Process p = new Process();
p.StartInfo.UseShellExecute = true;
p.StartInfo.FileName = @"C:\Windows\System32\diskpart.exe";
p.StartInfo.Verb = "runas";
p.StartInfo.Arguments = "vhdScript.txt";
p.Start();

Version 2:

Process p = new Process();
p.StartInfo.UseShellExecute = true;
p.StartInfo.FileName = @"C:\Windows\System32\diskpart.exe";
p.StartInfo.Verb = "runas";
p.StartInfo.Arguments = "/s vhdScript.txt";
p.Start();

Version 3:

Process p = new Process();
p.StartInfo.UseShellExecute = true;
p.StartInfo.FileName = @"C:\Windows\System32\cmd.exe";
p.StartInfo.Verb = "runas";
p.StartInfo.Arguments = "/c diskpart /s vhdScript.txt";
p.Start();

Version 4:

Process p = new Process();
p.StartInfo.UseShellExecute = false;
p.StartInfo.FileName = @"C:\Windows\System32\cmd.exe";
p.StartInfo.Verb = "runas";
p.StartInfo.Arguments = "/c diskpart";
p.StartInfo.RedirectStandardInput = true;
p.Start();
p.StandardInput.WriteLine("select vdisk 1");

Any thoughts? Thanks.

2 Answers2

0

The following shows how to create a diskpart script and then use System.Diagnostics.Process to execute the script.

Create a new Windows Forms App (.NET Framework) project (name: ProcessDiskPartTest)

Add an Application Manifest to your project

Note: This is used to prompt the user to execute the program as Administrator.

  • In VS menu, click Project
  • Select Add New Item...
  • Select Application Manifest File (Windows Only) (name: app.manifest)
  • Click Add

In app.manifest, replace

<requestedExecutionLevel level="asInvoker" uiAccess="false" />

with

<requestedExecutionLevel  level="requireAdministrator" uiAccess="false" />

Add the following using statements:

  • using System.IO;
  • using System.Diagnostics;

The following code will use Process to execute diskpart script.

private void RunDiskPart(string arguments)
{
    string diskpartPath = Path.Combine(Environment.GetEnvironmentVariable("windir"), "System32", "diskpart.exe");

    if (!System.IO.File.Exists(diskpartPath))
        throw new Exception(String.Format("'{0}' doesn't exist.", diskpartPath));

    Debug.WriteLine("diskpartPath: " + diskpartPath);

    ProcessStartInfo psInfo = new ProcessStartInfo(diskpartPath);
    psInfo.Arguments = arguments;
    
    psInfo.CreateNoWindow = true;
    psInfo.RedirectStandardError = true; //redirect standard Error
    psInfo.RedirectStandardOutput = true; //redirect standard output
    psInfo.RedirectStandardInput = false;
    psInfo.UseShellExecute = false; //if True, uses 'ShellExecute'; if false, uses 'CreateProcess'
    psInfo.Verb = "runas"; //use elevated permissions
    psInfo.WindowStyle = ProcessWindowStyle.Hidden;
    
    //create new instance and set properties
    using (Process p = new Process() { EnableRaisingEvents = true, StartInfo = psInfo })
    {
        //subscribe to event and add event handler code
        p.ErrorDataReceived += (sender, e) =>
        {
            if (!String.IsNullOrEmpty(e.Data))
            {
                //ToDo: add desired code 
                Debug.WriteLine("Error: " + e.Data);
            }
        };

        //subscribe to event and add event handler code
        p.OutputDataReceived += (sender, e) =>
        {
            if (!String.IsNullOrEmpty(e.Data))
            {
                //ToDo: add desired code
                Debug.WriteLine("Output: " + e.Data);
            }
        };

        p.Start(); //start

        p.BeginErrorReadLine(); //begin async reading for standard error
        p.BeginOutputReadLine(); //begin async reading for standard output

        //waits until the process is finished before continuing
        p.WaitForExit();
    }
}

Below, shows how to add a diskpart script as an embedded resource.

Create a folder for your DiskPart script(s)

  • In VS menu, click View
  • Select Solution Explorer
  • In Solution Explorer, right-click <project name>. Select Add. Select New Folder (name: DiskPartScripts)

Add DiskPart script to project

Note: The diskpart script below is only for testing purposes. Rename it to your desired name and replace the commands with your desired diskpart command(s).

  • In Solution Explorer, right-click DiskPartScripts folder. Select Add. Select New Item....
  • Select Text File (name: DiskPartListDisk.txt)

DiskPartListDisk.txt

list disk
list volume

Make text file an embedded resource

  • In VS menu, click View
  • Select Properties Window
  • In Solution Explorer, click on "DiskpartListDisk.txt"
  • In Properties Window, set Build Action: Embedded Resource

The following is used to read an embedded text file.

Create a class (name: HelperLoadResource.cs)

HelperLoadResource.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Reflection;
using System.Diagnostics;

namespace ProcessDiskPartTest
{
    public static class HelperLoadResource
    {
        public static string ReadResource(string filename)
        {
            //use UTF8 encoding as the default encoding
            return ReadResource(filename, Encoding.UTF8);
        }

        public static string ReadResource(string filename, Encoding fileEncoding)
        {
            string fqResourceName = string.Empty;
            string result = string.Empty;

            //get executing assembly
            Assembly execAssembly = Assembly.GetExecutingAssembly();

            //get resource names
            string[] resourceNames = execAssembly.GetManifestResourceNames();

            if (resourceNames != null && resourceNames.Length > 0)
            {
                foreach (string rName in resourceNames)
                {
                    if (rName.EndsWith(filename))
                    {

                        //set value to 1st match
                        //if the same filename exists in different folders,
                        //the filename can be specified as <folder name>.<filename>
                        //or <namespace>.<folder name>.<filename>
                        fqResourceName = rName;

                        //exit loop
                        break;
                    }
                }

                //if not found, throw exception
                if (String.IsNullOrEmpty(fqResourceName))
                {
                    throw new Exception($"Resource '{filename}' not found.");
                }

                //get file text
                using (Stream s = execAssembly.GetManifestResourceStream(fqResourceName))
                {
                    using (StreamReader reader = new StreamReader(s, fileEncoding))
                    {
                        //get text
                        result = reader.ReadToEnd();
                    }
                }
            }

            return result;
        }
    }
}

Note: The code for "ReadResource" is from here.


Usage:

//temp filename that we'll use for diskpart
string diskpartScriptFilename = Path.Combine(Path.GetTempPath(), String.Format("diskpart_{0}.txt", System.Reflection.Assembly.GetExecutingAssembly().GetName().Name));

//get embedded diskpart script
string diskpartScript = HelperLoadResource.ReadResource("DiskpartListDisk.txt");

//write script to file
File.WriteAllText(diskpartScriptFilename, diskpartScript);

//execute script
RunDiskPart("/s " + diskpartScriptFilename);

if (File.Exists(diskpartScriptFilename))
    File.Delete(diskpartScriptFilename); //delete file

Resources:

Tu deschizi eu inchid
  • 4,117
  • 3
  • 13
  • 24
0

I followed one of the suggestions in this article UseShellExecute=false and Raising Elevation

When I create an command line exe with the below code, and Run As Administrator, I get the results I expect. May or may not work for you. (the SELECT VDISK 1 syntax was incorrect from your example so I improvised). This might help

using (Process p = new())
{
    p.StartInfo.UseShellExecute = false;
    p.StartInfo.FileName = @"C:\Windows\System32\diskpart.exe";
    p.StartInfo.Verb = "runas";
    //p.StartInfo.Arguments = "/c diskpart";
    p.StartInfo.RedirectStandardInput = true;

    p.Start();

    StreamWriter myStreamWriter = p.StandardInput;
    myStreamWriter.WriteLine("LIST DISK");
    myStreamWriter.Close();
    p.WaitForExitAsync();

}
PsiMatrix
  • 61
  • 8
  • By setting `p.StartInfo.UseShellExecute = false;`, ` p.StartInfo.Verb = "runas";` is _ignored_. Sure if the calling process is _itself_ already elevated, any processes it spawns are elevated too, so stdin input can be provided to them, and their output can be captured. However, the premise of the question is how to communicate with the streams of an elevated process from a _non-elevated_ process (which isn't directly supported). – mklement0 Dec 06 '22 at 03:59