using System.Runtime.Versioning; using Microsoft.Win32; namespace DevDrive; /// /// Handles hiding and showing drives from Windows Explorer using registry. /// [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); } }