Move DevDrive project to repo root
This commit is contained in:
174
DriveHider.cs
Normal file
174
DriveHider.cs
Normal file
@@ -0,0 +1,174 @@
|
||||
using System.Runtime.Versioning;
|
||||
using Microsoft.Win32;
|
||||
|
||||
namespace DevDrive;
|
||||
|
||||
/// <summary>
|
||||
/// Handles hiding and showing drives from Windows Explorer using registry.
|
||||
/// </summary>
|
||||
[SupportedOSPlatform("windows")]
|
||||
public static class DriveHider
|
||||
{
|
||||
private const string LogPrefix = "[DriveHider]";
|
||||
private const string ExplorerPoliciesKey = @"Software\Microsoft\Windows\CurrentVersion\Policies\Explorer";
|
||||
private const string NoDrivesValue = "NoDrives";
|
||||
|
||||
private static int _hiddenDriveMask;
|
||||
private static string? _hiddenDriveLetter;
|
||||
private static int _originalNoDrives;
|
||||
|
||||
public static bool HideDrive(string driveLetter)
|
||||
{
|
||||
if (_hiddenDriveLetter != null)
|
||||
{
|
||||
Console.WriteLine($"{LogPrefix} Drive already hidden");
|
||||
return true;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var letter = driveLetter.TrimEnd('\\', ':').ToUpperInvariant();
|
||||
if (letter.Length != 1 || letter[0] < 'A' || letter[0] > 'Z')
|
||||
{
|
||||
Console.WriteLine($"{LogPrefix} Invalid drive letter: {driveLetter}");
|
||||
return false;
|
||||
}
|
||||
|
||||
var driveIndex = letter[0] - 'A';
|
||||
_hiddenDriveMask = 1 << driveIndex;
|
||||
_hiddenDriveLetter = letter + ":\\";
|
||||
|
||||
using var key = Registry.CurrentUser.CreateSubKey(ExplorerPoliciesKey);
|
||||
if (key == null)
|
||||
{
|
||||
Console.WriteLine($"{LogPrefix} Failed to open registry key");
|
||||
return false;
|
||||
}
|
||||
|
||||
_originalNoDrives = GetNoDrivesValue(key);
|
||||
var newValue = _originalNoDrives | _hiddenDriveMask;
|
||||
|
||||
key.SetValue(NoDrivesValue, newValue, RegistryValueKind.DWord);
|
||||
|
||||
RefreshExplorer();
|
||||
|
||||
Console.WriteLine($"{LogPrefix} Hidden drive {letter}: from Explorer");
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"{LogPrefix} Error hiding drive: {ex.Message}");
|
||||
_hiddenDriveMask = 0;
|
||||
_hiddenDriveLetter = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool ShowDrive()
|
||||
{
|
||||
if (_hiddenDriveLetter == null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
using var key = Registry.CurrentUser.OpenSubKey(ExplorerPoliciesKey, writable: true);
|
||||
if (key == null)
|
||||
{
|
||||
Console.WriteLine($"{LogPrefix} Failed to open registry key for restore");
|
||||
return false;
|
||||
}
|
||||
|
||||
var currentValue = GetNoDrivesValue(key);
|
||||
var newValue = currentValue & ~_hiddenDriveMask;
|
||||
|
||||
if (newValue == 0)
|
||||
{
|
||||
key.DeleteValue(NoDrivesValue, throwOnMissingValue: false);
|
||||
}
|
||||
else
|
||||
{
|
||||
key.SetValue(NoDrivesValue, newValue, RegistryValueKind.DWord);
|
||||
}
|
||||
|
||||
RefreshExplorer();
|
||||
|
||||
Console.WriteLine($"{LogPrefix} Restored drive {_hiddenDriveLetter.TrimEnd('\\')} in Explorer");
|
||||
_hiddenDriveMask = 0;
|
||||
_hiddenDriveLetter = null;
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"{LogPrefix} Error restoring drive: {ex.Message}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool IsHidden => _hiddenDriveLetter != null;
|
||||
public static string? HiddenDriveLetter => _hiddenDriveLetter;
|
||||
|
||||
private static int GetNoDrivesValue(RegistryKey key)
|
||||
{
|
||||
var value = key.GetValue(NoDrivesValue);
|
||||
|
||||
return value switch
|
||||
{
|
||||
null => 0,
|
||||
int intValue => intValue,
|
||||
uint uintValue => (int)uintValue,
|
||||
byte[] bytes when bytes.Length >= 4 => BitConverter.ToInt32(bytes, 0),
|
||||
byte[] bytes when bytes.Length > 0 => bytes[0],
|
||||
_ => 0
|
||||
};
|
||||
}
|
||||
|
||||
private static void RefreshExplorer()
|
||||
{
|
||||
try
|
||||
{
|
||||
NativeMethods.SendMessageTimeout(
|
||||
NativeMethods.HWND_BROADCAST,
|
||||
NativeMethods.WM_SETTINGCHANGE,
|
||||
IntPtr.Zero,
|
||||
"Policy",
|
||||
NativeMethods.SMTO_ABORTIFHUNG,
|
||||
1000,
|
||||
out _);
|
||||
|
||||
NativeMethods.SHChangeNotify(
|
||||
NativeMethods.SHCNE_DRIVEADD | NativeMethods.SHCNE_DRIVEREMOVED,
|
||||
NativeMethods.SHCNF_FLUSHNOWAIT,
|
||||
IntPtr.Zero,
|
||||
IntPtr.Zero);
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
private static class NativeMethods
|
||||
{
|
||||
public const int SHCNE_DRIVEADD = 0x00000100;
|
||||
public const int SHCNE_DRIVEREMOVED = 0x00000080;
|
||||
public const int SHCNF_FLUSHNOWAIT = 0x3000;
|
||||
|
||||
public static readonly IntPtr HWND_BROADCAST = new(0xFFFF);
|
||||
public const int WM_SETTINGCHANGE = 0x001A;
|
||||
public const int SMTO_ABORTIFHUNG = 0x0002;
|
||||
|
||||
[System.Runtime.InteropServices.DllImport("shell32.dll")]
|
||||
public static extern void SHChangeNotify(int wEventId, int uFlags, IntPtr dwItem1, IntPtr dwItem2);
|
||||
|
||||
[System.Runtime.InteropServices.DllImport("user32.dll", SetLastError = true, CharSet = System.Runtime.InteropServices.CharSet.Auto)]
|
||||
public static extern IntPtr SendMessageTimeout(
|
||||
IntPtr hWnd,
|
||||
int Msg,
|
||||
IntPtr wParam,
|
||||
string lParam,
|
||||
int fuFlags,
|
||||
int uTimeout,
|
||||
out IntPtr lpdwResult);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user