Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
304 changes: 304 additions & 0 deletions mods/toggle-hidden-files.wh.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,304 @@
// ==WindhawkMod==
// @id toggle-hidden-files
// @name Toggle Hidden Files
// @description Toggle the visibility of hidden files in Windows Explorer using Ctrl+H
// @version 1.0.0
// @author Asteski
// @github https://github.com/Asteski
// @include explorer.exe
// @compilerOptions -std=c++20

Check warning on line 9 in mods/toggle-hidden-files.wh.cpp

View workflow job for this annotation

GitHub Actions / Test changed files

@‌compilerOptions require manual verification
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this compiler option needed?

// ==/WindhawkMod==

// ==WindhawkModSettings==
/*
- toggleProtectedFiles: true
$name: Also toggle protected OS files
$description: When enabled, Ctrl+H will also toggle the visibility of protected operating system files
*/
// ==/WindhawkModSettings==

// ==WindhawkModReadme==
/*
# Toggle Hidden Files

This mod allows you to toggle the visibility of hidden files in Windows Explorer using the Ctrl+H keyboard shortcut.

## Features
- Ctrl+H hotkey that works only when Explorer windows are focused
- Toggles the "Show hidden files" setting
- Optional: Also toggle protected OS files
- Automatically refreshes Explorer windows
- Works with all Windows Explorer windows

## Usage
1. **Focus an Explorer window** - Click on or open any File Explorer window
2. **Press Ctrl+H** - Use the keyboard shortcut to toggle hidden files visibility
3. **The setting will be applied immediately** to all Explorer windows

## Settings
- **Also toggle protected OS files**: When enabled, Ctrl+H will also show/hide protected operating system files

## Technical Details
- Only activates when Windows Explorer windows are in focus
- Modifies the standard registry settings for showing hidden files
- Sends refresh messages to all Explorer windows
- Handles proper cleanup when the mod is unloaded
- Explorer process must be restarted for changes to take effect
*/
// ==/WindhawkModReadme==

#include <windows.h>
#include <shlobj.h>
#include <shellapi.h>

// Settings structure
struct {
bool toggleProtectedFiles;
} g_settings;

// Global variables
HHOOK g_hKeyboardHook = nullptr;
bool g_modEnabled = false;

// Registry keys and values for hidden files settings
const wchar_t* EXPLORER_ADVANCED_KEY = L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Advanced";
const wchar_t* HIDDEN_FILES_VALUE = L"Hidden";
const wchar_t* SUPER_HIDDEN_VALUE = L"ShowSuperHidden";

const DWORD SHOW_HIDDEN = 1;
const DWORD HIDE_HIDDEN = 2;
const DWORD SHOW_SUPER_HIDDEN = 1;
const DWORD HIDE_SUPER_HIDDEN = 0;

// Window context enumeration
enum WindowContext {
CONTEXT_UNKNOWN = 0,
CONTEXT_EXPLORER = 1,
CONTEXT_DESKTOP = 2
};

// Function declarations
LRESULT CALLBACK KeyboardHookProc(int nCode, WPARAM wParam, LPARAM lParam);
bool ToggleHiddenFiles();
bool ToggleProtectedFiles();
void RefreshAllExplorerWindows();
bool IsCtrlHPressed(WPARAM wParam, LPARAM lParam);
void LoadSettings();
WindowContext GetCurrentWindowContext();
DWORD GetHiddenFilesSetting();
bool SetHiddenFilesSetting(DWORD dwValue);
DWORD GetProtectedFilesSetting();
bool SetProtectedFilesSetting(DWORD dwValue);

// Get current window context based on focused window
WindowContext GetCurrentWindowContext() {
HWND hForeground = GetForegroundWindow();
if (!hForeground) {
return CONTEXT_UNKNOWN;
}

wchar_t className[256];
if (GetClassNameW(hForeground, className, sizeof(className) / sizeof(wchar_t)) == 0) {
return CONTEXT_UNKNOWN;
}

// Check for Explorer windows
if (wcscmp(className, L"CabinetWClass") == 0 ||
wcscmp(className, L"ExploreWClass") == 0) {
return CONTEXT_EXPLORER;
}

// Check for Desktop
if (wcscmp(className, L"Progman") == 0 ||
wcscmp(className, L"WorkerW") == 0) {
return CONTEXT_DESKTOP;
}

// Also check if it's a desktop child window
HWND hDesktop = GetShellWindow();
if (hDesktop && (hForeground == hDesktop || IsChild(hDesktop, hForeground))) {
return CONTEXT_DESKTOP;
}

return CONTEXT_UNKNOWN;
}

// Get current hidden files setting from registry
DWORD GetHiddenFilesSetting() {
HKEY hKey;
DWORD dwValue = HIDE_HIDDEN; // Default to hidden
DWORD dwSize = sizeof(DWORD);

if (RegOpenKeyExW(HKEY_CURRENT_USER, EXPLORER_ADVANCED_KEY, 0, KEY_READ, &hKey) == ERROR_SUCCESS) {
RegQueryValueExW(hKey, HIDDEN_FILES_VALUE, nullptr, nullptr, (LPBYTE)&dwValue, &dwSize);
RegCloseKey(hKey);
}

return dwValue;
}

// Set hidden files setting in registry
bool SetHiddenFilesSetting(DWORD dwValue) {
HKEY hKey;
bool success = false;

if (RegOpenKeyExW(HKEY_CURRENT_USER, EXPLORER_ADVANCED_KEY, 0, KEY_WRITE, &hKey) == ERROR_SUCCESS) {
if (RegSetValueExW(hKey, HIDDEN_FILES_VALUE, 0, REG_DWORD, (LPBYTE)&dwValue, sizeof(DWORD)) == ERROR_SUCCESS) {
success = true;
}
RegCloseKey(hKey);
}

return success;
}

