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.
CodePudding user response:
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:
CodePudding user response:
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();
}