Skip to content

Add Redirect Settings to Control Panel mod#4278

Open
babamohammed2022 wants to merge 4 commits into
ramensoftware:mainfrom
babamohammed2022:patch-1
Open

Add Redirect Settings to Control Panel mod#4278
babamohammed2022 wants to merge 4 commits into
ramensoftware:mainfrom
babamohammed2022:patch-1

Conversation

@babamohammed2022
Copy link
Copy Markdown

Redirect Settings to Control Panel

This mod intercepts modern ms-settings: URI protocols and forces Windows to open their classic Control Panel equivalents using native Windows components and legacy CLSIDs.

Features

  • Intercepts ShellExecuteExW, ShellExecuteW, and CreateProcessW calls
  • Smart Personalization Detection via hwnd context awareness
  • 200+ URI mappings to Control Panel applets
  • Configurable fallback modes (ignore, control.exe, passthrough)
  • Windows 11 compatibility mode for deprecated CLSIDs

Compatibility

  • Windows 10 21H2 (IoT Enterprise LTSC 2021) ✅
  • Windows 10 22H2 ✅
  • Windows 11 24H2 ✅ (but more limited compared to Windows 10)

Limitations

  • Windows 10: ~70% redirection success rate
  • Windows 11: ~5% baseline restoration (many CLSIDs deprecated by Microsoft)

Changelog

If this pull request updates an existing mod, describe the changes below:

  • Initial release - Redirect Settings to Control Panel v9.6.0
  • 200+ ms-settings: URI mappings to classic Control Panel
  • Smart Personalization detection with hwnd context awareness
  • Windows 11 compatibility mode

Mod authorship

If this pull request introduces a new mod, please complete the section below.

This mod was created by:

    • The submitter, without AI assistance
    • The submitter, with AI assistance
    • Claude
    • ChatGPT
    • Gemini
    • Another AI (please specify): Deepseek
    • Other (please specify):

Please select the options that best apply. Your selection does not affect the acceptance criteria, but it helps reviewers understand the context of the code and provide relevant feedback.

@m417z
Copy link
Copy Markdown
Member

m417z commented Jun 3, 2026

Submission review

Note: This review was done by Claude, and then refined manually. Due to the amount of submissions, doing a fully manual review for each pull request is no longer feasible. Thank you for understanding.

Please address the following issues. The items in the collapsed sections are optional, so it's your call whether to address them.


The approach (hooking ShellExecute*/CreateProcessW in explorer so the redirect is fully reversible) is the right pattern for Windhawk. But the detection logic is too broad in a couple of places and breaks unrelated launches, and the mapping table is large but inaccurate. A few concrete things to fix:

1. The CreateProcessW "control system" matcher fires on almost every control.exe <applet> call. The match is cmdLine contains "control" and "system", minus a short exclusion list. The problem: when explorer launches a Control Panel item it passes the full path, e.g. C:\Windows\System32\control.exe appwiz.cpl — that string contains "system" (from System32) and "control", so it is intercepted and redirected to System Properties. The exclusion list (mmsys.cpl, main.cpl, desk.cpl, …) is itself proof of this — none of those names contain "system", so they only needed excluding because the System32 path is present. Anything not in that list (appwiz.cpl, powercfg.cpl, firewall.cpl, wscui.cpl, colorcpl, sysdm.cpl, …) gets wrongly sent to System Properties.

Match the actual command precisely instead of substring-scanning: parse the command line into argv, confirm the executable is control.exe and the lone argument is system (or /name microsoft.system).

Related: when the hook intercepts, it does return TRUE without ever populating lpProcessInformation. CreateProcessW's contract is that on success it fills hProcess/hThread/dwProcessId/dwThreadId. A caller that doesn't zero-init that struct and then reads/closes hProcess will touch a garbage handle. If you keep the fake-success path, zero the struct (and don't hand back a handle you didn't create); better, redirect via the ShellExecute* hooks and leave CreateProcessW to call the original.

2. Unmapped shell:::{…} targets are hijacked to Control Panel. IsShellClsid() matches any shell::: string passed to ShellExecuteExW/ShellExecuteW, and an unmapped one falls into HandleFallback, whose default (FallbackMode = 1) launches control.exe and returns intercept = true — so the original action is suppressed. That means any shell:::{clsid} invoked from explorer that you didn't explicitly list silently opens Control Panel instead of doing what was asked. Only ms-settings: inputs should be subject to the fallback policy; an unknown shell::: target should fall through to the original (return ShellExecute*_orig(...)).

