175 lines
5.4 KiB
C#
175 lines
5.4 KiB
C#
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);
|
|
}
|
|
}
|