// Get current protected files setting from registry
DWORD GetProtectedFilesSetting() {
HKEY hKey;
DWORD dwValue = HIDE_SUPER_HIDDEN; // Default to hidden
DWORD dwSize = sizeof(DWORD);

if (RegOpenKeyExW(HKEY_CURRENT_USER, EXPLORER_ADVANCED_KEY, 0, KEY_READ, &hKey) == ERROR_SUCCESS) {
RegQueryValueExW(hKey, SUPER_HIDDEN_VALUE, nullptr, nullptr, (LPBYTE)&dwValue, &dwSize);
RegCloseKey(hKey);
}

return dwValue;
}

// Set protected files setting in registry
bool SetProtectedFilesSetting(DWORD dwValue) {
HKEY hKey;
bool success = false;

if (RegOpenKeyExW(HKEY_CURRENT_USER, EXPLORER_ADVANCED_KEY, 0, KEY_WRITE, &hKey) == ERROR_SUCCESS) {
if (RegSetValueExW(hKey, SUPER_HIDDEN_VALUE, 0, REG_DWORD, (LPBYTE)&dwValue, sizeof(DWORD)) == ERROR_SUCCESS) {
success = true;
}
RegCloseKey(hKey);
}

return success;
}

// Toggle hidden files setting
bool ToggleHiddenFiles() {
DWORD currentSetting = GetHiddenFilesSetting();
DWORD newSetting = (currentSetting == SHOW_HIDDEN) ? HIDE_HIDDEN : SHOW_HIDDEN;

return SetHiddenFilesSetting(newSetting);
}

// Toggle protected files setting
bool ToggleProtectedFiles() {
DWORD currentSetting = GetProtectedFilesSetting();
DWORD newSetting = (currentSetting == SHOW_SUPER_HIDDEN) ? HIDE_SUPER_HIDDEN : SHOW_SUPER_HIDDEN;

return SetProtectedFilesSetting(newSetting);
}

// Load settings from Windhawk configuration
void LoadSettings() {
// Default values
g_settings.toggleProtectedFiles = true;
}

// Refresh all Explorer windows
void RefreshAllExplorerWindows() {
// Send a message to all windows to refresh their view
SendNotifyMessageW(HWND_BROADCAST, WM_SETTINGCHANGE, 0, (LPARAM)L"ShellState");

// Also try to refresh specifically Explorer windows
HWND hWnd = nullptr;
while ((hWnd = FindWindowExW(nullptr, hWnd, L"CabinetWClass", nullptr)) != nullptr) {
SendNotifyMessageW(hWnd, WM_COMMAND, 41504, 0); // Refresh command
}

// Also check ExploreWClass windows
hWnd = nullptr;
while ((hWnd = FindWindowExW(nullptr, hWnd, L"ExploreWClass", nullptr)) != nullptr) {
SendNotifyMessageW(hWnd, WM_COMMAND, 41504, 0);
}

// Refresh desktop as well
HWND hDesktop = GetShellWindow();
if (hDesktop) {
SendNotifyMessageW(hDesktop, WM_COMMAND, 41504, 0);
}
}

// Check if Ctrl+H is pressed
bool IsCtrlHPressed(WPARAM wParam, LPARAM lParam) {
if (wParam != WM_KEYDOWN) {
return false;
}

KBDLLHOOKSTRUCT* pKeyboard = (KBDLLHOOKSTRUCT*)lParam;

// Check if 'H' key is pressed
if (pKeyboard->vkCode != 'H') {
return false;
}

// Check if Ctrl is pressed
return (GetAsyncKeyState(VK_CONTROL) & 0x8000) != 0;
}

// Keyboard hook procedure
LRESULT CALLBACK KeyboardHookProc(int nCode, WPARAM wParam, LPARAM lParam) {
if (nCode >= 0 && g_modEnabled) {
WindowContext context = GetCurrentWindowContext();

// Only process if we're in Explorer windows
if (context == CONTEXT_EXPLORER && IsCtrlHPressed(wParam, lParam)) {
// Toggle hidden files
bool success = ToggleHiddenFiles();

// Also toggle protected files if setting is enabled
if (g_settings.toggleProtectedFiles) {
success = ToggleProtectedFiles() && success;
}

if (success) {
RefreshAllExplorerWindows();
}

// Consume the key press
return 1;
}
}

return CallNextHookEx(g_hKeyboardHook, nCode, wParam, lParam);
}

// Mod initialization
BOOL Wh_ModInit() {
// Load settings
LoadSettings();

// Install keyboard hook
g_hKeyboardHook = SetWindowsHookExW(WH_KEYBOARD_LL, KeyboardHookProc, GetModuleHandle(nullptr), 0);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From the documentation:

This hook is called in the context of the thread that installed it. The call is made by sending a message to the thread that installed the hook. Therefore, the thread that installed the hook must have a message loop.

Wh_ModInit is either called on the initial Explorer thread, or in a dedicated Windhawk thread. Not in all cases that thread has a message loop. To make it work in all cases, create a dedicated thread for it. Here's an example:

https://github.com/m417z/7-Taskbar-Numberer/blob/master/dll/keybd_hook.c


if (!g_hKeyboardHook) {
return FALSE;
}

g_modEnabled = true;
return TRUE;
}

// Settings changed callback
void Wh_ModSettingsChanged() {
LoadSettings();
}

// Mod cleanup
void Wh_ModUninit() {
g_modEnabled = false;

// Remove keyboard hook
if (g_hKeyboardHook) {
UnhookWindowsHookEx(g_hKeyboardHook);
g_hKeyboardHook = nullptr;
}
}
Loading