3. .msc targets won't launch. In LaunchTarget, devmgmt.msc / compmgmt.msc take the "run directly" branch and are passed straight to CreateProcessW. CreateProcessW does not do shell association resolution, so a bare .msc (which isn't an executable) fails — Device Manager / Computer Management never open (you'll see the [ERROR] CreateProcess failed log). Launch them as mmc.exe <file>.msc, or route .msc/.cpl through ShellExecuteW/the original instead of CreateProcessW.

4. FallbackMode uses $options on a number setting. FallbackMode: 1 is inferred as a number and read with Wh_GetIntSetting, but $options dropdowns are for string settings — the stored option keys are compared as strings read via Wh_GetStringSetting. Compare with dynamic-island-for-windows, where both the default and the option keys are quoted strings. Either make it a string setting (FallbackMode: "1", quote the 0/1/2 keys, read with Wh_GetStringSetting) or drop $options and describe the integer values in $description.

5. Accuracy of the mapping table / overall effectiveness. The README itself states ~70% success on Win10 and ~5% on Win11, and many of the 200+ entries point at unrelated applets — e.g. ms-settings:privacy-microphone → Ease of Access Center ({D555645E…}), ms-settings:taskbar → Notification Area Icons, the ms-settings:gaming* family → a bare control.exe (Control Panel root). For a mod whose entire purpose is accurate redirection, landing the user on the wrong page is worse than not redirecting. Please trim the table to mappings that are actually verified, and for pages with no real classic equivalent, prefer leaving them alone (fallback passthrough) over mapping them to an unrelated applet or a bare Control Panel window. This is also relevant to the mod's standing in the catalog — a mod that's openly ~5% effective on the current OS is a hard sell.

Since the PR discloses heavy AI assistance, a few of these read like AI artifacts worth a second look: the control+system substring heuristic, the duplicated mmsys.cpl exclusion, and the many semantically-wrong mappings. Worth a manual pass to confirm each mapping is intentional and correct.

Optional improvements

Minor polish — none of this affects users, so it's your call.

  • Redundant log prefixes. Wh_Log already prefixes every line with the mod name, so the [v9.6.0], [SETTINGS], [DEBUG], [HOOK] … tags are noise — drop them.
  • @compilerOptions -lshell32 -lkernel32. Both are linked by default, and the mod resolves these APIs through GetProcAddress anyway, so neither -l is needed.
  • NormalizeUri off-by-one. result.substr(pos + 15) should be pos + 14 (length of "ms-settings://"); as written it drops the first character after // (e.g. ms-settings://displayms-settings:isplay). Narrow trigger (the // form is uncommon) but still wrong.
  • (HINSTANCE)42 as the fake ShellExecuteW success value is a magic number — a named constant (any value > 32) with a one-line comment reads better.
  • Settings reads race with LoadSettings. Wh_ModSettingsChanged runs on an arbitrary thread and rewrites the g_settings fields while the hooks read them. The torn reads are harmless scalars and only happen on a settings change, so this is low priority, but std::atomic (or just accepting it) would tidy it up.
  • README visual. The effect is visible (a Control Panel window opening instead of Settings); a short GIF/screenshot would help users understand what the mod does. Only i.imgur.com / raw.githubusercontent.com are allowed image hosts.

Functionality notes

Non-critical observations about the feature behavior itself.

  • Personalization detection is a fuzzy, English-only heuristic. IsPersonalizationWindow returns true for any CabinetWClass window (the class is shared by all Explorer and Control Panel windows) and otherwise matches the substring "personaliz" in the window title — which only works on English Windows (localized builds use translated titles). So "Desktop Background → wallpaper" detection can misfire when invoked from an unrelated Explorer window or on a non-English UI. It's a reasonable best-effort, just noting the limitation.
  • Scope is explorer-only. Redirects fire only for ShellExecute*/CreateProcessW calls made inside explorer.exe. ms-settings: links opened from Start/Search (StartMenuExperienceHost, SearchHost) or from other apps won't be redirected. That's a sensible conservative scope, but worth stating in the README so users know what's covered.

…ettings, trimmed mappings

Fix review issues: precise command parsing, msc via mmc, string settings, trimmed mappings to enhance the mod
…ngs, trimmed mappings

@m417z All review issues addressed:

1. ✅ CreateProcessW now uses IsControlSystemCommand() with proper argv parsing - "System32" path no longer causes false matches
2. ✅ Unmapped shell::: targets pass through to original instead of opening Control Panel
3. ✅ .msc files launched via mmc.exe
4. ✅ FallbackMode changed to string setting with quoted option keys
5. ✅ Removed inaccurate mappings (gaming family, privacy→easeofaccess, taskbar→notification area, bare control.exe redirects)
6. ✅ Fixed NormalizeUri off-by-one (pos+14)
7. ✅ Replaced magic number (HINSTANCE)42 with SHELL_EXECUTE_SUCCESS constant
8. ✅ Removed redundant @compilerOptions
9. ✅ Added control desktop → Personalization mapping
10. ✅ ZeroMemory on PROCESS_INFORMATION struct

Ready for re-review.
@babamohammed2022
Copy link
Copy Markdown
Author

Hello @m417z, I've edited the code to fix the reported issues (command parsing, shell fallback, .msc launching, string settings, wrong mappings). I hope that the mod is now ready for Windhawk. Have a good day!

Note: The fixes were assisted by AI.

Copy link
Copy Markdown
Author

@babamohammed2022 babamohammed2022 left a comment

Choose a reason for hiding this comment

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

Hello! The code has been updated.

@m417z
Copy link
Copy Markdown
Member

m417z commented Jun 4, 2026

As the review below says, manual testing is expected. Please do extensive manual testing, and post here how the testing was done.

Submission review

Note: This review was done by Claude, and then refined manually. Due to the amount of submissions, doing a fully manual review for each pull request is no longer feasible. Thank you for understanding.

Please address the following issues. The items in the collapsed sections are optional, so it's your call whether to address them.


Thanks for the quick turnaround — most of the previous round is addressed: the control system matcher now parses argv precisely (no more System32 false-match), unmapped shell::: targets fall through, .msc files launch via mmc.exe, FallbackMode is a quoted-string $options setting, PROCESS_INFORMATION is zeroed, the NormalizeUri off-by-one is fixed, the magic (HINSTANCE) value is named, and the redundant @compilerOptions/log prefixes are gone. Two things still need attention before merge:

1. The __display__ sentinel never resolves — those two redirects are broken. Two entries map to the literal string __display__:

{L"shell:::{26ee0668-...}\\3\\::{c555438b-...}", L"__display__"},   // line 316
{L"shell:::{f20df4e5-ea01-41a2-b02a-dcbd92d4696e}", L"__display__"}, // line 344

Nothing in ResolveUri/ApplyWin11Compat/LaunchTarget ever translates __display__, so it falls into the final else branch of LaunchTarget and runs control.exe __display__ — control.exe doesn't recognize that token, so it opens the Control Panel root (or nothing), not Display settings. Map these to a real target (e.g. L"desk.cpl", matching the other display entries on lines 185-188) or remove them. Easy to miss because the symptom is "wrong page opens," not a crash.

2. Mapping accuracy — the gating concern from last round — is only partly addressed. You trimmed the worst offenders (gaming family, privacy → easeofaccess, bare control.exe redirects), which is good. But the table still collapses whole ms-settings: categories onto a single generic CLSID, so the user lands on a page that isn't what they clicked:

  • every ms-settings:easeofaccess-* (narrator, magnifier, colorfilter, audio, …) → the same Ease of Access Center {D555645E…} (lines 261-276);
  • every ms-settings:signinoptions-* / accounts-* (fingerprint, face, dynamiclock, otherusers, …) → the same User Accounts {60632754…} (lines 234-249) — classic User Accounts has no fingerprint/face page;
  • every ms-settings:network-* → Network Connections {8E908FC9…} (lines 218-225);
  • ms-settings:storagesense / storagepolicies / disksandvolumes / savelocations{20D04FE0…}, which is just This PC (lines 205-208), not any storage UI.

As noted before, for a mod whose entire purpose is accurate redirection, landing on a generic/unrelated page is worse than not redirecting — please prefer fallback-passthrough for subpages that have no real classic equivalent rather than bucketing them onto a parent CLSID. This also ties back to catalog standing: the README still advertises ~5% effectiveness on Windows 11, which is a hard sell for a merged mod. It's worth being honest with yourself about which mappings you've actually verified open the intended page, and dropping the rest.

Since the PR still discloses heavy AI assistance, the coarse category→single-CLSID buckets and the dangling __display__ placeholder both read like AI artifacts — worth a manual pass to confirm each remaining mapping is intentional and verified.

Optional improvements

Minor polish — none of this affects users, so it's your call.

  • Dead mapping entries. uri is only ever populated for strings containing ms-settings: or shell::: (see IsMsSettings/IsShellClsid in the hooks), so the entries keyed L"explorer.exe", L"explorer" (lines 158-159) and L"control desktop" (line 251) can never be looked up. Remove them.
  • Identity shell::: → same shell::: mappings add cost with no benefit. Entries like lines 157, 317, 345-357 map a CLSID to itself, so the hook intercepts the call, spawns a new explorer.exe shell:::{…} process, and returns success — i.e. the same window the caller would have opened anyway, just via an extra process while suppressing the caller's own handling. Letting these fall through to the original (don't list them) is simpler and avoids the redundant spawn.
  • Type-safe hooks / skip the GetProcAddress dance. ShellExecuteExW/ShellExecuteW are ordinary shell32 exports the mod already links against, so you can hook them directly and type-checked: WindhawkUtils::SetFunctionHook(ShellExecuteExW, ShellExecuteExW_hook, &ShellExecuteExW_orig) — no GetModuleHandle/GetProcAddress/void* casts needed (lines 743-768).
  • Wh_GetStringSetting never returns NULL — it returns L"" on unset/error — so the fallbackStr ? guard (lines 92-94) is dead, and an empty value would silently give fallbackMode = 0 (ignore) rather than the intended default 1. WindhawkUtils::StringSetting (RAII) would also drop the manual Wh_FreeStringSetting.
  • README visual. The effect is visible (a classic Control Panel window opening instead of Settings); a short GIF/screenshot would help users. Only i.imgur.com / raw.githubusercontent.com are allowed image hosts.
  • Settings race (still present, low priority). Wh_ModSettingsChanged rewrites g_settings on an arbitrary thread while the hooks read it. The torn reads are harmless scalars and only on a settings change, so it's fine to leave — noting it for completeness.

