The following shows how to both set and clear the readonly attribute for a removable USB disk.
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" />
Option 1 - Diskpart (script)
The following shows how to create a diskpart script and then use System.Diagnostics.Process to execute the script.
Add the following using directives:
using System.IO;
using System.Diagnostics;
The following code will use Process to execute a diskpart
script.
public void RunDiskPartScript(string scriptText)
{
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);
string scriptFilename = System.IO.Path.Combine(Path.GetTempPath(), Path.GetFileNameWithoutExtension(System.Reflection.Assembly.GetExecutingAssembly().Location));
Debug.WriteLine($"scriptFilename: '{scriptFilename}'");
//save script
System.IO.File.WriteAllText(scriptFilename, scriptText);
psInfo.Arguments = $"/s {scriptFilename}";
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();
if (File.Exists(scriptFilename))
File.Delete(scriptFilename); //delete file
}
}
public void SetDiskProperty(string driveLetter, bool isReadOnly)
{
//create diskpart script text
StringBuilder sbScript = new StringBuilder();
sbScript.AppendLine($"select volume {driveLetter}");
if (isReadOnly)
sbScript.AppendLine($"attributes disk set readonly");
else
sbScript.AppendLine($"attributes disk clear readonly");
//Debug.WriteLine($"Script:\n'{sbScript.ToString()}'");
//execute script
RunDiskPartScript(sbScript.ToString());
}
Usage (Clear Readonly):
//The drive letter can be one of the following formats: `H`, `H:`, or `H:\`
SetDiskProperty("H:\", false);
Usage (Set Readonly):
//The drive letter can be one of the following formats: `H`, `H:`, or `H:\`
SetDiskProperty("H", true);
Option 2 - Diskpart (StandardInput)
The following shows how to call diskpart by using System.Diagnostics.Process and sending commands via StandardInput
Add the following using directives:
using System.IO;
using System.Diagnostics;
public static void DiskClearReadOnly(string driveLetter)
{
List<string> commands = new List<string>() { $"select volume {driveLetter}", "attributes disk clear readonly", "exit" };
RunDiskPart(commands);
Debug.WriteLine($"Disk '{driveLetter}' is now read-write.");
}
public static void DiskSetReadOnly(string driveLetter)
{
List<string> commands = new List<string>() { $"select volume {driveLetter}", "attributes disk set readonly", "exit" };
RunDiskPart(commands);
Debug.WriteLine($"Disk '{driveLetter}' is now read-only.");
}
private void RunDiskPart(List<string> commands, string arguments = null)
{
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);
if (!String.IsNullOrEmpty (arguments))
psInfo.Arguments = arguments;
psInfo.CreateNoWindow = true;
psInfo.RedirectStandardError = true; //redirect standard Error
psInfo.RedirectStandardOutput = true; //redirect standard output
psInfo.RedirectStandardInput = true;
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
//send input
foreach (string cmd in commands)
{
p.StandardInput.WriteLine(cmd);
}
//waits until the process is finished before continuing
p.WaitForExit();
}
}
Usage (Clear Readonly):
//The drive letter can be one of the following formats: `H`, `H:`, or `H:\`
DiskClearReadOnly("H:\");
Usage (Set Readonly):
//The drive letter can be one of the following formats: `H`, `H:`, or `H:\`
DiskSetReadOnly("H:\");
Option 3 - PowerShell:
The following shows how to use PowerShell to set or clear the readonly attribute for a removable USB drive.
Download/install the appropriate PowerShell NuGet package:
For .NET Framework, choose one of the following:
For .NET (version >= 6), choose one of the following:
See Choosing the right PowerShell NuGet package for your .NET project for more information and Differences between Windows PowerShell 5.1 and PowerShell 7.x
Add the following using directives:
using System.Management.Automation.Runspaces
using System.Management.Automation;
using System.Diagnostics;
public static void DiskClearReadOnly(string driveLetter)
{
RunPowerShellCommand(driveLetter, false);
Debug.WriteLine($"Disk '{driveLetter}' is now read-write.");
}
public static void DiskClearReadOnly(int driveNumber)
{
RunPowerShellCommand(driveNumber, false);
Debug.WriteLine($"Disk '{driveNumber}' is now read-write.");
}
public static void DiskSetReadOnly(string driveLetter)
{
RunPowerShellCommand(driveLetter, true);
Debug.WriteLine($"Disk '{driveLetter}' is now read-only.");
}
public static void DiskSetReadOnly(int driveNumber)
{
RunPowerShellCommand(driveNumber, true);
Debug.WriteLine($"Disk '{driveNumber}' is now read-only.");
}
private static void RunPowerShellCommand(string driveLetter, bool isReadOnly)
{
InitialSessionState iss = InitialSessionState.CreateDefault();
iss.ExecutionPolicy = Microsoft.PowerShell.ExecutionPolicy.RemoteSigned; //set execution policy
using (Runspace runspace = RunspaceFactory.CreateRunspace(iss))
{
//open
runspace.Open();
using (PowerShell ps = PowerShell.Create())
{
ps.Runspace = runspace;
var psInstance = PowerShell.Create();
//Get-Partition -DriveLetter <driveLetter>
psInstance.AddCommand("Get-Partition").AddParameter("DriveLetter", driveLetter);
//Get-Disk
psInstance.AddCommand("Get-Disk");
//Set-Disk -IsReadOnly [True | False]
psInstance.AddCommand("Set-Disk").AddParameter("IsReadOnly", isReadOnly);
System.Collections.ObjectModel.Collection<PSObject> outputCollection = psInstance.Invoke();
//'Set-Disk' doesn't produce any output, so there won't be any output
foreach (PSObject outputItem in outputCollection)
{
//create reference
string data = outputItem.BaseObject?.ToString();
System.Diagnostics.Debug.WriteLine(outputItem.ToString());
}
}
}
}
private static void RunPowerShellCommand(int diskNumber, bool isReadOnly)
{
InitialSessionState iss = InitialSessionState.CreateDefault();
iss.ExecutionPolicy = Microsoft.PowerShell.ExecutionPolicy.RemoteSigned; //set execution policy
using (Runspace runspace = RunspaceFactory.CreateRunspace(iss))
{
//open
runspace.Open();
using (PowerShell ps = PowerShell.Create())
{
ps.Runspace = runspace;
var psInstance = PowerShell.Create();
//Get-Disk -Number <diskNumber>
psInstance.AddCommand("Get-Disk").AddParameter("Number", diskNumber);
//Set-Disk -IsReadOnly [True | False]
psInstance.AddCommand("Set-Disk").AddParameter("IsReadOnly", isReadOnly);
System.Collections.ObjectModel.Collection<PSObject> outputCollection = psInstance.Invoke();
//'Set-Disk' doesn't produce any output, so there won't be any output
foreach (PSObject outputItem in outputCollection)
{
//create reference
string data = outputItem.BaseObject?.ToString();
System.Diagnostics.Debug.WriteLine(outputItem.ToString());
}
}
}
}
Usage (Clear Readonly):
//The drive letter can be one of the following formats: `H`, `H:`, or `H:\`
DiskClearReadOnly("H:\");
Usage (Set Readonly):
//The drive letter can be one of the following formats: `H`, `H:`, or `H:\`
DiskSetReadOnly("H:\");
Resources: