Reputation: 9
I'm quite lost with this task. Basically I have to check if a pendrive has readonly property.
This is my code:
private void protectpendrive(string disk)
{
Process p = new Process();
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.FileName = @"C:\Windows\System32\diskpart.exe";
p.StartInfo.RedirectStandardInput = true;
p.Start();
p.StandardInput.WriteLine("select volume " + disk);
p.StandardInput.WriteLine("attributes disk set readonly");
debugListView.Items.Add(DateTime.Now + "Pendrive is now read-only.");
p.StandardInput.WriteLine("exit");
}
To unprotect:
private void unprotectpendrive(string disk)
{
Process p = new Process();
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.FileName = @"C:\Windows\System32\diskpart.exe";
p.StartInfo.RedirectStandardInput = true;
p.Start();
p.StandardInput.WriteLine("select volume " + disk);
p.StandardInput.WriteLine("attributes disk clear readonly");
debugListView.Items.Add(DateTime.Now + "Pendrive is now not protected.");
p.StandardInput.WriteLine("exit");
}
This is my control for the current status, but it doesn't work either:
private bool checkreadonly(string disk)
{
DirectoryInfo pendrive = new DirectoryInfo(disk);
if (pendrive.Attributes.HasFlag(FileAttributes.ReadOnly))
{
debugListView.Items.Add(DateTime.Now + " Pendrive is read-only.");
return true;
}
debugListView.Items.Add(DateTime.Now + " Pendrive is writable.");
return false;
}
The issues I have are:
What am I doing wrong?
Upvotes: 0
Views: 286
Reputation: 4993
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 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:
Upvotes: 0