Functionality notes

Non-critical observations about the feature behavior itself.

  • CreateProcessW is hooked process-wide in explorer for a single narrow case. The matcher is now precise, so the false-positive risk is gone, but every process creation in explorer still pays a tokenization pass just to catch control system. As suggested last round, handling System Properties through the ShellExecute* hooks and leaving CreateProcessW alone would be lighter — your call now that the match is correct.
  • LaunchTarget calls the hooked CreateProcessW, not CreateProcessW_orig. Harmless today because none of the resolved targets tokenize to control system, but it means every redirect re-enters your hook, and the moment a mapping ever resolves to control system it would recurse. Calling CreateProcessW_orig from LaunchTarget would make it robust.
  • Personalization detection is a fuzzy, English-only heuristic. IsPersonalizationWindow treats any CabinetWClass window as the Personalization window (that class is shared by all Explorer/Control Panel windows) and otherwise matches the substring "personaliz" in the title, which only holds on English Windows. So the "Desktop Background → wallpaper" branch can misfire from an unrelated Explorer window or on a localized UI. Reasonable best-effort — just noting the limitation.

@babamohammed2022
Copy link
Copy Markdown
Author

babamohammed2022 commented Jun 4, 2026

Hello @m417z. Regarding the testing methodology: I've tested on Windows 10 21H2 LTSC by opening each URI manually via Start → Run and the ms-settings: protocol, and verified the correct classic window opened. I personally confirmed: personalization (right-click desktop and from inside the window), mouse/touchpad, keyboard, sound, network, firewall, accounts, date/time, devices, printers, UAC prompts for Device Manager/Computer Management/Optional Features, and FallbackMode 0/1/2.
Regarding Windows 11: I don't currently have a Win11 machine available to test on. The last time I tested, redirects were partial as some CLSIDs fired correctly (network, firewall, accounts) and others didn't due to deprecated CLSIDs. I'd rather be honest about this than claim coverage I can't verify. I'm open to repositioning the mod as Windows 10 primary with best-effort Win11 support if that's acceptable.
Regarding CabinetWClass: I understand the limitation, it matches all Explorer windows, not just Personalization. I don't currently know how to make that detection more precise. I'd be happy to accept guidance on a better approach.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants