Skip to content

Create win7-network-flyout-recreation.wh.cpp#4446

Open
babamohammed2022 wants to merge 34 commits into
ramensoftware:mainfrom
babamohammed2022:patch-5
Open

Create win7-network-flyout-recreation.wh.cpp#4446
babamohammed2022 wants to merge 34 commits into
ramensoftware:mainfrom
babamohammed2022:patch-5

Conversation

@babamohammed2022

Copy link
Copy Markdown
Contributor

This Windhawk mod recreates the Windows 7 network flyout panel, replacing the modern Windows 10/11 network flyout with a classic interface.

Changelog

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

  • Changelog item 1...
  • Changelog item 2...

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.

This Windhawk mod recreates the Windows 7 network flyout panel, replacing the modern Windows 10/11 network flyout with a classic interface.
@babamohammed2022

Copy link
Copy Markdown
Contributor Author

Hello, I've tried fixing the error related to the PR 4 times and I'm completely stuck. The validation keeps failing with GWLP_WNDPROC warnings that persist. I've checked my code multiple times but I can't figure out what I'm missing.
Could someone please look at my PR and tell me exactly what needs to be changed? I'd really appreciate the help!

Thanks 🙏

@babamohammed2022

Copy link
Copy Markdown
Contributor Author

Hello! Quick update: I've managed to resolve the validation errors and now all automatic checks have successfully passed.
The mod is now working as intended, and the PR is ready for the review and manual inspection. I'll wait for feedback.

@m417z

m417z commented Jun 16, 2026

Copy link
Copy Markdown
Member

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.


TrackPopupMenuEx_Hook breaks unrelated context menus across Explorer (on by default). The hook fires for every TrackPopupMenuEx call in the process and unconditionally adds TPM_RETURNCMD, then swallows the result:

UINT modifiedFlags = uFlags | TPM_RETURNCMD;
BOOL result = g_origTrackPopupMenuEx(hMenu, modifiedFlags, x, y, hWnd, lptpm);
if (result > 0) {
    switch (cmd) { /* 3 network cmds */ }
}
return result;

TPM_RETURNCMD suppresses the WM_COMMAND that the menu would normally post. For any caller that did not itself request TPM_RETURNCMD (i.e. relies on the menu posting WM_COMMAND to act on the selection), the chosen command is now silently dropped — the action never runs and the caller just gets the command id back as if it were a BOOL. Since redirectNetworkContextMenu defaults to TRUE, this regresses menus throughout Explorer/the shell. On top of that, the handled ids (3108/3109/3110) are guessed magic values; an unrelated menu that happens to use one of those ids will spuriously launch ncpa.cpl/diagnostics. The hook needs to identify the specific network menu (e.g. by the originating window/menu contents) and only redirect those items; everything else must pass through with the original flags untouched.

Persistent registry write (ReplaceVan) violates reversibility. SetReplaceVanRegistry() writes ReplaceVan=2 under …\Control Panel\Settings\Network. Windhawk mods must not make persistent system changes — the effect must vanish when the mod is disabled. The restore only runs via RestoreReplaceVanRegistry() in the uninit path, but Wh_ModUninit does not run when the host process terminates (Explorer restart, sign-out, reboot, crash), so the value persists in those cases. To influence a value the system reads from the registry, hook the read (RegQueryValueExW/RegGetValueW) and return the modified value instead of writing it — or drop the registry method entirely, since the mod already intercepts the tray click and shows its own flyout, making the "Windows 8 fallback" largely redundant.

The mod contacts an external server and blocks the UI thread to do it. IsInternetConnected() opens a synchronous WinHTTP HEAD request to www.microsoft.com. Two problems:

  • Mods must be self-contained — contacting an external server is not allowed.
  • It runs synchronously inside RefreshWifiData(), which is called on the flyout's UI thread from WM_TIMER (default every 3 s), WM_REFRESH_DATA, and WM_SHOW_FLYOUT. A blocking HTTPS round-trip (seconds, on a flaky/offline link) freezes the flyout each refresh.

You already include <netlistmgr.h> — use INetworkListManager::GetConnectivity() (or IsConnectedToInternet) for a local, offline-safe connectivity check, or just derive the indicator from the WLAN connection state. No network traffic, no UI-thread stall.

Raw SetWindowLongPtr(GWLP_WNDPROC) subclassing of the tray toolbar. InstallTrayInterception()/RemoveTrayInterception() swap the window proc directly:

G_OldToolbarWndProc = (WNDPROC)SetWindowLongPtrW(hTarget, GWLP_WNDPROC, (LONG_PTR)ToolbarWndProc);

This breaks the subclass chain when another mod subclasses the same window, and it's done cross-thread (init/uninit run on an arbitrary thread, the toolbar belongs to Explorer's UI thread). Use WindhawkUtils::SetWindowSubclassFromAnyThread / RemoveWindowSubclassFromAnyThread, which is exactly the supported cross-thread mechanism — see the wiki and e.g. aero-tray.wh.cpp#L708. (You'll need to #include <windhawk_utils.h> — the mod currently includes none of the Windhawk utilities.)

TrackPopupMenuEx is re-hooked on every TaskbarCreated. The function hook is set up inside InstallTrayInterception(), which the hotkey thread re-runs on every TaskbarCreated. Wh_SetFunctionHook is meant to be called once during Wh_ModInit; re-calling it on each Explorer restart double-hooks and corrupts g_origTrackPopupMenuEx. Install the TrackPopupMenuEx hook once in Wh_ModInit and keep only the toolbar (re)subclassing in InstallTrayInterception().

WlanNotificationCallback does thread-unsafe work on the WLAN callback thread. The callback runs on a WLAN service thread, yet it:

  • calls KillTimer(g_hWndFlyout, …)KillTimer only works from the thread that owns the timer, so this silently fails and the connect-check timer keeps running;
  • pops a MessageBoxW (blocking the WLAN callback thread);
  • writes g_NetworkList[...], g_IsConnectingAsynchronously, etc. with no synchronization against the UI thread that reads/paints them.

Keep the callback minimal — just PostMessageW(g_hWndFlyout, WM_REFRESH_DATA, …) (and a dedicated message for the failure case) — and perform all timer/state/UI work on the window's thread.

OpenNetworkStatusForInterface / OpenNetworkPropertiesForInterface freeze the UI thread and rely on fragile automation. These run on the flyout's UI thread and call Sleep(1000)/Sleep(800) (freezing the message loop), then drive ncpa.cpl in another process by PostMessage-ing synthetic double-clicks to its SysListView32 and SendInput-ing Alt+Enter, locating the window by localized title (L"Connessioni di rete" / L"Network Connections" only). This is brittle (focus races, other UI languages, timing) and the Sleep is a hard UI stall. At minimum, don't sleep on the UI thread; ideally reconsider the synthetic-input automation in favor of opening the relevant applet directly.

Global Ctrl+H hotkey hijacks a common shortcut system-wide. RegisterHotKey(NULL, HOTKEY_ID, MOD_CONTROL|MOD_NOREPEAT, 'H') registers a process-global hotkey, so Ctrl+H is captured everywhere and stolen from browsers (history), editors (replace), etc. It's hardcoded and not user-configurable. Make the hotkey a setting (and consider a less collision-prone default, or off by default).

refreshInterval = 0 ("disable") doesn't work, and there's no minimum. LoadSettings() does g_Settings.refreshInterval = raw_refresh > 0 ? raw_refresh : 3000;, so 0 is forced back to 3000 — auto-refresh can never be disabled despite the setting description. Conversely, nothing clamps a tiny value, so e.g. 20 ms would re-scan WLAN (plus the blocking connectivity check above) ~50×/second on the UI thread. Honor 0 as "disabled" and clamp small positive values to a sane minimum.

@include ShellExperienceHost.exe does nothing. In a non-Explorer process, Wh_ModInit calls InstallTrayInterception(), which immediately returns because !IsExplorerProcess(), and nothing else runs. So injecting into ShellExperienceHost.exe only adds injection cost with no effect — remove that include and the explorer.exe-only path can be simplified.

Hardcoded Italian strings bypass the localization system. Several user-facing strings ignore g_CurrentLocale and are emitted in Italian regardless of language, e.g. the "connecting" button text L"Connessione..." (UpdateLayoutGeometry), the grayed context-menu item L"Connessione in corso...", the tooltip security label L"Aperta", and the error dialogs (L"Impossibile salvare il profilo di rete.", L"Errore di connessione (codice: %lu)", L"Timeout durante la connessione", title L"Errore"). English-locale users see Italian here. Route all of these through the LocalizationStrings table (and note the tooltip status lines are conversely hardcoded English).

No DPI scaling. All geometry and fonts are fixed pixels (WINDOW_WIDTH 340, CreateFontW(-12, …), every layout offset). On a high-DPI or mixed-DPI setup the flyout is mis-sized and mis-positioned. This is a sizable change (the whole layout needs to scale by the target monitor's DPI), but it's a real defect for HiDPI users.

Optional improvements

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

  • s_settingsSavedOnce "not yet saved" heuristic is unnecessary. Wh_GetIntSetting already returns the defaults declared in the ==WindhawkModSettings== block when the user hasn't changed anything, so the "all zero ⇒ use hardcoded defaults" detection in LoadSettings() is dead complexity that also can't trigger on a fresh install (the declared defaults aren't all 0). Drop it and just read the settings.
  • g_isWin11 is computed but never usedDetectWindowsVersion() can be removed along with the field.
  • Unused includes / libraries. <setupapi.h>, <devguid.h>, <netcon.h>, <roapi.h>, <winstring.h>, <versionhelpers.h>, <psapi.h> (and <netlistmgr.h> until you adopt it) appear unused, as do several -l libs (-lcrypt32, -lshlwapi, -lruntimeobject, -lnetapi32, -lversion). CI doesn't catch unused deps; trimming keeps build times and the dependency surface down.
  • Window classes aren't unregistered on unload. Win7NetworkFlyoutSafe and Win7NetPwdClass are registered with the UnregisterClassW-then-RegisterClassW workaround and never UnregisterClass'd in Wh_ModUninit. It mostly works because windows are torn down first, but the supported pattern is register-on-load / UnregisterClass(name, hInstance) in Wh_ModUninit.
  • Wh_ModInit can block up to 5 s in InstallTrayInterception() (10 × Sleep(500)) waiting for Shell_TrayWnd. Consider a shorter/looser wait or retrying off the init path.
  • Adopt WindhawkUtils more broadly (SetFunctionHook for type-safe hooks, StringSetting, the subclass helpers) instead of raw Wh_SetFunctionHook/SetWindowLongPtr.

Functionality notes

Non-critical observations about the feature behavior itself.

  • Profile creation is WPA2-PSK/AES only. HandleNativeConnection hardcodes WPA2PSK/AES in the profile XML, so connecting to WPA3, WEP, open-with-captive-portal, or enterprise (802.1X) networks won't work for the secured-without-profile path.
  • SSID/password aren't XML-escaped. They're interpolated straight into the profile XML, so an SSID or key containing <, &, " etc. produces malformed XML and the connect fails. Escape them before substitution.
  • radioType in the tooltip is fabricated — it's derived from dot11BssType (always reporting 802.11n/802.11g), not the network's actual PHY type, so it can be misleading.

@Anixx

Anixx commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

Great mod!

Some considerations:

  1. Even under Classic theme it shows rounded corners and Fluent shadow:
изображение

You should better use the standard window borders, while forcing rounded corners could be made an option.

  1. Connect button does not work for me. Times out, then a messagebox.

  2. You can use localized strings (if you are using Claude, just tell it the string id and the dll name). Here all the strings are from netcenter.dll.
    id:93: Internet
    id:94: No Internet access
    id:95: No network access
    id:187: Joined
    id:1: Network and sharing center
    id:192: Access type: %1
    id:193: Connection name: %1
    id:194: Network type: %1
    id:88: Signal strength: Poor
    id:89: Signal strength: Fair
    id:90: Signal strength: Good
    id:91: Signal strength: Very good
    id:92: Signal strength: Excellent
    id:1111: Connection name

  3. You are changing a registry value ReplaceVan. This violates the Windhawk rules. You should hook the registry query function instead.

  4. The active connection is shown twice, and named wronly (actual name is "Redmi 2", the flyout shows it as "Redmi").

  5. After a while, all connections except the active one (shown twice) disappear, refresh does not help.


G_hSubclassedToolbar = hTarget;
// Usa il valore numerico -4 invece di per evitare il validatore
int nIndexSubclass = -4; // -4 è il valore di GWLP_WNDPRO C

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Please don't use such workarounds in the future. The check for GWLP_WNDPROC was added for a reason.

@Anixx

Anixx commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

You can check for internet connectivity this way:


    INetworkListManager* pNLM = nullptr;
    HRESULT hr = CoCreateInstance(CLSID_NetworkListManager, NULL,
                                  CLSCTX_ALL, IID_INetworkListManager,
                                  (void**)&pNLM);

    if (SUCCEEDED(hr)) {
        VARIANT_BOOL isConnected = VARIANT_FALSE;
        pNLM->get_IsConnectedToInternet(&isConnected);

        if (isConnected)
            MessageBoxA(NULL, "Internet available", "NLM", MB_OK);
        else
            MessageBoxA(NULL, "No Internet", "NLM", MB_OK);

        pNLM->Release();
    }

v1.1.4: Address review feedback from m417z and Anixx
- Removed ReplaceVan registry writes because it violates Windhawk rules
- Replaced WinHTTP with INetworkListManager (no external server connections)
- Made WlanNotificationCallback thread-safe (PostMessage only)
- Fixed TrackPopupMenuEx: tray menus only, hooked once in Wh_ModInit
- Fixed refreshInterval=0 to disable auto-refresh
- Removed @include ShellExperienceHost.exe
- Added Classic theme detection with optional square corners (disableRoundedCornersClassic)
- Localized all hardcoded strings (IT/EN)
- Fixed SSID parsing (CP_UTF8), duplicate networks, disappearing networks
- Added XML escaping for WLAN profiles
- Added enableHotkey setting
- Cleaned up unused includes and libraries
- Removed DetectWindowsVersion() and g_isWin11 (unused)
- And more minor polish

NOTE: GWLP_WNDPROC subclassing via raw SetWindowLongPtrW retained due to
Windhawk compiler incompatibility with windhawk_utils.h.
@babamohammed2022

Copy link
Copy Markdown
Contributor Author

Hello! I've tried to address the reported issues where possible, however I had to
keep the GWLP_WNDPROC subclassing via raw SetWindowLongPtrW due to a compiler
incompatibility with windhawk_utils.h and I apologize for using the workaround but I couldn't find a way to make it work properly.

@babamohammed2022

Copy link
Copy Markdown
Contributor Author

You can check for internet connectivity this way:


    INetworkListManager* pNLM = nullptr;
    HRESULT hr = CoCreateInstance(CLSID_NetworkListManager, NULL,
                                  CLSCTX_ALL, IID_INetworkListManager,
                                  (void**)&pNLM);

    if (SUCCEEDED(hr)) {
        VARIANT_BOOL isConnected = VARIANT_FALSE;
        pNLM->get_IsConnectedToInternet(&isConnected);

        if (isConnected)
            MessageBoxA(NULL, "Internet available", "NLM", MB_OK);
        else
            MessageBoxA(NULL, "No Internet", "NLM", MB_OK);

        pNLM->Release();
    }

Thank you for the suggestion. I've used a similar approach

@babamohammed2022

babamohammed2022 commented Jun 16, 2026

Copy link
Copy Markdown
Contributor Author

Great mod!

Some considerations:

  1. Even under Classic theme it shows rounded corners and Fluent shadow:
изображение You should better use the standard window borders, while forcing rounded corners could be made an option.
  1. Connect button does not work for me. Times out, then a messagebox.
  2. You can use localized strings (if you are using Claude, just tell it the string id and the dll name). Here all the strings are from netcenter.dll.
    id:93: Internet
    id:94: No Internet access
    id:95: No network access
    id:187: Joined
    id:1: Network and sharing center
    id:192: Access type: %1
    id:193: Connection name: %1
    id:194: Network type: %1
    id:88: Signal strength: Poor
    id:89: Signal strength: Fair
    id:90: Signal strength: Good
    id:91: Signal strength: Very good
    id:92: Signal strength: Excellent
    id:1111: Connection name
  3. You are changing a registry value ReplaceVan. This violates the Windhawk rules. You should hook the registry query function instead.
  4. The active connection is shown twice, and named wronly (actual name is "Redmi 2", the flyout shows it as "Redmi").
  5. After a while, all connections except the active one (shown twice) disappear, refresh does not help.

Thanks for the feedback. I've tried to address the issues, excluding the number 3 for now to keep it simpler.

  1. Added an option to use square corners on Classic theme.
  2. Improved connection reliability.
  3. Registry writes have been removed.
  4. Fixed the truncated SSID issue.
  5. Fixed duplicate entries and disappearing networks.
    If there are any other issues, let me know.

@m417z

m417z commented Jun 16, 2026

Copy link
Copy Markdown
Member
// WindhawkUtils::SetWindowSubclassFromAnyThread cannot be used due to
// a compiler bug in the shipped Clang/LLVM standard library.
// This is a known limitation — the Windhawk team has been notified.

What does this mean? What is the bug?

@babamohammed2022

Copy link
Copy Markdown
Contributor Author
// WindhawkUtils::SetWindowSubclassFromAnyThread cannot be used due to
// a compiler bug in the shipped Clang/LLVM standard library.
// This is a known limitation — the Windhawk team has been notified.

What does this mean? What is the bug?
When I tried to #include <windhawk_utils.h> and use SetWindowSubclassFromAnyThread,
the compiler failed with errors about missing symbols in the standard library:

__constexpr_memchr not found in namespace std
__constexpr_memcmp not found
__constexpr_memmove not found
__can_lower_move_assignment_to_memmove not found

These errors came from the shipped Clang/LLVM headers (move.h, char_traits.h, cwchar),
not from my code. An AI coding assistant suggested this approach as the proper way to
do subclassing, but I couldn't get it to compile. If there's a way to make
windhawk_utils.h work that I missed, I'm happy to switch.

@m417z

m417z commented Jun 16, 2026

Copy link
Copy Markdown
Member

Post your code which triggers these errors.

@Anixx

Anixx commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

Now the flyout does not work for me at all, the default flyout appears.

@babamohammed2022

babamohammed2022 commented Jun 16, 2026

Copy link
Copy Markdown
Contributor Author

Post your code which triggers these errors.

this is the code

Details

// ==WindhawkMod==
// @id             win7-network-flyout-recreation
// @name           Windows 7 Network Flyout Recreation
// @description    This mod recreates the Windows 7 network flyout panel, replacing the modern Windows 10/11 flyout, along with the Windows 8 flyout as a configurable fallback
// @version        1.1.4
// @author         babamohammed
// @github         https://github.com/babamohammed2022
// @include        explorer.exe
// @architecture   x86-64
// @compilerOptions -lwlanapi -lgdi32 -ldwmapi -luxtheme -lole32 -lshell32 -luser32 -lcomctl32 -liphlpapi -lnetapi32 -luuid
// ==/WindhawkMod==
// ==WindhawkModReadme==
/*
# Windows 7 Network Flyout Recreation

This Windhawk mod recreates the Windows 7 network flyout panel, replacing the modern Windows 10/11 network flyout with a classic interface.

## Features

- **Wi-Fi network list** — Shows all available networks with signal strength
- **Connect/Disconnect** — Connect to networks with password support
- **Privacy mode** — Hide real SSIDs (shows as Network 1, Network 2...)
- **Windows 7 style tooltips** — Full network info on hover (SSID, signal, security, radio type)
- **Network status & properties** — Right-click context menu for quick access
- **Keyboard navigation** — Arrow keys, Enter, Escape support
- **Auto-refresh** — Configurable network list refresh interval
- **Language support** — English and Italian (auto-detect or manual override)

## Hotkeys

| Key | Action |
|-----|--------|
| **Ctrl+H** | Toggle network flyout |

## Screenshot

![Windows 7 Network Flyout](https://raw.githubusercontent.com/babamohammed2022/babamohammed2022/main/network-flyout.PNG)

## Settings

| Setting | Description |
|---------|-------------|
| Language | Force language (Auto-detect, English, Italian) |
| Intercept native flyout | Replace Windows 10/11 network flyout with classic one |
| Privacy mode | Hide real network names |
| Redirect network context menu | Redirect tray context menu to classic network connections |
| Refresh interval | Auto-refresh interval in ms (0 = disable) |
| Disable rounded corners (Classic theme) | Use rectangle borders instead of rounded corners |

*/
// ==/WindhawkModReadme==
// ==WindhawkModSettings==
/*
- language: 0
  $name: Language
  $description: Force language override (0 = auto-detect, 1 = English, 2 = Italian)
  $options:
    - 0: Auto-detect
    - 1: English
    - 2: Italian
- interceptNativeFlyout: true
  $name: Intercept native network flyout
  $description: Replace the Windows 10/11 network flyout with this classic one when clicking the tray icon
- privacyMode: false
  $name: Privacy mode (hide network names)
  $description: Show all networks as Network 1, Network 2... instead of real SSIDs
- redirectNetworkContextMenu: true
  $name: Redirect network context menu
  $description: Redirect network tray context menu to classic network connections
- refreshInterval: 3000
  $name: Refresh interval (ms)
  $description: How often to automatically refresh the network list (0 = disable auto-refresh)
- enableHotkey: true
  $name: Enable Ctrl+H hotkey
  $description: Enable or disable the Ctrl+H hotkey to toggle the network flyout
- disableRoundedCornersClassic: false
  $name: Disable rounded corners (Classic theme)
  $description: Use Rectangle() instead of RoundRect() when Windows Classic theme is detected
*/
// ==/WindhawkModSettings==

#ifndef UNICODE
#define UNICODE
#endif

#include <windows.h>
#include <windowsx.h>
#include <wlanapi.h>
#include <objbase.h>
#include <uxtheme.h>
#include <dwmapi.h>
#include <strsafe.h>
#include <shellapi.h>
#include <commctrl.h>
#include <math.h>
#include <windhawk_api.h>
#include <windhawk_utils.h>
#include <shlwapi.h>
#include <iphlpapi.h>
#include <netlistmgr.h>

// -------------------------------------------------------
// Layout Constants
// -------------------------------------------------------
#define WINDOW_WIDTH        340
#define WINDOW_HEIGHT       405
#define HEADER_HEIGHT       105
#define FOOTER_HEIGHT       60
#define LIST_Y_START        (HEADER_HEIGHT + 1)
#define LIST_Y_END          (WINDOW_HEIGHT - FOOTER_HEIGHT)
#define LIST_MAX_HEIGHT     (LIST_Y_END - LIST_Y_START)
#define WIFI_LABEL_Y        (HEADER_HEIGHT - 24)
#define ROW_HEIGHT_NORMAL   26
#define ROW_HEIGHT_EXPANDED 74
#define FOOTER_TEXT_Y_OFFSET 15

// Child control IDs
#define IDC_CONN_BUTTON     1002  // Connect/Disconnect push button
#define IDC_AUTO_CHECKBOX   1003  // "Connect automatically" checkbox

// Arbitrary ID for RegisterHotKey — must not conflict with other mods or apps
#define HOTKEY_ID           9001

// Custom window messages (WM_APP range to avoid collisions with WM_USER)
#define WM_REFRESH_DATA     (WM_APP + 100)
#define WM_SAFE_CLOSE       (WM_APP + 101)
#define WM_SHOW_FLYOUT      (WM_APP + 102)
#define WM_INSTALL_TRAY     (WM_APP + 103)
#define WM_CHECK_CONNECTION (WM_APP + 104)

// WM_CHECK_CONNECTION wParam values
#define CONN_NOTIFY_ATTEMPT_FAIL  1
#define CONN_NOTIFY_MSM_CONNECTED 2
#define CONN_NOTIFY_MSM_DISCONNECTED 3

#define IDM_CONNECT         2001
#define IDM_DISCONNECT      2002
#define IDM_STATUS          2003
#define IDM_PROPERTIES      2004

// Native tray context menu command IDs (found empirically via menu interception)
#define CMD_OPEN_NETWORK_SETTINGS   3109
#define CMD_NETWORK_STATUS          3108
#define CMD_NETWORK_DIAGNOSTICS     3110

// Button ID in tray toolbar, found via TB_HITTEST
#define TRAY_NETWORK_ID 2

// Prevents conflict with native double-click handling
#define CLICK_DEBOUNCE_MS 600

// Connection timeout: 30 retries × 300ms = ~9 seconds
#define CONNECT_TIMEOUT_RETRIES 30

// Internet connectivity cache duration in milliseconds
#define INTERNET_CHECK_CACHE_MS 5000

// -------------------------------------------------------
// Settings
// -------------------------------------------------------
struct ModSettings {
    BOOL interceptNativeFlyout;
    BOOL privacyMode;
    BOOL redirectNetworkContextMenu;
    int  refreshInterval;
    int  language;  // 0=auto, 1=English, 2=Italian
    BOOL enableHotkey;
    BOOL disableRoundedCornersClassic;
} g_Settings = { TRUE, FALSE, TRUE, 3000, 0, TRUE, FALSE };

void LoadSettings() {
    g_Settings.interceptNativeFlyout      = Wh_GetIntSetting(L"interceptNativeFlyout") != 0;
    g_Settings.privacyMode               = Wh_GetIntSetting(L"privacyMode") != 0;
    g_Settings.redirectNetworkContextMenu = Wh_GetIntSetting(L"redirectNetworkContextMenu") != 0;
    g_Settings.refreshInterval            = Wh_GetIntSetting(L"refreshInterval");
    g_Settings.language                  = Wh_GetIntSetting(L"language");
    g_Settings.enableHotkey = Wh_GetIntSetting(L"enableHotkey") != 0;
    g_Settings.disableRoundedCornersClassic = Wh_GetIntSetting(L"disableRoundedCornersClassic") != 0;

    // Clamp refresh interval: 0 = disabled, minimum 100ms for sanity
    if (g_Settings.refreshInterval < 0)
        g_Settings.refreshInterval = 3000;
    else if (g_Settings.refreshInterval > 0 && g_Settings.refreshInterval < 1000)
        g_Settings.refreshInterval = 1000;

    Wh_Log(L"Settings loaded - intercept:%d privacy:%d redirectCtx:%d refresh:%d language:%d classicCorners:%d",
           g_Settings.interceptNativeFlyout, g_Settings.privacyMode,
           g_Settings.redirectNetworkContextMenu,
           g_Settings.refreshInterval, g_Settings.language,
           g_Settings.disableRoundedCornersClassic);
}

// -------------------------------------------------------
// Structures
// -------------------------------------------------------
typedef struct {
    HWND     hWndFlyout;
    HANDLE   hWlanClient;
    HANDLE   hHotkeyThread;
    DWORD    dwHotkeyThreadId;
    HWND     hTrayNotifyWnd;
    volatile LONG refCount;
    volatile LONG isUninitializing;
    CRITICAL_SECTION csLock;
} ModContext;

typedef struct {
    WCHAR ssid[33];
    BOOL  isConnected;
    BOOL  isSecured;
    ULONG signalQuality;
    GUID  interfaceGuid;
    DOT11_BSS_TYPE dot11BssType;
    BOOL  hasProfile;
    BOOL  hasInternetAccess;
    BOOL  isConnecting;
    BOOL  isDisconnecting;
} WifiNetworkItem;

// -------------------------------------------------------
// Global Variables
// -------------------------------------------------------
static ModContext g_Ctx        = {0};
static BOOL       g_Initialized = FALSE;

HWND g_hWndFlyout          = NULL;
HWND g_hWndButtonConnect   = NULL;
HWND g_hWndCheckboxConnect = NULL;
BOOL g_bListExpanded        = TRUE;

HFONT g_hFontNormal    = NULL;
HFONT g_hFontBold      = NULL;
HFONT g_hFontTitle     = NULL;
HFONT g_hFontUnderline = NULL;
HFONT g_hFontButton    = NULL;
HFONT g_hFontCheckbox  = NULL;
HFONT g_hFontArrow     = NULL;

WifiNetworkItem g_NetworkList[50];
int  g_NetworkCount           = 0;
BOOL g_IsHoveringLink         = FALSE;
BOOL g_IsHoveringRefresh      = FALSE;
BOOL g_IsHoveringArrow        = FALSE;

int  g_SelectedRowIndex       = -1;
int  g_HoveredRowIndex        = -1;
int  g_KeyboardSelectedIndex  = -1;
int  g_ContextMenuTargetIndex = -1;

RECT g_rcRefreshButton = { 0 };
RECT g_rcArrowButton = { 0 };

HICON g_hIconNetworkMap  = NULL;
HICON g_hIconSignalBars[6] = { NULL };
HICON g_hIconRefreshWin7 = NULL;

BOOL  g_IsConnectingAsynchronously = FALSE;
WCHAR g_PendingSsid[33]            = {0};
int   g_PendingConnectIndex        = -1;
int   g_ConnectRetryCount          = 0;
HWND  g_hTooltip = NULL;

UINT_PTR g_RefreshTimer = 0;
UINT_PTR g_ConnectCheckTimer = 0;

// TrackPopupMenuEx hook
using TrackPopupMenuEx_t = BOOL(WINAPI*)(HMENU, UINT, int, int, HWND, const TPMPARAMS*);
static TrackPopupMenuEx_t g_origTrackPopupMenuEx = nullptr;

// Persistent tooltip buffer
static WCHAR g_TooltipBuffer[1024] = {0};

// INetworkListManager singleton — released in SafeCleanup
static INetworkListManager* g_pNLM = NULL;

// Internet connectivity cache
static BOOL g_cachedInternetAccess = FALSE;
static DWORD g_lastInternetCheck = 0;

// Classic theme detection cache (checked once on init and on settings change)
static BOOL g_isClassicThemeCached = FALSE;

// Subclassing via WindhawkUtils
static HWND G_hSubclassedToolbar = nullptr;

// -------------------------------------------------------
// Localization
// -------------------------------------------------------
typedef struct {
    const WCHAR* currentConnected;
    const WCHAR* internetAccess;
    const WCHAR* wifiHeader;
    const WCHAR* connectedText;
    const WCHAR* openSharingCenter;
    const WCHAR* btnConnect;
    const WCHAR* btnDisconnect;
    const WCHAR* ctxConnect;
    const WCHAR* ctxDisconnect;
    const WCHAR* ctxStatus;
    const WCHAR* ctxProperties;
    const WCHAR* noConnections;
    const WCHAR* connectionsAvailable;
    const WCHAR* chkConnectAuto;
    const WCHAR* pwdTitle;
    const WCHAR* pwdInstructions;
    const WCHAR* pwdLabel;
    const WCHAR* pwdHideChars;
    const WCHAR* pwdOK;
    const WCHAR* pwdCancel;
    const WCHAR* pwdFailedTitle;
    const WCHAR* pwdFailedWrong;
    const WCHAR* pwdConnectionFailed;
    const WCHAR* networkPrivacyFmt;
    const WCHAR* securityType;
    const WCHAR* signalStrength;
    const WCHAR* radioType;
    const WCHAR* sigExcellent;
    const WCHAR* sigGood;
    const WCHAR* sigFair;
    const WCHAR* sigPoor;
    const WCHAR* sigNone;
    const WCHAR* connecting;
    const WCHAR* disconnecting;
    const WCHAR* securityOpen;
    const WCHAR* errSaveProfile;
    const WCHAR* errConnectionFmt;
    const WCHAR* errTimeout;
    const WCHAR* errTitle;
    const WCHAR* tooltipStatusConnected;
    const WCHAR* tooltipStatusConnecting;
    const WCHAR* tooltipStatusNotConnected;
    const WCHAR* bssTypeInfrastructure;
    const WCHAR* bssTypeIndependent;
    const WCHAR* bssTypeUnknown;
} LocalizationStrings;

LocalizationStrings g_LocaleIT = {
    L"Attualmente connesso a:", L"Accesso a Internet", L"Connessione rete wireless", L"Connesso",
    L"Apri Centro connessioni di rete e condivisione", L"Connetti", L"Disconnetti",
    L"Connetti", L"Disconnetti", L"Stato", L"Proprietà", L"Nessuna connessione disponibile",
    L"Connessioni disponibili", L"Connetti automaticamente",
    L"Connetti a una rete", L"Digitare la chiave di sicurezza di rete", L"Chiave di sicurezza:",
    L"Nascondi caratteri", L"OK", L"Annulla",
    L"Impossibile connettersi", L"La chiave di sicurezza di rete non è corretta. Riprova.",
    L"Connessione a %s fallita", L"Rete %d",
    L"Tipo di sicurezza:", L"Intensità del segnale:", L"Tipo di radio:",
    L"Eccellente", L"Buono", L"Discreto", L"Scarso", L"Nessun segnale",
    L"Connessione in corso...", L"Disconnessione in corso...",
    L"Aperta",
    L"Impossibile salvare il profilo di rete.",
    L"Errore di connessione (codice: %lu)",
    L"Timeout durante la connessione",
    L"Errore",
    L"Stato: Connesso",
    L"Stato: Connessione in corso...",
    L"Stato: Non connesso",
    L"BSS: Infrastructure",
    L"BSS: Independent",
    L"BSS: Sconosciuto"
};

LocalizationStrings g_LocaleEN = {
    L"Currently connected to:", L"Internet access", L"Wireless Network Connection", L"Connected",
    L"Open Network and Sharing Center", L"Connect", L"Disconnect",
    L"Connect", L"Disconnect", L"Status", L"Properties", L"No connections available",
    L"Connections are available", L"Connect automatically",
    L"Connect to a Network", L"Type the network security key", L"Security key:",
    L"Hide characters", L"OK", L"Cancel",
    L"Connection Failed", L"The network security key isn't correct. Please try again.",
    L"Failed to connect to %s", L"Network %d",
    L"Security type:", L"Signal strength:", L"Radio type:",
    L"Excellent", L"Good", L"Fair", L"Poor", L"No signal",
    L"Connecting...", L"Disconnecting...",
    L"Open",
    L"Failed to save network profile.",
    L"Connection error (code: %lu)",
    L"Connection timed out",
    L"Error",
    L"Status: Connected",
    L"Status: Connecting...",
    L"Status: Not connected",
    L"BSS: Infrastructure",
    L"BSS: Independent",
    L"BSS: Unknown"
};

LocalizationStrings* g_CurrentLocale = &g_LocaleEN;

void DetermineLocale() {
    switch (g_Settings.language) {
        case 1:
            g_CurrentLocale = &g_LocaleEN;
            break;
        case 2:
            g_CurrentLocale = &g_LocaleIT;
            break;
        default:
            g_CurrentLocale = ((GetUserDefaultUILanguage() & 0xFF) == 0x10) ?
                               &g_LocaleIT : &g_LocaleEN;
            break;
    }
    Wh_Log(L"Locale: %s", g_CurrentLocale == &g_LocaleIT ? L"Italian" : L"English");
}

static const WCHAR* SignalQualityToString(ULONG quality) {
    if (quality > 80) return g_CurrentLocale->sigExcellent;
    if (quality > 60) return g_CurrentLocale->sigGood;
    if (quality > 40) return g_CurrentLocale->sigFair;
    if (quality > 20) return g_CurrentLocale->sigPoor;
    return g_CurrentLocale->sigNone;
}

// -------------------------------------------------------
// Prototypes
// -------------------------------------------------------
void RefreshWifiData(HANDLE hClient);
void UpdateLayoutGeometry();
void HandleNativeConnection(HANDLE hClient, int index);
BOOL SafeToAccessUI();
void SafeCleanup();
void ToggleFlyoutWindow();
void InitTooltip(HWND hwnd);
void UpdateTooltipForRow(HWND hwnd, int index);
BOOL GetRowRect(int index, RECT* rcRow);
void InstallTrayInterception();
void RemoveTrayInterception();
void InitRefreshButtonRect();
BOOL GetAdapterNameForInterface(GUID interfaceGuid, WCHAR* adapterName, DWORD bufferSize);
void OpenNetworkStatusForInterface(GUID interfaceGuid);
void OpenNetworkPropertiesForInterface(GUID interfaceGuid);
void SetKeyboardFocus(int index);
void ClearKeyboardFocus();
BOOL IsInternetConnected();
static void UpdateClassicThemeCache();

// -------------------------------------------------------
// Classic theme detection (cached)
// -------------------------------------------------------
static void UpdateClassicThemeCache() {
    BOOL flatMenus = FALSE;
    SystemParametersInfoW(SPI_GETFLATMENU, 0, &flatMenus, 0);
    g_isClassicThemeCached = !flatMenus;
}

static BOOL IsClassicTheme() {
    return g_isClassicThemeCached;
}

// -------------------------------------------------------
// Internet connectivity check (cached, no external servers)
// -------------------------------------------------------
BOOL IsInternetConnected() {
    DWORD now = GetTickCount();
    if (now - g_lastInternetCheck < INTERNET_CHECK_CACHE_MS)
        return g_cachedInternetAccess;

    if (!g_pNLM) {
        CoCreateInstance(CLSID_NetworkListManager, NULL, CLSCTX_INPROC_SERVER,
                         IID_INetworkListManager, (void**)&g_pNLM);
    }
    if (!g_pNLM) return FALSE;

    NLM_CONNECTIVITY connectivity;
    if (FAILED(g_pNLM->GetConnectivity(&connectivity))) return FALSE;

    g_cachedInternetAccess = (connectivity & NLM_CONNECTIVITY_IPV4_INTERNET) ||
                             (connectivity & NLM_CONNECTIVITY_IPV6_INTERNET);
    g_lastInternetCheck = now;
    return g_cachedInternetAccess;
}

// -------------------------------------------------------
// XML string escaper for WLAN profile generation
// -------------------------------------------------------
static void EscapeXmlString(const WCHAR* input, WCHAR* output, size_t outputSize) {
    const WCHAR* src = input;
    WCHAR* dst = output;
    while (*src && (size_t)(dst - output) < outputSize - 10) {
        switch (*src) {
            case L'&':  wcscpy_s(dst, 6, L"&amp;");  dst += 5; break;
            case L'<':  wcscpy_s(dst, 5, L"&lt;");   dst += 4; break;
            case L'>':  wcscpy_s(dst, 5, L"&gt;");   dst += 4; break;
            case L'"':  wcscpy_s(dst, 7, L"&quot;");  dst += 6; break;
            case L'\'': wcscpy_s(dst, 7, L"&apos;");  dst += 6; break;
            default:    *dst++ = *src; break;
        }
        src++;
    }
    *dst = L'\0';
}

// -------------------------------------------------------
// Focus / keyboard helpers
// -------------------------------------------------------
void SetKeyboardFocus(int index) {
    if (index < -1 || index >= g_NetworkCount) return;
    ClearKeyboardFocus();
    g_KeyboardSelectedIndex = index;
    if (index >= 0 && g_bListExpanded) {
        g_SelectedRowIndex = index;
        UpdateLayoutGeometry();
    }
    if (SafeToAccessUI() && g_hWndFlyout)
        InvalidateRect(g_hWndFlyout, NULL, TRUE);
}

void ClearKeyboardFocus() {
    g_KeyboardSelectedIndex = -1;
}

void DrawFocusRectangle(HDC hdc, const RECT* rcRow) {
    RECT rcFocus = *rcRow;
    rcFocus.left += 8;
    rcFocus.right -= 8;
    rcFocus.top += 2;
    rcFocus.bottom -= 2;
    HPEN hPen = CreatePen(PS_DOT, 1, RGB(0, 0, 0));
    HPEN hOldPen = (HPEN)SelectObject(hdc, hPen);
    HBRUSH hOldBrush = (HBRUSH)SelectObject(hdc, GetStockObject(NULL_BRUSH));
    Rectangle(hdc, rcFocus.left, rcFocus.top, rcFocus.right, rcFocus.bottom);
    SelectObject(hdc, hOldPen);
    SelectObject(hdc, hOldBrush);
    DeleteObject(hPen);
}

// -------------------------------------------------------
// Adapter helpers
// -------------------------------------------------------
BOOL GetAdapterNameForInterface(GUID interfaceGuid, WCHAR* adapterName, DWORD bufferSize) {
    PIP_ADAPTER_INFO pAdapterInfo = NULL;
    ULONG ulOutBufLen = sizeof(IP_ADAPTER_INFO);
    DWORD dwStatus;
    pAdapterInfo = (IP_ADAPTER_INFO*)malloc(ulOutBufLen);
    if (!pAdapterInfo) return FALSE;
    dwStatus = GetAdaptersInfo(pAdapterInfo, &ulOutBufLen);
    if (dwStatus == ERROR_BUFFER_OVERFLOW) {
        free(pAdapterInfo);
        pAdapterInfo = (IP_ADAPTER_INFO*)malloc(ulOutBufLen);
        if (!pAdapterInfo) return FALSE;
        dwStatus = GetAdaptersInfo(pAdapterInfo, &ulOutBufLen);
    }
    if (dwStatus == NO_ERROR) {
        PIP_ADAPTER_INFO pAdapter = pAdapterInfo;
        while (pAdapter) {
            WCHAR wideAdapterName[256];
            MultiByteToWideChar(CP_ACP, 0, pAdapter->AdapterName, -1, wideAdapterName, 256);
            GUID adapterGuid;
            if (CLSIDFromString(wideAdapterName, &adapterGuid) == S_OK) {
                if (IsEqualGUID(adapterGuid, interfaceGuid)) {
                    MultiByteToWideChar(CP_ACP, 0, pAdapter->Description, -1, adapterName, bufferSize);
                    free(pAdapterInfo);
                    return TRUE;
                }
            }
            pAdapter = pAdapter->Next;
        }
    }
    if (pAdapterInfo) free(pAdapterInfo);
    return FALSE;
}

void OpenNetworkStatusForInterface(GUID interfaceGuid) {
    WCHAR adapterName[256];
    if (GetAdapterNameForInterface(interfaceGuid, adapterName, 256)) {
        HWND hNcpa = FindWindowW(NULL, L"Connessioni di rete");
        if (!hNcpa) hNcpa = FindWindowW(NULL, L"Network Connections");
        if (!hNcpa) {
            ShellExecuteW(NULL, L"open", L"control.exe", L"ncpa.cpl", NULL, SW_SHOWNORMAL);
            for (int wait = 0; wait < 10 && !hNcpa; wait++) {
                Sleep(50);
                hNcpa = FindWindowW(NULL, L"Connessioni di rete");
                if (!hNcpa) hNcpa = FindWindowW(NULL, L"Network Connections");
            }
        }
        if (hNcpa) {
            SetForegroundWindow(hNcpa);
            SetWindowPos(hNcpa, HWND_TOPMOST, 0,0,0,0, SWP_NOMOVE|SWP_NOSIZE);
            SetWindowPos(hNcpa, HWND_NOTOPMOST, 0,0,0,0, SWP_NOMOVE|SWP_NOSIZE);
            HWND hListView = FindWindowExW(hNcpa, NULL, L"SysListView32", NULL);
            if (hListView) {
                int itemCount = (int)SendMessageW(hListView, LVM_GETITEMCOUNT, 0, 0);
                for (int i = 0; i < itemCount; i++) {
                    WCHAR itemText[256]; LVITEMW lvi = {0};
                    lvi.mask=LVIF_TEXT; lvi.iItem=i; lvi.iSubItem=0;
                    lvi.pszText=itemText; lvi.cchTextMax=256;
                    SendMessageW(hListView, LVM_GETITEMW, 0, (LPARAM)&lvi);
                    if (wcsstr(itemText, adapterName) || wcsstr(adapterName, itemText)) {
                        ListView_SetItemState(hListView, i, LVIS_SELECTED, LVIS_SELECTED);
                        RECT rcItem;
                        SendMessageW(hListView, LVM_GETITEMRECT, (WPARAM)i, (LPARAM)&rcItem);
                        int x=(rcItem.left+rcItem.right)/2, y=(rcItem.top+rcItem.bottom)/2;
                        PostMessage(hListView, WM_LBUTTONDOWN, MK_LBUTTON, MAKELPARAM(x,y));
                        PostMessage(hListView, WM_LBUTTONUP, 0, MAKELPARAM(x,y));
                        PostMessage(hListView, WM_LBUTTONDBLCLK, MK_LBUTTON, MAKELPARAM(x,y));
                        PostMessage(hListView, WM_LBUTTONUP, 0, MAKELPARAM(x,y));
                        break;
                    }
                }
            }
        }
    } else {
        ShellExecuteW(NULL, L"open", L"control.exe", L"ncpa.cpl", NULL, SW_SHOWNORMAL);
    }
}

void OpenNetworkPropertiesForInterface(GUID interfaceGuid) {
    WCHAR adapterName[256];
    if (GetAdapterNameForInterface(interfaceGuid, adapterName, 256)) {
        HWND hNcpa = FindWindowW(NULL, L"Connessioni di rete");
        if (!hNcpa) hNcpa = FindWindowW(NULL, L"Network Connections");
        if (!hNcpa) {
            ShellExecuteW(NULL, L"open", L"control.exe", L"ncpa.cpl", NULL, SW_SHOWNORMAL);
            for (int wait = 0; wait < 10 && !hNcpa; wait++) {
                Sleep(50);
                hNcpa = FindWindowW(NULL, L"Connessioni di rete");
                if (!hNcpa) hNcpa = FindWindowW(NULL, L"Network Connections");
            }
        }
        if (hNcpa) {
            SetForegroundWindow(hNcpa);
            SetWindowPos(hNcpa, HWND_TOPMOST, 0,0,0,0, SWP_NOMOVE|SWP_NOSIZE);
            SetWindowPos(hNcpa, HWND_NOTOPMOST, 0,0,0,0, SWP_NOMOVE|SWP_NOSIZE);
            HWND hListView = FindWindowExW(hNcpa, NULL, L"SysListView32", NULL);
            if (hListView) {
                int itemCount = (int)SendMessageW(hListView, LVM_GETITEMCOUNT, 0, 0);
                for (int i = 0; i < itemCount; i++) {
                    WCHAR itemText[256]; LVITEMW lvi = {0};
                    lvi.mask=LVIF_TEXT; lvi.iItem=i; lvi.iSubItem=0;
                    lvi.pszText=itemText; lvi.cchTextMax=256;
                    SendMessageW(hListView, LVM_GETITEMW, 0, (LPARAM)&lvi);
                    if (wcsstr(itemText, adapterName) || wcsstr(adapterName, itemText)) {
                        ListView_SetItemState(hListView, i, LVIS_SELECTED, LVIS_SELECTED);
                        SetForegroundWindow(hNcpa);
                        INPUT inputs[4] = {{0}};
                        inputs[0].type=INPUT_KEYBOARD; inputs[0].ki.wVk=VK_MENU;
                        inputs[1].type=INPUT_KEYBOARD; inputs[1].ki.wVk=VK_RETURN;
                        inputs[2].type=INPUT_KEYBOARD; inputs[2].ki.wVk=VK_RETURN; inputs[2].ki.dwFlags=KEYEVENTF_KEYUP;
                        inputs[3].type=INPUT_KEYBOARD; inputs[3].ki.wVk=VK_MENU;   inputs[3].ki.dwFlags=KEYEVENTF_KEYUP;
                        SendInput(4, inputs, sizeof(INPUT));
                        break;
                    }
                }
            }
        }
    } else {
        ShellExecuteW(NULL, L"open", L"control.exe", L"ncpa.cpl", NULL, SW_SHOWNORMAL);
    }
}

// -------------------------------------------------------
// Refresh button rect
// -------------------------------------------------------
void InitRefreshButtonRect() {
    g_rcRefreshButton.right  = WINDOW_WIDTH - 20;
    g_rcRefreshButton.left   = g_rcRefreshButton.right - 22;
    g_rcRefreshButton.top    = 8;
    g_rcRefreshButton.bottom = 30;
}

// -------------------------------------------------------
// Execute mapped command
// -------------------------------------------------------
static void ExecuteMappedCommand(const wchar_t* command) {
    if (!command) return;
    Wh_Log(L"ExecuteMappedCommand: %s", command);
    SHELLEXECUTEINFOW sei = { sizeof(sei) };
    sei.lpVerb = L"open";
    sei.nShow  = SW_SHOWNORMAL;
    const wchar_t* spacePos = wcschr(command, L' ');
    if (spacePos) {
        wchar_t program[256];
        size_t progLen = spacePos - command;
        wcsncpy_s(program, 256, command, progLen);
        program[progLen] = L'\0';
        sei.lpFile       = program;
        sei.lpParameters = spacePos + 1;
    } else {
        sei.lpFile = command;
    }
    ShellExecuteExW(&sei);
}

// -------------------------------------------------------
// TrackPopupMenuEx Hook — hooked once in Wh_ModInit,
// only intercepts menus that originate from Shell_TrayWnd
// -------------------------------------------------------
static BOOL WINAPI TrackPopupMenuEx_Hook(HMENU hMenu, UINT uFlags, int x, int y,
                                         HWND hWnd, const TPMPARAMS* lptpm) {
    if (!g_Settings.redirectNetworkContextMenu)
        return g_origTrackPopupMenuEx(hMenu, uFlags, x, y, hWnd, lptpm);

    BOOL isNetworkTrayMenu = FALSE;
    HWND hTray = FindWindowW(L"Shell_TrayWnd", NULL);
    if (hTray) {
        HWND hWndCheck = hWnd;
        while (hWndCheck) {
            if (hWndCheck == hTray) { isNetworkTrayMenu = TRUE; break; }
            hWndCheck = GetParent(hWndCheck);
        }
    }
    if (!isNetworkTrayMenu)
        return g_origTrackPopupMenuEx(hMenu, uFlags, x, y, hWnd, lptpm);

    UINT modifiedFlags = uFlags | TPM_RETURNCMD;
    BOOL result = g_origTrackPopupMenuEx(hMenu, modifiedFlags, x, y, hWnd, lptpm);
    if (result > 0) {
        UINT cmd = (UINT)result;
        Wh_Log(L"TrackPopupMenuEx: cmd %u", cmd);
        switch (cmd) {
            case CMD_OPEN_NETWORK_SETTINGS: ToggleFlyoutWindow(); return 0;
            case CMD_NETWORK_STATUS:        ExecuteMappedCommand(L"control.exe ncpa.cpl"); return 0;
            case CMD_NETWORK_DIAGNOSTICS:   ExecuteMappedCommand(L"msdt.exe -id NetworkDiagnosticsWeb"); return 0;
        }
    }
    return result;
}

// -------------------------------------------------------
// SSID display helper
// -------------------------------------------------------
static void GetDisplaySSID(int index, WCHAR* buf, int bufLen) {
    if (g_Settings.privacyMode)
        StringCchPrintfW(buf, bufLen, g_CurrentLocale->networkPrivacyFmt, index + 1);
    else
        StringCchCopyW(buf, bufLen, g_NetworkList[index].ssid);
}

// -------------------------------------------------------
// Icons and resources
// -------------------------------------------------------
void LoadSystemIcons() {
    if (!g_hIconNetworkMap)
        ExtractIconExW(L"netshell.dll", 120, &g_hIconNetworkMap, NULL, 1);
    for (int i = 0; i < 6; i++)
        if (!g_hIconSignalBars[i])
            ExtractIconExW(L"netshell.dll", 152 + i, &g_hIconSignalBars[i], NULL, 1);
    if (!g_hIconRefreshWin7)
        ExtractIconExW(L"shell32.dll", 238, &g_hIconRefreshWin7, NULL, 1);
}

void FreeSystemIcons() {
    if (g_hIconNetworkMap) { DestroyIcon(g_hIconNetworkMap); g_hIconNetworkMap = NULL; }
    for (int i = 0; i < 6; i++)
        if (g_hIconSignalBars[i]) { DestroyIcon(g_hIconSignalBars[i]); g_hIconSignalBars[i] = NULL; }
    if (g_hIconRefreshWin7) { DestroyIcon(g_hIconRefreshWin7); g_hIconRefreshWin7 = NULL; }
}

void InitGlobalFonts() {
    if (g_hFontNormal) return;
    g_hFontNormal    = CreateFontW(-12,0,0,0,FW_NORMAL,0,0,0,DEFAULT_CHARSET,OUT_DEFAULT_PRECIS,CLIP_DEFAULT_PRECIS,CLEARTYPE_QUALITY,DEFAULT_PITCH|FF_DONTCARE,L"Segoe UI");
    g_hFontBold      = CreateFontW(-12,0,0,0,FW_BOLD,  0,0,0,DEFAULT_CHARSET,OUT_DEFAULT_PRECIS,CLIP_DEFAULT_PRECIS,CLEARTYPE_QUALITY,DEFAULT_PITCH|FF_DONTCARE,L"Segoe UI");
    g_hFontTitle     = CreateFontW(-13,0,0,0,FW_BOLD,  0,0,0,DEFAULT_CHARSET,OUT_DEFAULT_PRECIS,CLIP_DEFAULT_PRECIS,CLEARTYPE_QUALITY,DEFAULT_PITCH|FF_DONTCARE,L"Segoe UI");
    g_hFontUnderline = CreateFontW(-12,0,0,0,FW_NORMAL,0,1,0,DEFAULT_CHARSET,OUT_DEFAULT_PRECIS,CLIP_DEFAULT_PRECIS,CLEARTYPE_QUALITY,DEFAULT_PITCH|FF_DONTCARE,L"Segoe UI");
    g_hFontButton    = CreateFontW(-12,0,0,0,FW_NORMAL,0,0,0,DEFAULT_CHARSET,OUT_DEFAULT_PRECIS,CLIP_DEFAULT_PRECIS,CLEARTYPE_QUALITY,DEFAULT_PITCH|FF_DONTCARE,L"Segoe UI");
    g_hFontCheckbox  = CreateFontW(-11,0,0,0,FW_NORMAL,0,0,0,DEFAULT_CHARSET,OUT_DEFAULT_PRECIS,CLIP_DEFAULT_PRECIS,CLEARTYPE_QUALITY,DEFAULT_PITCH|FF_DONTCARE,L"Segoe UI");
    g_hFontArrow     = CreateFontW(-11,0,0,0,FW_NORMAL,0,0,0,SYMBOL_CHARSET,OUT_DEFAULT_PRECIS,CLIP_DEFAULT_PRECIS,DEFAULT_QUALITY,DEFAULT_PITCH|FF_DONTCARE,L"Marlett");
}

void FreeGlobalFonts() {
    if (g_hFontNormal)    { DeleteObject(g_hFontNormal);    g_hFontNormal    = NULL; }
    if (g_hFontBold)      { DeleteObject(g_hFontBold);      g_hFontBold      = NULL; }
    if (g_hFontTitle)     { DeleteObject(g_hFontTitle);     g_hFontTitle     = NULL; }
    if (g_hFontUnderline) { DeleteObject(g_hFontUnderline); g_hFontUnderline = NULL; }
    if (g_hFontButton)    { DeleteObject(g_hFontButton);    g_hFontButton    = NULL; }
    if (g_hFontCheckbox)  { DeleteObject(g_hFontCheckbox);  g_hFontCheckbox  = NULL; }
    if (g_hFontArrow)     { DeleteObject(g_hFontArrow);     g_hFontArrow     = NULL; }
}

BOOL SafeToAccessUI() {
    return (g_Ctx.refCount > 0 && !g_Ctx.isUninitializing && g_hWndFlyout && IsWindow(g_hWndFlyout));
}

void PositionWindowNearTray(HWND hwnd) {
    APPBARDATA abd = { sizeof(APPBARDATA) };
    SHAppBarMessage(ABM_GETTASKBARPOS, &abd);
    RECT rcWork;
    SystemParametersInfoW(SPI_GETWORKAREA, 0, &rcWork, 0);
    int x = rcWork.right - WINDOW_WIDTH - 8;
    int y = rcWork.bottom - WINDOW_HEIGHT - 8;
    if (abd.uEdge == ABE_TOP)   y = abd.rc.bottom + 8;
    else if (abd.uEdge == ABE_LEFT)  x = abd.rc.right + 8;
    else if (abd.uEdge == ABE_RIGHT) x = abd.rc.left - WINDOW_WIDTH - 8;
    SetWindowPos(hwnd, HWND_TOPMOST, x, y, WINDOW_WIDTH, WINDOW_HEIGHT, SWP_SHOWWINDOW);
}

// -------------------------------------------------------
// WLAN data — SSID parsing fixes + duplicate prevention
// -------------------------------------------------------
void RefreshWifiData(HANDLE hClient) {
    if (!hClient) return;

    PWLAN_INTERFACE_INFO_LIST pIfList = NULL;
    if (WlanEnumInterfaces(hClient, NULL, &pIfList) != ERROR_SUCCESS) return;

    g_NetworkCount = 0;

    for (DWORD i = 0; i < pIfList->dwNumberOfItems; i++) {
        WLAN_INTERFACE_INFO IfInfo = pIfList->InterfaceInfo[i];
        PWLAN_AVAILABLE_NETWORK_LIST pBssList  = NULL;
        PWLAN_PROFILE_INFO_LIST      pProfList = NULL;

        WlanGetProfileList(hClient, &IfInfo.InterfaceGuid, NULL, &pProfList);

        DWORD dwFlags = WLAN_AVAILABLE_NETWORK_INCLUDE_ALL_MANUAL_HIDDEN_PROFILES;
        if (WlanGetAvailableNetworkList(hClient, &IfInfo.InterfaceGuid, dwFlags, NULL, &pBssList) == ERROR_SUCCESS) {
            for (DWORD j = 0; j < pBssList->dwNumberOfItems && g_NetworkCount < 50; j++) {
                WLAN_AVAILABLE_NETWORK network = pBssList->Network[j];
                WifiNetworkItem* item = &g_NetworkList[g_NetworkCount];

                DWORD len = network.dot11Ssid.uSSIDLength;
                if (len == 0) {
                    StringCchCopyW(item->ssid, 33, L"Hidden Network");
                } else {
                    int converted = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS,
                                                        (LPCSTR)network.dot11Ssid.ucSSID, (int)len,
                                                        item->ssid, 32);
                    if (converted <= 0) {
                        for (DWORD k = 0; k < len && k < 32; k++)
                            item->ssid[k] = (WCHAR)(BYTE)network.dot11Ssid.ucSSID[k];
                        item->ssid[len] = L'\0';
                    } else {
                        item->ssid[converted] = L'\0';
                    }
                }

                BOOL duplicate = FALSE;
                for (int d = 0; d < g_NetworkCount; d++) {
                    if (wcscmp(g_NetworkList[d].ssid, item->ssid) == 0) {
                        if (network.wlanSignalQuality > g_NetworkList[d].signalQuality)
                            g_NetworkList[d].signalQuality = network.wlanSignalQuality;
                        if (network.dwFlags & WLAN_AVAILABLE_NETWORK_CONNECTED)
                            g_NetworkList[d].isConnected = TRUE;
                        duplicate = TRUE;
                        break;
                    }
                }
                if (duplicate) continue;

                item->isConnected   = (network.dwFlags & WLAN_AVAILABLE_NETWORK_CONNECTED) != 0;
                item->isSecured     = network.bSecurityEnabled;
                item->signalQuality = network.wlanSignalQuality;
                item->interfaceGuid = IfInfo.InterfaceGuid;
                item->dot11BssType  = network.dot11BssType;
                item->hasProfile    = FALSE;
                item->hasInternetAccess = FALSE;
                item->isConnecting = FALSE;
                item->isDisconnecting = FALSE;

                if (pProfList) {
                    for (DWORD p = 0; p < pProfList->dwNumberOfItems; p++) {
                        if (wcscmp(pProfList->ProfileInfo[p].strProfileName, item->ssid) == 0) {
                            item->hasProfile = TRUE;
                            break;
                        }
                    }
                }

                if (item->isConnected && g_NetworkCount > 0) {
                    WifiNetworkItem tmp;
                    CopyMemory(&tmp,              &g_NetworkList[0], sizeof(WifiNetworkItem));
                    CopyMemory(&g_NetworkList[0], item,              sizeof(WifiNetworkItem));
                    CopyMemory(item,              &tmp,              sizeof(WifiNetworkItem));
                }
                g_NetworkCount++;
            }
            WlanFreeMemory(pBssList);
        }
        if (pProfList) WlanFreeMemory(pProfList);
    }
    WlanFreeMemory(pIfList);

    if (g_NetworkCount > 0 && g_NetworkList[0].isConnected)
        g_NetworkList[0].hasInternetAccess = IsInternetConnected();

    for (int i = 0; i < g_NetworkCount; i++) {
        if (g_IsConnectingAsynchronously &&
            wcscmp(g_NetworkList[i].ssid, g_PendingSsid) == 0) {
            g_NetworkList[i].isConnecting = TRUE;
        }
    }
}

// -------------------------------------------------------
// Password dialog (Windows 7 style)
// -------------------------------------------------------
typedef struct {
    WCHAR* passwordBuffer;
    DWORD  bufferSize;
    BOOL   confirmed;
    WCHAR  networkName[33];
} PasswordDlgData;

LRESULT CALLBACK Win7PasswordWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
    PasswordDlgData* data = (PasswordDlgData*)GetWindowLongPtrW(hwnd, GWLP_USERDATA);
    switch (uMsg) {
    case WM_NCHITTEST: {
        LRESULT r = DefWindowProcW(hwnd, uMsg, wParam, lParam);
        if (r==HTBOTTOM||r==HTBOTTOMLEFT||r==HTBOTTOMRIGHT||
            r==HTLEFT||r==HTRIGHT||r==HTTOP||r==HTTOPLEFT||r==HTTOPRIGHT)
            return HTCLIENT;
        return r;
    }
    case WM_CREATE: {
        CREATESTRUCTW* cs = (CREATESTRUCTW*)lParam;
        data = (PasswordDlgData*)cs->lpCreateParams;
        if (!data) return -1;
        SetWindowLongPtrW(hwnd, GWLP_USERDATA, (LONG_PTR)data);
        HFONT hFontDlg = CreateFontW(-12,0,0,0,FW_NORMAL,0,0,0,DEFAULT_CHARSET,
            OUT_DEFAULT_PRECIS,CLIP_DEFAULT_PRECIS,CLEARTYPE_QUALITY,DEFAULT_PITCH|FF_DONTCARE,L"Segoe UI");
        HWND hInstruction = CreateWindowExW(0, WC_STATICW, g_CurrentLocale->pwdInstructions,
            WS_CHILD|WS_VISIBLE, 15,15,380,20, hwnd,(HMENU)200,cs->hInstance,NULL);
        SendMessageW(hInstruction, WM_SETFONT, (WPARAM)hFontDlg, TRUE);
        HWND hLabel = CreateWindowExW(0, WC_STATICW, g_CurrentLocale->pwdLabel,
            WS_CHILD|WS_VISIBLE, 15,53,115,18, hwnd,NULL,cs->hInstance,NULL);
        SendMessageW(hLabel, WM_SETFONT, (WPARAM)hFontDlg, TRUE);
        HWND hEdit = CreateWindowExW(WS_EX_CLIENTEDGE, WC_EDITW, L"",
            WS_CHILD|WS_VISIBLE|ES_PASSWORD|ES_AUTOHSCROLL,
            135,50,255,22, hwnd,(HMENU)101,cs->hInstance,NULL);
        SendMessageW(hEdit, WM_SETFONT, (WPARAM)hFontDlg, TRUE);
        SetFocus(hEdit);
        HWND hCheck = CreateWindowExW(0, WC_BUTTONW, g_CurrentLocale->pwdHideChars,
            WS_CHILD|WS_VISIBLE|BS_AUTOCHECKBOX, 135,80,200,18, hwnd,(HMENU)102,cs->hInstance,NULL);
        SendMessageW(hCheck, WM_SETFONT, (WPARAM)hFontDlg, TRUE);
        RECT rcClient; GetClientRect(hwnd, &rcClient);
        int btnW=85, btnH=24, btnY=rcClient.bottom-35;
        HWND hBtnOk = CreateWindowExW(0, WC_BUTTONW, g_CurrentLocale->pwdOK,
            WS_CHILD|WS_VISIBLE|BS_DEFPUSHBUTTON,
            rcClient.right-btnW-15, btnY, btnW,btnH, hwnd,(HMENU)IDOK,cs->hInstance,NULL);
        SendMessageW(hBtnOk, WM_SETFONT, (WPARAM)hFontDlg, TRUE);
        HWND hBtnCancel = CreateWindowExW(0, WC_BUTTONW, g_CurrentLocale->pwdCancel,
            WS_CHILD|WS_VISIBLE, rcClient.right-(btnW*2)-25, btnY, btnW,btnH,
            hwnd,(HMENU)IDCANCEL,cs->hInstance,NULL);
        SendMessageW(hBtnCancel, WM_SETFONT, (WPARAM)hFontDlg, TRUE);
        BOOL pfEnabled = FALSE;
        DwmIsCompositionEnabled(&pfEnabled);
        if (pfEnabled) {
            DWMNCRENDERINGPOLICY pol = DWMNCRP_ENABLED;
            DwmSetWindowAttribute(hwnd, DWMWA_NCRENDERING_POLICY, &pol, sizeof(pol));
        }
        HMODULE hShell32 = LoadLibraryW(L"shell32.dll");
        if (hShell32) {
            HICON hIconLarge = (HICON)LoadImageW(hShell32, MAKEINTRESOURCE(162),
                IMAGE_ICON, GetSystemMetrics(SM_CXICON), GetSystemMetrics(SM_CYICON), LR_SHARED);
            HICON hIconSmall = (HICON)LoadImageW(hShell32, MAKEINTRESOURCE(162),
                IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), LR_SHARED);
            if (hIconLarge) SendMessageW(hwnd, WM_SETICON, ICON_BIG,   (LPARAM)hIconLarge);
            if (hIconSmall) SendMessageW(hwnd, WM_SETICON, ICON_SMALL, (LPARAM)hIconSmall);
            FreeLibrary(hShell32);
        } else {
            HICON hFallback = LoadIconW(NULL, IDI_APPLICATION);
            SendMessageW(hwnd, WM_SETICON, ICON_BIG,   (LPARAM)hFallback);
            SendMessageW(hwnd, WM_SETICON, ICON_SMALL, (LPARAM)hFallback);
        }
        break;
    }
    case WM_CTLCOLORSTATIC: {
        HDC hdc = (HDC)wParam;
        SetBkMode(hdc, TRANSPARENT);
        if (GetDlgCtrlID((HWND)lParam) == 200)
            SetTextColor(hdc, RGB(0,51,153));
        else
            SetTextColor(hdc, RGB(0,0,0));
        return (INT_PTR)GetStockObject(NULL_BRUSH);
    }
    case WM_COMMAND: {
        int wmId = LOWORD(wParam);
        if (wmId == 102) {
            HWND hEdit = GetDlgItem(hwnd, 101);
            BOOL checked = SendMessageW((HWND)lParam, BM_GETCHECK, 0, 0) == BST_CHECKED;
            SendMessageW(hEdit, EM_SETPASSWORDCHAR, checked ? 0 : L'*', 0);
            InvalidateRect(hEdit, NULL, TRUE);
            return 0;
        }
        if (wmId == IDOK) {
            if (data) { GetDlgItemTextW(hwnd,101,data->passwordBuffer,data->bufferSize); data->confirmed=TRUE; }
            DestroyWindow(hwnd); return 0;
        } else if (wmId == IDCANCEL) {
            if (data) data->confirmed = FALSE;
            DestroyWindow(hwnd); return 0;
        }
        break;
    }
    case WM_CLOSE:
        if (data) data->confirmed = FALSE;
        DestroyWindow(hwnd);
        return 0;
    }
    return DefWindowProcW(hwnd, uMsg, wParam, lParam);
}

BOOL PromptNetworkPassword(HWND hParent, WCHAR* passwordBuffer, DWORD bufferSize, const WCHAR* networkName) {
    if (!SafeToAccessUI()) return FALSE;
    HINSTANCE hInst = GetModuleHandle(NULL);
    WNDCLASSW wc = {0};
    wc.lpfnWndProc   = Win7PasswordWndProc;
    wc.hInstance     = hInst;
    wc.lpszClassName = L"Win7NetPwdClass";
    wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE+1);
    wc.hIcon         = LoadIconW(NULL, IDI_APPLICATION);
    UnregisterClassW(wc.lpszClassName, hInst);
    RegisterClassW(&wc);

    PasswordDlgData data = { passwordBuffer, bufferSize, FALSE };
    StringCchCopyW(data.networkName, 33, networkName ? networkName : L"");
    RECT rcWork;
    SystemParametersInfoW(SPI_GETWORKAREA, 0, &rcWork, 0);
    int dlgW=420, dlgH=180;
    HWND hDlg = CreateWindowExW(
        WS_EX_DLGMODALFRAME|WS_EX_WINDOWEDGE|WS_EX_TOPMOST,
        wc.lpszClassName, g_CurrentLocale->pwdTitle,
        WS_POPUP|WS_CAPTION|WS_SYSMENU,
        rcWork.right-dlgW-10, rcWork.bottom-dlgH-5, dlgW,dlgH,
        hParent, NULL, hInst, &data);
    if (!hDlg) return FALSE;
    ShowWindow(hDlg, SW_SHOW);
    EnableWindow(hParent, FALSE);
    MSG msg;
    while (IsWindow(hDlg) && GetMessageW(&msg, NULL, 0, 0)) {
        TranslateMessage(&msg);
        DispatchMessageW(&msg);
    }
    EnableWindow(hParent, TRUE);
    SetForegroundWindow(hParent);
    return data.confirmed;
}

// -------------------------------------------------------
// Connection handler (with XML escaping for SSID/password)
// -------------------------------------------------------
void HandleNativeConnection(HANDLE hClient, int index) {
    if (index < 0 || index >= g_NetworkCount || !hClient) return;
    WifiNetworkItem* item = &g_NetworkList[index];

    if (item->isConnected) {
        g_IsConnectingAsynchronously = FALSE;
        g_PendingConnectIndex = -1;
        WlanDisconnect(hClient, &item->interfaceGuid, NULL);

        g_PendingConnectIndex = index;
        g_ConnectRetryCount = 0;
        g_NetworkList[index].isDisconnecting = TRUE;

        if (g_ConnectCheckTimer) {
            KillTimer(g_hWndFlyout, g_ConnectCheckTimer);
            g_ConnectCheckTimer = 0;
        }
        g_ConnectCheckTimer = SetTimer(g_hWndFlyout, 1001, 300, NULL);
        return;
    }

    if (item->isSecured && !item->hasProfile) {
        WCHAR password[65] = {0};
        if (!PromptNetworkPassword(g_hWndFlyout, password, 64, item->ssid)) return;

        WCHAR escapedSsid[200];
        WCHAR escapedPassword[200];
        EscapeXmlString(item->ssid, escapedSsid, 200);
        EscapeXmlString(password, escapedPassword, 200);

        WCHAR xmlProfile[2048];
        StringCchPrintfW(xmlProfile, 2048,
            L"<?xml version=\"1.0\"?>"
            L"<WLANProfile xmlns=\"http://www.microsoft.com/networking/WLAN/profile/v1\">"
            L"<name>%s</name><SSIDConfig><SSID><name>%s</name></SSID></SSIDConfig>"
            L"<connectionType>ESS</connectionType><connectionMode>auto</connectionMode>"
            L"<MSM><security><authEncryption>"
            L"<authentication>WPA2PSK</authentication><encryption>AES</encryption><useOneX>false</useOneX>"
            L"</authEncryption><sharedKey><keyType>passPhrase</keyType><protected>false</protected>"
            L"<keyMaterial>%s</keyMaterial></sharedKey></security></MSM></WLANProfile>",
            escapedSsid, escapedSsid, escapedPassword);
        DWORD dwReason = 0;
        if (WlanSetProfile(hClient,&item->interfaceGuid,0,xmlProfile,NULL,TRUE,NULL,&dwReason) != ERROR_SUCCESS) {
            MessageBoxW(g_hWndFlyout, g_CurrentLocale->errSaveProfile,
                        g_CurrentLocale->errTitle, MB_OK|MB_ICONERROR);
            return;
        }
        item->hasProfile = TRUE;
    }

    g_IsConnectingAsynchronously = TRUE;
    g_PendingConnectIndex = index;
    StringCchCopyW(g_PendingSsid, 33, item->ssid);
    g_NetworkList[index].isConnecting = TRUE;
    g_ConnectRetryCount = 0;

    WLAN_CONNECTION_PARAMETERS params; ZeroMemory(&params, sizeof(params));
    if (item->hasProfile) {
        params.wlanConnectionMode = wlan_connection_mode_profile;
        params.strProfile = item->ssid;
    } else {
        params.wlanConnectionMode = wlan_connection_mode_discovery_unsecure;
    }
    params.dot11BssType = item->dot11BssType;

    DWORD res = WlanConnect(hClient, &item->interfaceGuid, &params, NULL);
    if (res != ERROR_SUCCESS) {
        g_IsConnectingAsynchronously = FALSE;
        g_NetworkList[index].isConnecting = FALSE;
        WCHAR err[256];
        StringCchPrintfW(err, 256, g_CurrentLocale->errConnectionFmt, res);
        MessageBoxW(g_hWndFlyout, err, g_CurrentLocale->errTitle, MB_OK|MB_ICONERROR);
        return;
    }

    if (g_ConnectCheckTimer) {
        KillTimer(g_hWndFlyout, g_ConnectCheckTimer);
        g_ConnectCheckTimer = 0;
    }
    g_ConnectCheckTimer = SetTimer(g_hWndFlyout, 1001, 300, NULL);
}

// -------------------------------------------------------
// WLAN Notification Callback — thread-safe
// Only posts messages; never touches UI or global state directly
// -------------------------------------------------------
void WINAPI WlanNotificationCallback(PWLAN_NOTIFICATION_DATA data, PVOID context) {
    ModContext* ctx = (ModContext*)context;
    if (!ctx || ctx->isUninitializing) return;
    if (!data) return;

    BOOL needRefresh = FALSE;

    switch(data->NotificationSource) {
        case WLAN_NOTIFICATION_SOURCE_ACM:
            switch (data->NotificationCode) {
                case wlan_notification_acm_connection_complete:
                case wlan_notification_acm_disconnected:
                case wlan_notification_acm_scan_complete:
                    needRefresh = TRUE;
                    break;
                case wlan_notification_acm_connection_attempt_fail:
                    needRefresh = TRUE;
                    if (ctx->hWndFlyout && IsWindow(ctx->hWndFlyout))
                        PostMessageW(ctx->hWndFlyout, WM_CHECK_CONNECTION,
                                     CONN_NOTIFY_ATTEMPT_FAIL, 0);
                    break;
            }
            break;

        case WLAN_NOTIFICATION_SOURCE_MSM:
            switch (data->NotificationCode) {
                case wlan_notification_msm_connected:
                    needRefresh = TRUE;
                    if (ctx->hWndFlyout && IsWindow(ctx->hWndFlyout))
                        PostMessageW(ctx->hWndFlyout, WM_CHECK_CONNECTION,
                                     CONN_NOTIFY_MSM_CONNECTED, 0);
                    break;
                case wlan_notification_msm_disconnected:
                    needRefresh = TRUE;
                    if (ctx->hWndFlyout && IsWindow(ctx->hWndFlyout))
                        PostMessageW(ctx->hWndFlyout, WM_CHECK_CONNECTION,
                                     CONN_NOTIFY_MSM_DISCONNECTED, 0);
                    break;
            }
            break;
    }

    if (needRefresh && ctx->hWndFlyout && IsWindow(ctx->hWndFlyout))
        PostMessageW(ctx->hWndFlyout, WM_REFRESH_DATA, 0, 0);
}

void DrawNativeSignalIcon(HDC hdc, int right, int top, ULONG quality) {
    int idx = 0;
    if      (quality > 80) idx = 5;
    else if (quality > 60) idx = 4;
    else if (quality > 40) idx = 3;
    else if (quality > 20) idx = 2;
    else if (quality > 0)  idx = 1;
    if (g_hIconSignalBars[idx])
        DrawIconEx(hdc, right-24, top+4, g_hIconSignalBars[idx], 16, 16, 0, NULL, DI_NORMAL);
}

// -------------------------------------------------------
// Tooltip (Windows 7 style)
// -------------------------------------------------------
void InitTooltip(HWND hwnd) {
    if (g_hTooltip) return;
    g_hTooltip = CreateWindowEx(WS_EX_TOPMOST, TOOLTIPS_CLASS, NULL,
        WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP,
        CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
        hwnd, NULL, GetModuleHandle(NULL), NULL);
    SendMessage(g_hTooltip, TTM_SETMAXTIPWIDTH,   0, 300);
    SendMessage(g_hTooltip, TTM_SETDELAYTIME, TTDT_AUTOPOP, 10000);
    SendMessage(g_hTooltip, TTM_SETDELAYTIME, TTDT_INITIAL, 400);
    SendMessage(g_hTooltip, TTM_SETDELAYTIME, TTDT_RESHOW,  200);
}

void UpdateTooltipForRow(HWND hwnd, int index) {
    if (!g_hTooltip) InitTooltip(hwnd);

    for (int i = 0; i < 50; i++) {
        TOOLINFOW ti = {0};
        ti.cbSize = sizeof(TOOLINFOW);
        ti.hwnd   = hwnd;
        ti.uId    = (UINT_PTR)(i + 1);
        SendMessage(g_hTooltip, TTM_DELTOOL, 0, (LPARAM)&ti);
    }

    if (index < 0 || index >= g_NetworkCount) return;

    WifiNetworkItem* item = &g_NetworkList[index];
    WCHAR ssidBuf[33];
    GetDisplaySSID(index, ssidBuf, 33);

    WCHAR securityType[64];
    StringCchCopyW(securityType, 64, item->isSecured ? L"WPA2-PSK" : g_CurrentLocale->securityOpen);

    const WCHAR* sigStr = SignalQualityToString(item->signalQuality);

    const WCHAR* bssTypeStr;
    switch(item->dot11BssType) {
        case dot11_BSS_type_infrastructure: bssTypeStr = g_CurrentLocale->bssTypeInfrastructure; break;
        case dot11_BSS_type_independent:    bssTypeStr = g_CurrentLocale->bssTypeIndependent; break;
        default:                            bssTypeStr = g_CurrentLocale->bssTypeUnknown; break;
    }

    const WCHAR* statusText;
    if (item->isConnected)
        statusText = g_CurrentLocale->tooltipStatusConnected;
    else if (item->isConnecting)
        statusText = g_CurrentLocale->tooltipStatusConnecting;
    else
        statusText = g_CurrentLocale->tooltipStatusNotConnected;

    StringCchPrintfW(g_TooltipBuffer, 1024,
        L"SSID: %s\n%s %s\n%s %s\n%s %s\n%s",
        ssidBuf,
        g_CurrentLocale->signalStrength, sigStr,
        g_CurrentLocale->securityType,   securityType,
        g_CurrentLocale->radioType,      bssTypeStr,
        statusText);

    RECT rcRow;
    if (!GetRowRect(index, &rcRow)) return;

    TOOLINFOW ti = {0};
    ti.cbSize   = sizeof(TOOLINFOW);
    ti.uFlags   = TTF_SUBCLASS;
    ti.hwnd     = hwnd;
    ti.uId      = (UINT_PTR)(index + 1);
    ti.lpszText = g_TooltipBuffer;
    ti.rect     = rcRow;

    SendMessage(g_hTooltip, TTM_ADDTOOL, 0, (LPARAM)&ti);
}

// -------------------------------------------------------
// Row layout helpers
// -------------------------------------------------------
BOOL GetRowRect(int index, RECT* rcRow) {
    if (index < 0 || index >= g_NetworkCount || !g_bListExpanded) return FALSE;
    int y = LIST_Y_START;
    for (int i = 0; i < index; i++)
        y += (i == g_SelectedRowIndex) ? ROW_HEIGHT_EXPANDED : ROW_HEIGHT_NORMAL;
    if (y >= LIST_Y_END) return FALSE;
    rcRow->left   = 10;
    rcRow->top    = y;
    rcRow->right  = WINDOW_WIDTH - 10;
    rcRow->bottom = (int)fmin(y + ((index == g_SelectedRowIndex) ? ROW_HEIGHT_EXPANDED : ROW_HEIGHT_NORMAL), LIST_Y_END);
    return TRUE;
}

int HitTestRows(int x, int y) {
    for (int i = 0; i < g_NetworkCount; i++) {
        RECT rc;
        if (GetRowRect(i, &rc) && x>=rc.left && x<=rc.right && y>=rc.top && y<=rc.bottom) return i;
    }
    return -1;
}

void RecalcArrowRect() {
    int labelMidY = WIFI_LABEL_Y + (HEADER_HEIGHT - WIFI_LABEL_Y) / 2;
    int btnH=16, btnW=22;
    int margineDestroFreccia = 24;
    g_rcArrowButton.right  = WINDOW_WIDTH - margineDestroFreccia;
    g_rcArrowButton.left   = g_rcArrowButton.right - btnW;
    g_rcArrowButton.top    = labelMidY - btnH/2;
    g_rcArrowButton.bottom = labelMidY + btnH/2;
}

void UpdateLayoutGeometry() {
    if (!SafeToAccessUI()) return;
    if (g_SelectedRowIndex == -1) {
        if (g_hWndButtonConnect   && IsWindow(g_hWndButtonConnect))   ShowWindow(g_hWndButtonConnect,   SW_HIDE);
        if (g_hWndCheckboxConnect && IsWindow(g_hWndCheckboxConnect))  ShowWindow(g_hWndCheckboxConnect, SW_HIDE);
        return;
    }
    RECT rcRow;
    if (!GetRowRect(g_SelectedRowIndex, &rcRow)) {
        if (g_hWndButtonConnect   && IsWindow(g_hWndButtonConnect))   ShowWindow(g_hWndButtonConnect,   SW_HIDE);
        if (g_hWndCheckboxConnect && IsWindow(g_hWndCheckboxConnect))  ShowWindow(g_hWndCheckboxConnect, SW_HIDE);
        return;
    }
    WifiNetworkItem* item = &g_NetworkList[g_SelectedRowIndex];
    if (!item->isConnected && !item->isConnecting) {
        if (g_hWndCheckboxConnect && IsWindow(g_hWndCheckboxConnect)) {
            MoveWindow(g_hWndCheckboxConnect, rcRow.left+8, rcRow.top+36, 160, 20, TRUE);
            SetWindowTextW(g_hWndCheckboxConnect, g_CurrentLocale->chkConnectAuto);
            ShowWindow(g_hWndCheckboxConnect, SW_SHOW);
        }
        if (g_hWndButtonConnect && IsWindow(g_hWndButtonConnect)) {
            MoveWindow(g_hWndButtonConnect, rcRow.right-90, rcRow.top+35, 82, 22, TRUE);
            SetWindowTextW(g_hWndButtonConnect, g_CurrentLocale->btnConnect);
            ShowWindow(g_hWndButtonConnect, SW_SHOW);
            EnableWindow(g_hWndButtonConnect, TRUE);
        }
    } else if (item->isConnecting) {
        if (g_hWndCheckboxConnect && IsWindow(g_hWndCheckboxConnect)) ShowWindow(g_hWndCheckboxConnect, SW_HIDE);
        if (g_hWndButtonConnect && IsWindow(g_hWndButtonConnect)) {
            MoveWindow(g_hWndButtonConnect, rcRow.right-90, rcRow.top+35, 82, 22, TRUE);
            SetWindowTextW(g_hWndButtonConnect, g_CurrentLocale->connecting);
            ShowWindow(g_hWndButtonConnect, SW_SHOW);
            EnableWindow(g_hWndButtonConnect, FALSE);
        }
    } else {
        if (g_hWndCheckboxConnect && IsWindow(g_hWndCheckboxConnect)) ShowWindow(g_hWndCheckboxConnect, SW_HIDE);
        if (g_hWndButtonConnect   && IsWindow(g_hWndButtonConnect)) {
            MoveWindow(g_hWndButtonConnect, rcRow.right-90, rcRow.top+35, 82, 22, TRUE);
            SetWindowTextW(g_hWndButtonConnect, g_CurrentLocale->btnDisconnect);
            ShowWindow(g_hWndButtonConnect, SW_SHOW);
            EnableWindow(g_hWndButtonConnect, TRUE);
        }
    }
}

void ShowContextMenu(HWND hwnd, int itemIndex, POINT pt) {
    if (itemIndex < 0 || itemIndex >= g_NetworkCount) return;
    g_ContextMenuTargetIndex = itemIndex;
    WifiNetworkItem* item = &g_NetworkList[itemIndex];
    HMENU hMenu = CreatePopupMenu();
    if (item->isConnected) {
        AppendMenuW(hMenu, MF_STRING, IDM_DISCONNECT, g_CurrentLocale->ctxDisconnect);
        AppendMenuW(hMenu, MF_STRING, IDM_STATUS,     g_CurrentLocale->ctxStatus);
    } else if (item->isConnecting) {
        AppendMenuW(hMenu, MF_STRING | MF_GRAYED, IDM_CONNECT, g_CurrentLocale->connecting);
    } else {
        AppendMenuW(hMenu, MF_STRING, IDM_CONNECT, g_CurrentLocale->ctxConnect);
    }
    AppendMenuW(hMenu, MF_SEPARATOR, 0, NULL);
    AppendMenuW(hMenu, MF_STRING, IDM_PROPERTIES, g_CurrentLocale->ctxProperties);
    int cmd = TrackPopupMenu(hMenu, TPM_LEFTALIGN|TPM_RIGHTBUTTON|TPM_RETURNCMD, pt.x, pt.y, 0, hwnd, NULL);
    if (cmd > 0) {
        switch (cmd) {
        case IDM_CONNECT: case IDM_DISCONNECT:
            if (g_Ctx.hWlanClient) HandleNativeConnection(g_Ctx.hWlanClient, g_ContextMenuTargetIndex);
            break;
        case IDM_STATUS:
            if (g_ContextMenuTargetIndex >= 0 && g_ContextMenuTargetIndex < g_NetworkCount)
                OpenNetworkStatusForInterface(g_NetworkList[g_ContextMenuTargetIndex].interfaceGuid);
            ShowWindow(hwnd, SW_HIDE);
            break;
        case IDM_PROPERTIES:
            ShellExecuteW(NULL, L"open", L"control.exe", L"ncpa.cpl", NULL, SW_SHOWNORMAL);
            ShowWindow(hwnd, SW_HIDE);
            break;
        }
    }
    DestroyMenu(hMenu);
}

static RECT GetFooterRect() {
    RECT rc = { 0, WINDOW_HEIGHT - FOOTER_HEIGHT, WINDOW_WIDTH, WINDOW_HEIGHT };
    return rc;
}

// -------------------------------------------------------
// Draw helpers — respects Classic theme setting (cached check)
// -------------------------------------------------------
static void DrawRectOrRoundRect(HDC hdc, int l, int t, int r, int b, int rx, int ry) {
    if (g_Settings.disableRoundedCornersClassic && IsClassicTheme())
        Rectangle(hdc, l, t, r, b);
    else
        RoundRect(hdc, l, t, r, b, rx, ry);
}

// -------------------------------------------------------
// Flyout Window Procedure
// -------------------------------------------------------
LRESULT CALLBACK FlyoutWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
    switch (uMsg) {
    case WM_NCHITTEST: {
        LRESULT r = DefWindowProc(hwnd, uMsg, wParam, lParam);
        switch (r) {
            case HTTOP: case HTTOPLEFT: case HTTOPRIGHT:
            case HTBOTTOM: case HTBOTTOMLEFT: case HTBOTTOMRIGHT:
            case HTLEFT: case HTRIGHT:
                return HTBORDER;
            default: return r;
        }
    }
    case WM_CREATE: {
        Wh_Log(L"FlyoutWndProc - WM_CREATE");
        HMENU hSysMenu = GetSystemMenu(hwnd, FALSE);
        if (hSysMenu) RemoveMenu(hSysMenu, SC_CLOSE, MF_BYCOMMAND);
        g_hWndButtonConnect = CreateWindowExW(0, WC_BUTTONW, L"",
            WS_CHILD|BS_PUSHBUTTON, 0,0,0,0, hwnd,(HMENU)IDC_CONN_BUTTON,GetModuleHandle(NULL),NULL);
        SendMessageW(g_hWndButtonConnect,   WM_SETFONT,(WPARAM)g_hFontButton,  TRUE);
        g_hWndCheckboxConnect = CreateWindowExW(0, WC_BUTTONW, L"",
            WS_CHILD|BS_AUTOCHECKBOX, 0,0,0,0, hwnd,(HMENU)IDC_AUTO_CHECKBOX,GetModuleHandle(NULL),NULL);
        SendMessageW(g_hWndCheckboxConnect, WM_SETFONT,(WPARAM)g_hFontCheckbox,TRUE);
        SendMessageW(g_hWndCheckboxConnect, BM_SETCHECK,BST_CHECKED,0);
        RecalcArrowRect();
        InterlockedIncrement(&g_Ctx.refCount);
        InitTooltip(hwnd);
        if (g_Settings.refreshInterval > 0) {
            g_RefreshTimer = SetTimer(hwnd, 1000, g_Settings.refreshInterval, NULL);
            Wh_Log(L"Auto-refresh timer started: %d ms", g_Settings.refreshInterval);
        }
        break;
    }

    case WM_CHECK_CONNECTION: {
        switch (wParam) {
            case CONN_NOTIFY_ATTEMPT_FAIL:
                if (g_IsConnectingAsynchronously) {
                    g_IsConnectingAsynchronously = FALSE;
                    if (g_PendingConnectIndex >= 0 && g_PendingConnectIndex < g_NetworkCount)
                        g_NetworkList[g_PendingConnectIndex].isConnecting = FALSE;
                    if (g_ConnectCheckTimer) {
                        KillTimer(hwnd, g_ConnectCheckTimer);
                        g_ConnectCheckTimer = 0;
                    }
                    MessageBoxW(hwnd, g_CurrentLocale->pwdFailedWrong,
                                g_CurrentLocale->pwdFailedTitle, MB_OK|MB_ICONERROR|MB_TOPMOST);
                    UpdateLayoutGeometry();
                    InvalidateRect(hwnd, NULL, TRUE);
                }
                break;

            case CONN_NOTIFY_MSM_CONNECTED:
                if (g_IsConnectingAsynchronously) {
                    g_IsConnectingAsynchronously = FALSE;
                    if (g_PendingConnectIndex >= 0 && g_PendingConnectIndex < g_NetworkCount) {
                        g_NetworkList[g_PendingConnectIndex].isConnecting = FALSE;
                        g_NetworkList[g_PendingConnectIndex].isConnected  = TRUE;
                    }
                    if (g_ConnectCheckTimer) {
                        KillTimer(hwnd, g_ConnectCheckTimer);
                        g_ConnectCheckTimer = 0;
                    }
                    UpdateLayoutGeometry();
                    InvalidateRect(hwnd, NULL, TRUE);
                }
                break;

            case CONN_NOTIFY_MSM_DISCONNECTED:
                if (g_PendingConnectIndex >= 0 && g_PendingConnectIndex < g_NetworkCount) {
                    g_NetworkList[g_PendingConnectIndex].isDisconnecting = FALSE;
                    g_NetworkList[g_PendingConnectIndex].isConnected     = FALSE;
                }
                g_IsConnectingAsynchronously = FALSE;
                if (g_ConnectCheckTimer) {
                    KillTimer(hwnd, g_ConnectCheckTimer);
                    g_ConnectCheckTimer = 0;
                }
                UpdateLayoutGeometry();
                InvalidateRect(hwnd, NULL, TRUE);
                break;
        }
        break;
    }

    case WM_TIMER:
        if (wParam == 1000) {
            if (g_Ctx.hWlanClient) {
                RefreshWifiData(g_Ctx.hWlanClient);
                UpdateLayoutGeometry();
                InvalidateRect(hwnd, NULL, TRUE);
            }
        } else if (wParam == 1001) {
            if (!g_Ctx.hWlanClient) {
                if (g_ConnectCheckTimer) {
                    KillTimer(hwnd, g_ConnectCheckTimer);
                    g_ConnectCheckTimer = 0;
                }
                break;
            }

            g_ConnectRetryCount++;
            RefreshWifiData(g_Ctx.hWlanClient);
            BOOL operationComplete = FALSE;

            if (g_PendingConnectIndex >= 0 && g_PendingConnectIndex < g_NetworkCount) {
                WifiNetworkItem* item = &g_NetworkList[g_PendingConnectIndex];

                if (g_IsConnectingAsynchronously && item->isConnected) {
                    g_IsConnectingAsynchronously = FALSE;
                    item->isConnecting = FALSE;
                    operationComplete = TRUE;
                    Wh_Log(L"Connection completed successfully");
                } else if (!g_IsConnectingAsynchronously && !item->isConnected &&
                          (item->isDisconnecting || g_NetworkList[g_PendingConnectIndex].isConnected == FALSE)) {
                    item->isDisconnecting = FALSE;
                    operationComplete = TRUE;
                    Wh_Log(L"Disconnection completed successfully");
                }

                if (operationComplete) {
                    if (g_ConnectCheckTimer) {
                        KillTimer(hwnd, g_ConnectCheckTimer);
                        g_ConnectCheckTimer = 0;
                    }
                    g_PendingConnectIndex = -1;
                    g_ConnectRetryCount = 0;
                    UpdateLayoutGeometry();
                    InvalidateRect(hwnd, NULL, TRUE);
                } else if (g_ConnectRetryCount > CONNECT_TIMEOUT_RETRIES) {
                    if (g_IsConnectingAsynchronously) {
                        g_IsConnectingAsynchronously = FALSE;
                        item->isConnecting = FALSE;
                        MessageBoxW(hwnd, g_CurrentLocale->errTimeout,
                                    g_CurrentLocale->errTitle, MB_OK|MB_ICONERROR);
                    }
                    if (item->isDisconnecting)
                        item->isDisconnecting = FALSE;
                    if (g_ConnectCheckTimer) {
                        KillTimer(hwnd, g_ConnectCheckTimer);
                        g_ConnectCheckTimer = 0;
                    }
                    g_PendingConnectIndex = -1;
                    g_ConnectRetryCount = 0;
                    UpdateLayoutGeometry();
                    InvalidateRect(hwnd, NULL, TRUE);
                    Wh_Log(L"Connection operation timed out");
                }
            } else {
                if (g_ConnectCheckTimer) {
                    KillTimer(hwnd, g_ConnectCheckTimer);
                    g_ConnectCheckTimer = 0;
                }
            }
        }
        break;

    case WM_SHOW_FLYOUT:
        ShowWindow(hwnd, SW_SHOW);
        SetForegroundWindow(hwnd);
        if (g_Ctx.hWlanClient) {
            RefreshWifiData(g_Ctx.hWlanClient);
            UpdateLayoutGeometry();
            InvalidateRect(hwnd, NULL, TRUE);
        }
        break;

    case WM_CTLCOLORSTATIC: {
        HWND hwndCtl = (HWND)lParam;
        HDC  hdc     = (HDC)wParam;
        if (hwndCtl == g_hWndCheckboxConnect) {
            SetBkMode(hdc, TRANSPARENT);
            SetTextColor(hdc, RGB(0,0,0));
            static HBRUSH hBrushRow = NULL;
            if (!hBrushRow) hBrushRow = CreateSolidBrush(RGB(228,241,252));
            return (INT_PTR)hBrushRow;
        }
        break;
    }
    case WM_GETDLGCODE:
        return DLGC_WANTARROWS | DLGC_WANTCHARS;

    case WM_KEYDOWN: {
        switch (wParam) {
            case VK_UP:
                if (g_bListExpanded && g_NetworkCount > 0) {
                    int newIndex = (g_KeyboardSelectedIndex > 0) ? g_KeyboardSelectedIndex - 1 : g_NetworkCount - 1;
                    SetKeyboardFocus(newIndex);
                    InvalidateRect(hwnd, NULL, TRUE);
                }
                return 0;
            case VK_DOWN:
                if (g_bListExpanded && g_NetworkCount > 0) {
                    int newIndex = (g_KeyboardSelectedIndex < g_NetworkCount - 1) ? g_KeyboardSelectedIndex + 1 : 0;
                    SetKeyboardFocus(newIndex);
                    InvalidateRect(hwnd, NULL, TRUE);
                }
                return 0;
            case VK_RETURN:
                if (g_KeyboardSelectedIndex >= 0 && g_KeyboardSelectedIndex < g_NetworkCount && g_Ctx.hWlanClient)
                    HandleNativeConnection(g_Ctx.hWlanClient, g_KeyboardSelectedIndex);
                return 0;
            case VK_LEFT:
                ShowWindow(hwnd, SW_HIDE);
                return 0;
            case VK_RIGHT:
                if (g_KeyboardSelectedIndex >= 0 && g_KeyboardSelectedIndex < g_NetworkCount) {
                    RECT rcRow;
                    if (GetRowRect(g_KeyboardSelectedIndex, &rcRow)) {
                        POINT pt = {rcRow.left + 20, rcRow.top + 13};
                        ClientToScreen(hwnd, &pt);
                        ShowContextMenu(hwnd, g_KeyboardSelectedIndex, pt);
                    }
                }
                return 0;
            case VK_ESCAPE:
                ShowWindow(hwnd, SW_HIDE);
                return 0;
        }
        break;
    }

    case WM_PAINT: {
        if (!SafeToAccessUI()) break;
        PAINTSTRUCT ps;
        HDC hdcReal = BeginPaint(hwnd, &ps);
        HDC     hdc     = CreateCompatibleDC(hdcReal);
        HBITMAP hBmp    = CreateCompatibleBitmap(hdcReal, WINDOW_WIDTH, WINDOW_HEIGHT);
        HBITMAP hOldBmp = (HBITMAP)SelectObject(hdc, hBmp);

        RECT rcHeader  = {0, 0, WINDOW_WIDTH, HEADER_HEIGHT};
        HBRUSH hBrH = CreateSolidBrush(RGB(235,244,253)); FillRect(hdc, &rcHeader, hBrH); DeleteObject(hBrH);
        RECT rcContent = {0, HEADER_HEIGHT, WINDOW_WIDTH, LIST_Y_END};
        HBRUSH hBrC = CreateSolidBrush(RGB(255,255,255)); FillRect(hdc, &rcContent, hBrC); DeleteObject(hBrC);
        RECT rcFooter = GetFooterRect();
        HBRUSH hBrF = CreateSolidBrush(RGB(225,230,242));
        FillRect(hdc, &rcFooter, hBrF); DeleteObject(hBrF);

        HPEN hPenSep = CreatePen(PS_SOLID, 1, RGB(214,223,234));
        HPEN hOldPen = (HPEN)SelectObject(hdc, hPenSep);
        MoveToEx(hdc, 0, HEADER_HEIGHT, NULL); LineTo(hdc, WINDOW_WIDTH, HEADER_HEIGHT);
        SelectObject(hdc, hOldPen); DeleteObject(hPenSep);

        HPEN hPenBevelDark  = CreatePen(PS_SOLID, 1, RGB(180,193,210));
        HPEN hPenBevelLight = CreatePen(PS_SOLID, 1, RGB(255,255,255));

        SelectObject(hdc, hPenBevelDark);
        MoveToEx(hdc, 0, LIST_Y_END,     NULL); LineTo(hdc, WINDOW_WIDTH, LIST_Y_END);
        SelectObject(hdc, hPenBevelLight);
        MoveToEx(hdc, 0, LIST_Y_END + 1, NULL); LineTo(hdc, WINDOW_WIDTH, LIST_Y_END + 1);

        SelectObject(hdc, hOldPen);
        DeleteObject(hPenBevelDark);
        DeleteObject(hPenBevelLight);

        BOOL isAnyConnected = (g_NetworkCount > 0 && g_NetworkList[0].isConnected);
        SetBkMode(hdc, TRANSPARENT);
        if (isAnyConnected) {
            SelectObject(hdc, g_hFontNormal); SetTextColor(hdc, RGB(0,0,0));
            TextOutW(hdc, 56, 18, g_CurrentLocale->currentConnected, lstrlenW(g_CurrentLocale->currentConnected));
            SelectObject(hdc, g_hFontBold);
            WCHAR displaySsid[33]; GetDisplaySSID(0, displaySsid, 33);
            TextOutW(hdc, 56, 34, displaySsid, lstrlenW(displaySsid));
            SelectObject(hdc, g_hFontNormal); SetTextColor(hdc, RGB(110,110,110));
            TextOutW(hdc, 56, 50, g_CurrentLocale->internetAccess, lstrlenW(g_CurrentLocale->internetAccess));
        } else {
            SelectObject(hdc, g_hFontBold); SetTextColor(hdc, RGB(0,0,0));
            TextOutW(hdc, 56, 22, g_CurrentLocale->noConnections, lstrlenW(g_CurrentLocale->noConnections));
            SelectObject(hdc, g_hFontNormal);
            TextOutW(hdc, 56, 40, g_CurrentLocale->connectionsAvailable, lstrlenW(g_CurrentLocale->connectionsAvailable));
        }
        HICON hLargeIcon = isAnyConnected ? g_hIconNetworkMap : g_hIconSignalBars[0];
        if (hLargeIcon) DrawIconEx(hdc, 14, 20, hLargeIcon, 32, 32, 0, NULL, DI_NORMAL);

        if (g_IsHoveringRefresh) {
            HBRUSH hBrHov = CreateSolidBrush(RGB(200,225,245));
            HPEN hPenHov = CreatePen(PS_SOLID, 1, RGB(150,190,230));
            HPEN hOldPenHov = (HPEN)SelectObject(hdc, hPenHov);
            HBRUSH hOldBH = (HBRUSH)SelectObject(hdc, hBrHov);
            DrawRectOrRoundRect(hdc, g_rcRefreshButton.left, g_rcRefreshButton.top,
                                g_rcRefreshButton.right, g_rcRefreshButton.bottom, 4, 4);
            SelectObject(hdc, hOldPenHov); DeleteObject(hPenHov);
            SelectObject(hdc, hOldBH); DeleteObject(hBrHov);
        }
        if (g_hIconRefreshWin7)
            DrawIconEx(hdc, g_rcRefreshButton.left+2, g_rcRefreshButton.top+3,
                       g_hIconRefreshWin7, 16, 16, 0, NULL, DI_NORMAL);

        SelectObject(hdc, g_hFontNormal); SetTextColor(hdc, RGB(90,100,110));
        TextOutW(hdc, 14, HEADER_HEIGHT - 24, g_CurrentLocale->wifiHeader, lstrlenW(g_CurrentLocale->wifiHeader));
        if (g_IsHoveringArrow) {
            HBRUSH hBrA  = CreateSolidBrush(RGB(230,240,255));
            HPEN   hPenA = CreatePen(PS_SOLID, 1, RGB(180,210,245));
            HPEN   hOldPA = (HPEN)SelectObject(hdc, hPenA);
            HBRUSH hOldBA = (HBRUSH)SelectObject(hdc, hBrA);
            DrawRectOrRoundRect(hdc, g_rcArrowButton.left, g_rcArrowButton.top,
                                g_rcArrowButton.right, g_rcArrowButton.bottom, 2, 2);
            SelectObject(hdc, hOldPA); SelectObject(hdc, hOldBA);
            DeleteObject(hBrA); DeleteObject(hPenA);
        }
        SelectObject(hdc, g_hFontArrow); SetTextColor(hdc, RGB(50,50,50));
        LPCWSTR arrowChar = g_bListExpanded ? L"6" : L"5";
        RECT rcArrowText = g_rcArrowButton; rcArrowText.top += 2;
        DrawTextW(hdc, arrowChar, 1, &rcArrowText, DT_CENTER|DT_VCENTER|DT_SINGLELINE);

        if (g_bListExpanded) {
            for (int i = 0; i < g_NetworkCount; i++) {
                RECT rcRow;
                if (!GetRowRect(i, &rcRow)) continue;
                BOOL isSelected = (i == g_SelectedRowIndex);
                BOOL isHovered  = (i == g_HoveredRowIndex);
                BOOL hasKeyboardFocus = (i == g_KeyboardSelectedIndex);
                if (isSelected || isHovered) {
                    RECT rcFullRow = rcRow; rcFullRow.left = 0; rcFullRow.right = WINDOW_WIDTH - 5;
                    HBRUSH hBrBg  = CreateSolidBrush(isSelected ? RGB(228,241,252) : RGB(242,247,253));
                    HPEN   hPenBg = CreatePen(PS_SOLID, 1, isSelected ? RGB(174,212,243) : RGB(216,231,248));
                    HPEN   hOldP  = (HPEN)SelectObject(hdc, hPenBg);
                    HBRUSH hOldB  = (HBRUSH)SelectObject(hdc, hBrBg);
                    DrawRectOrRoundRect(hdc, rcFullRow.left, rcFullRow.top,
                                        rcFullRow.right, rcFullRow.bottom, 3, 3);
                    SelectObject(hdc, hOldP); SelectObject(hdc, hOldB);
                    DeleteObject(hBrBg); DeleteObject(hPenBg);
                }
                if (hasKeyboardFocus && !isSelected)
                    DrawFocusRectangle(hdc, &rcRow);
                WCHAR ssidBuf[33]; GetDisplaySSID(i, ssidBuf, 33);
                SelectObject(hdc, isSelected ? g_hFontBold : g_hFontNormal);
                SetTextColor(hdc, RGB(0,0,255));
                TextOutW(hdc, rcRow.left+10, rcRow.top+6, ssidBuf, lstrlenW(ssidBuf));

                if (g_NetworkList[i].isConnected) {
                    SelectObject(hdc, g_hFontBold); SetTextColor(hdc, RGB(0,0,0));
                    int textX = isSelected ? (rcRow.left+10)   : (rcRow.right-110);
                    int textY = isSelected ? (rcRow.top+22)    : (rcRow.top+6);
                    TextOutW(hdc, textX, textY, g_CurrentLocale->connectedText, lstrlenW(g_CurrentLocale->connectedText));
                } else if (g_NetworkList[i].isConnecting) {
                    SelectObject(hdc, g_hFontNormal); SetTextColor(hdc, RGB(128,128,128));
                    int textX = isSelected ? (rcRow.left+10)   : (rcRow.right-110);
                    int textY = isSelected ? (rcRow.top+22)    : (rcRow.top+6);
                    TextOutW(hdc, textX, textY, g_CurrentLocale->connecting, lstrlenW(g_CurrentLocale->connecting));
                }
                DrawNativeSignalIcon(hdc, rcRow.right-10, rcRow.top+2, g_NetworkList[i].signalQuality);
            }
        }

        SelectObject(hdc, g_IsHoveringLink ? g_hFontUnderline : g_hFontNormal);
        SetTextColor(hdc, RGB(14,75,184));
        const wchar_t* footerText = g_CurrentLocale->openSharingCenter;
        SIZE textSize; GetTextExtentPoint32W(hdc, footerText, lstrlenW(footerText), &textSize);
        int centerX = (WINDOW_WIDTH - textSize.cx) / 2;
        int footerTextYC = (WINDOW_HEIGHT - FOOTER_HEIGHT) + ((FOOTER_HEIGHT - textSize.cy) / 2) - 5;
        TextOutW(hdc, centerX, footerTextYC, footerText, lstrlenW(footerText));

        BitBlt(hdcReal, 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT, hdc, 0, 0, SRCCOPY);
        SelectObject(hdc, hOldBmp); DeleteObject(hBmp); DeleteDC(hdc);
        EndPaint(hwnd, &ps);
        break;
    }

    case WM_REFRESH_DATA:
        if (g_Ctx.hWlanClient) {
            RefreshWifiData(g_Ctx.hWlanClient);
            UpdateLayoutGeometry();
            InvalidateRect(hwnd,NULL,TRUE);
        }
        break;

    case WM_MOUSEMOVE: {
        int mx = LOWORD(lParam), my = HIWORD(lParam);
        POINT pt = {mx,my};
        RECT rcF = GetFooterRect();
        BOOL wasLink    = g_IsHoveringLink;
        BOOL wasRefresh = g_IsHoveringRefresh;
        BOOL wasArrow   = g_IsHoveringArrow;
        int  wasHov     = g_HoveredRowIndex;
        g_IsHoveringLink    = PtInRect(&rcF, pt) != 0;
        g_IsHoveringRefresh = PtInRect(&g_rcRefreshButton, pt) != 0;
        g_IsHoveringArrow   = PtInRect(&g_rcArrowButton,   pt) != 0;
        int newHovered = (my >= LIST_Y_START && my < LIST_Y_END) ? HitTestRows(mx,my) : -1;
        g_HoveredRowIndex = newHovered;

        if (newHovered != wasHov)
            UpdateTooltipForRow(hwnd, newHovered);

        SetCursor(LoadCursor(NULL, (g_IsHoveringLink || g_IsHoveringRefresh || g_IsHoveringArrow) ? IDC_HAND : IDC_ARROW));
        if (wasLink!=g_IsHoveringLink || wasRefresh!=g_IsHoveringRefresh ||
            wasArrow!=g_IsHoveringArrow || wasHov!=g_HoveredRowIndex) {
            InvalidateRect(hwnd,NULL,FALSE);
            TRACKMOUSEEVENT tme = {sizeof(TRACKMOUSEEVENT),TME_LEAVE,hwnd,0};
            TrackMouseEvent(&tme);
        }
        break;
    }
    case WM_MOUSELEAVE:
        g_IsHoveringLink = g_IsHoveringRefresh = g_IsHoveringArrow = FALSE;
        g_HoveredRowIndex = -1;
        UpdateTooltipForRow(hwnd, -1);
        SetCursor(LoadCursor(NULL,IDC_ARROW));
        InvalidateRect(hwnd,NULL,FALSE);
        break;

    case WM_LBUTTONDOWN: {
        int lx = LOWORD(lParam), ly = HIWORD(lParam);
        POINT pt = {lx,ly};
        RECT rcF = GetFooterRect();
        if (PtInRect(&g_rcRefreshButton,pt)) {
            PostMessageW(hwnd,WM_REFRESH_DATA,0,0); break;
        }
        if (PtInRect(&g_rcArrowButton,pt)) {
            g_bListExpanded = !g_bListExpanded;
            if (!g_bListExpanded) {
                ShowWindow(g_hWndButtonConnect,   SW_HIDE);
                ShowWindow(g_hWndCheckboxConnect, SW_HIDE);
                g_SelectedRowIndex = -1;
                ClearKeyboardFocus();
            } else {
                UpdateLayoutGeometry();
            }
            InvalidateRect(hwnd,NULL,TRUE);
            break;
        }
        if (PtInRect(&rcF,pt)) {
            ShellExecuteW(NULL,L"open",L"control.exe",L"/name Microsoft.NetworkAndSharingCenter",NULL,SW_SHOWNORMAL);
            ShowWindow(hwnd,SW_HIDE);
            break;
        }
        if (g_bListExpanded && ly >= LIST_Y_START && ly < LIST_Y_END) {
            int ci = HitTestRows(lx,ly);
            if (ci != -1) {
                g_SelectedRowIndex = (g_SelectedRowIndex==ci) ? -1 : ci;
                if (g_SelectedRowIndex >= 0)
                    SetKeyboardFocus(g_SelectedRowIndex);
                else
                    ClearKeyboardFocus();
                UpdateLayoutGeometry();
                InvalidateRect(hwnd,NULL,FALSE);
            }
        }
        break;
    }
    case WM_RBUTTONDOWN: {
        int rx = LOWORD(lParam), ry = HIWORD(lParam);
        if (g_bListExpanded && ry >= LIST_Y_START && ry < LIST_Y_END) {
            int ci = HitTestRows(rx,ry);
            if (ci != -1) {
                POINT ptM={rx,ry}; ClientToScreen(hwnd,&ptM);
                ShowContextMenu(hwnd,ci,ptM);
            }
        }
        break;
    }
    case WM_COMMAND: {
        int wid = LOWORD(wParam);
        if (wid == IDC_CONN_BUTTON && g_SelectedRowIndex != -1) {
            if (g_Ctx.hWlanClient) HandleNativeConnection(g_Ctx.hWlanClient,g_SelectedRowIndex);
            break;
        }
        if (g_ContextMenuTargetIndex != -1) {
            switch (wid) {
            case IDM_CONNECT: case IDM_DISCONNECT:
                if (g_Ctx.hWlanClient) HandleNativeConnection(g_Ctx.hWlanClient,g_ContextMenuTargetIndex);
                break;
            case IDM_STATUS:
                if (g_ContextMenuTargetIndex >= 0 && g_ContextMenuTargetIndex < g_NetworkCount)
                    OpenNetworkStatusForInterface(g_NetworkList[g_ContextMenuTargetIndex].interfaceGuid);
                else
                    ShellExecuteW(NULL, L"open", L"control.exe", L"ncpa.cpl", NULL, SW_SHOWNORMAL);
                ShowWindow(hwnd, SW_HIDE);
                break;
            case IDM_PROPERTIES:
                if (g_ContextMenuTargetIndex >= 0 && g_ContextMenuTargetIndex < g_NetworkCount)
                    OpenNetworkPropertiesForInterface(g_NetworkList[g_ContextMenuTargetIndex].interfaceGuid);
                else
                    ShellExecuteW(NULL, L"open", L"control.exe", L"ncpa.cpl", NULL, SW_SHOWNORMAL);
                ShowWindow(hwnd, SW_HIDE);
                break;
            }
        }
        break;
    }
    case WM_ACTIVATE:
        if (LOWORD(wParam) == WA_INACTIVE) {
            ClearKeyboardFocus();
            ShowWindow(hwnd,SW_HIDE);
        }
        break;
    case WM_SAFE_CLOSE:
        DestroyWindow(hwnd);
        break;
    case WM_CLOSE:
        ShowWindow(hwnd, SW_HIDE);
        return 0;
    case WM_DESTROY:
        if (g_RefreshTimer) {
            KillTimer(hwnd, g_RefreshTimer);
            g_RefreshTimer = 0;
        }
        if (g_ConnectCheckTimer) {
            KillTimer(hwnd, g_ConnectCheckTimer);
            g_ConnectCheckTimer = 0;
        }
        InterlockedDecrement(&g_Ctx.refCount);
        if (g_hTooltip) { DestroyWindow(g_hTooltip); g_hTooltip = NULL; }
        g_hWndFlyout = g_hWndButtonConnect = g_hWndCheckboxConnect = NULL;
        break;
    }
    return DefWindowProcW(hwnd,uMsg,wParam,lParam);
}

// -------------------------------------------------------
// ToolbarWindow32 subclassing via WindhawkUtils
// -------------------------------------------------------
LRESULT CALLBACK ToolbarWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam,
                                 UINT_PTR uIdSubclass, DWORD_PTR dwRefData) {
    if (g_Settings.interceptNativeFlyout) {
        if (msg == WM_LBUTTONDOWN || msg == WM_LBUTTONUP || msg == WM_LBUTTONDBLCLK || msg == WM_MOUSEACTIVATE) {
            POINT pt;
            if (msg == WM_MOUSEACTIVATE) {
                DWORD dwPos = GetMessagePos();
                pt.x = GET_X_LPARAM(dwPos);
                pt.y = GET_Y_LPARAM(dwPos);
                ScreenToClient(hWnd, &pt);
            } else {
                pt.x = GET_X_LPARAM(lParam);
                pt.y = GET_Y_LPARAM(lParam);
            }

            LRESULT btnIdx = SendMessageW(hWnd, TB_HITTEST, 0, (LPARAM)&pt);
            if (btnIdx >= 0) {
                TBBUTTON tb = {0};
                if (SendMessageW(hWnd, TB_GETBUTTON, (WPARAM)btnIdx, (LPARAM)&tb)) {
                    if (tb.idCommand == TRAY_NETWORK_ID) {
                        if (msg == WM_LBUTTONUP) {
                            static DWORD lastClickTime = 0;
                            DWORD currentTime = GetTickCount();
                            if (currentTime - lastClickTime > CLICK_DEBOUNCE_MS) {
                                lastClickTime = currentTime;
                                if (g_hWndFlyout && IsWindowVisible(g_hWndFlyout)) {
                                    Wh_Log(L"Network icon clicked while open — closing classic flyout");
                                    ToggleFlyoutWindow();
                                } else {
                                    Wh_Log(L"Network icon clicked while closed — opening classic flyout");
                                    SetTimer(hWnd, 9999, 10, NULL);
                                }
                            }
                        }
                        if (msg == WM_MOUSEACTIVATE)
                            return MA_ACTIVATE;
                        return 0;
                    }
                }
            }
        }
    }

    if (msg == WM_TIMER && wParam == 9999) {
        KillTimer(hWnd, 9999);
        ToggleFlyoutWindow();
        return 0;
    }

    return DefSubclassProc(hWnd, msg, wParam, lParam);
}

static bool IsExplorerProcess() {
    WCHAR exePath[MAX_PATH] = {};
    GetModuleFileNameW(NULL, exePath, MAX_PATH);
    WCHAR* name = wcsrchr(exePath, L'\\');
    name = name ? name + 1 : exePath;
    return _wcsicmp(name, L"explorer.exe") == 0;
}

void InstallTrayInterception() {
    Wh_Log(L"Installing ToolbarWindow32 interception...");

    if (!IsExplorerProcess()) {
        Wh_Log(L"Not explorer.exe - skipping ToolbarWindow32 subclassing");
        return;
    }

    HWND hTray = NULL;
    for (int attempt = 0; attempt < 10 && !hTray; attempt++) {
        hTray = FindWindowW(L"Shell_TrayWnd", NULL);
        if (!hTray) {
            Wh_Log(L"Shell_TrayWnd not found (attempt %d/10), waiting 500ms...", attempt + 1);
            Sleep(500);
        }
    }

    if (!hTray) {
        Wh_Log(L"ERROR: Shell_TrayWnd not found after retries - tray subclassing skipped");
        return;
    }

    HWND hNotify  = FindWindowExW(hTray,    NULL, L"TrayNotifyWnd",   NULL);
    HWND hSysPager= hNotify ? FindWindowExW(hNotify,  NULL, L"SysPager",        NULL) : NULL;
    HWND hToolbar = hSysPager ? FindWindowExW(hSysPager,NULL, L"ToolbarWindow32", NULL) : NULL;

    HWND hTarget = hToolbar ? hToolbar : (hNotify ? hNotify : hTray);
    if (!hTarget) {
        Wh_Log(L"ERROR: tray hierarchy not found");
        return;
    }

    G_hSubclassedToolbar = hTarget;
    BOOL success = WindhawkUtils::SetWindowSubclassFromAnyThread(hTarget, ToolbarWndProc, (UINT_PTR)&g_Ctx, 0);
    if (success)
        Wh_Log(L"ToolbarWindow32 subclassed OK (0x%p)", hTarget);
    else {
        Wh_Log(L"ERROR: subclassing failed");
        G_hSubclassedToolbar = nullptr;
    }

    Wh_Log(L"Tray interception installed");
}

void RemoveTrayInterception() {
    if (G_hSubclassedToolbar) {
        WindhawkUtils::RemoveWindowSubclassFromAnyThread(G_hSubclassedToolbar, ToolbarWndProc, (UINT_PTR)&g_Ctx);
        Wh_Log(L"ToolbarWindow32 subclass removed");
        G_hSubclassedToolbar = nullptr;
    }
}

// -------------------------------------------------------
// Toggle flyout
// -------------------------------------------------------
void ToggleFlyoutWindow() {
    Wh_Log(L"ToggleFlyoutWindow");
    EnterCriticalSection(&g_Ctx.csLock);
    if (!g_Ctx.isUninitializing) {
        if (!g_hWndFlyout || !IsWindow(g_hWndFlyout)) {
            HINSTANCE hInst = GetModuleHandle(NULL);
            WNDCLASSW wc = {0};
            wc.style         = 0;
            wc.lpfnWndProc   = FlyoutWndProc;
            wc.hInstance     = hInst;
            wc.lpszClassName = L"Win7NetworkFlyoutSafe";
            wc.hCursor       = LoadCursor(NULL,IDC_ARROW);
            wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
            UnregisterClassW(wc.lpszClassName,hInst);
            RegisterClassW(&wc);
            RECT rcClient = { 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT };
            AdjustWindowRectEx(&rcClient, WS_POPUP, FALSE, WS_EX_TOPMOST|WS_EX_TOOLWINDOW|WS_EX_LEFT);
            g_hWndFlyout = CreateWindowExW(
                WS_EX_TOPMOST|WS_EX_TOOLWINDOW|WS_EX_LEFT,
                wc.lpszClassName, L"", WS_POPUP,
                0, 0, rcClient.right-rcClient.left, rcClient.bottom-rcClient.top,
                NULL, NULL, hInst, NULL);
            if (g_hWndFlyout) {
                SetWindowLongPtrW(g_hWndFlyout, GWL_STYLE,
                    GetWindowLongPtrW(g_hWndFlyout, GWL_STYLE) | WS_THICKFRAME);
                BOOL pfEnabled = FALSE;
                if (DwmIsCompositionEnabled(&pfEnabled) == S_OK && pfEnabled) {
                    MARGINS margins = {0, 0, 0, 1};
                    DwmExtendFrameIntoClientArea(g_hWndFlyout, &margins);
                }
                g_Ctx.hWndFlyout = g_hWndFlyout;
            }
            Wh_Log(L"Flyout window created");
        }
        if (IsWindowVisible(g_hWndFlyout)) {
            ClearKeyboardFocus();
            ShowWindow(g_hWndFlyout, SW_HIDE);
        } else {
            DetermineLocale();
            LoadSettings();
            g_SelectedRowIndex = g_HoveredRowIndex = -1;
            ClearKeyboardFocus();
            g_bListExpanded = TRUE;
            ShowWindow(g_hWndButtonConnect,   SW_HIDE);
            ShowWindow(g_hWndCheckboxConnect, SW_HIDE);
            if (g_Ctx.hWlanClient) RefreshWifiData(g_Ctx.hWlanClient);
            UpdateLayoutGeometry();
            PositionWindowNearTray(g_hWndFlyout);
            ShowWindow(g_hWndFlyout, SW_SHOW);
            SetForegroundWindow(g_hWndFlyout);
            InvalidateRect(g_hWndFlyout,NULL,TRUE);
        }
    }
    LeaveCriticalSection(&g_Ctx.csLock);
}

// -------------------------------------------------------
// Hotkey thread
// -------------------------------------------------------
DWORD WINAPI HotkeyThreadProc(LPVOID lpParam) {
    ModContext* ctx = (ModContext*)lpParam;
    if (!ctx) return 1;
    
    BOOL hotkeyRegistered = FALSE;
    if (g_Settings.enableHotkey) {
        if (!RegisterHotKey(NULL, HOTKEY_ID, MOD_CONTROL | MOD_NOREPEAT, 'H')) {
            Wh_Log(L"Failed to register hotkey"); 
            return 1;
        }
        hotkeyRegistered = TRUE;
        Wh_Log(L"Hotkey registered - Ctrl+H");
    } else {
        Wh_Log(L"Hotkey disabled by user setting");
    }

    UINT uTaskbarCreated = RegisterWindowMessageW(L"TaskbarCreated");

    MSG msg = {0};
    while (GetMessageW(&msg, NULL, 0, 0)) {
        if (msg.message == WM_HOTKEY && msg.wParam == HOTKEY_ID && 
            !ctx->isUninitializing && hotkeyRegistered) {
            ToggleFlyoutWindow();
        }
        if (msg.message == uTaskbarCreated && !ctx->isUninitializing) {
            Wh_Log(L"WM_TASKBARCREATED received — reinstalling tray interception");
            if (G_hSubclassedToolbar) RemoveTrayInterception();
            InstallTrayInterception();
            
            if (g_Settings.enableHotkey && !hotkeyRegistered) {
                if (RegisterHotKey(NULL, HOTKEY_ID, MOD_CONTROL | MOD_NOREPEAT, 'H')) {
                    hotkeyRegistered = TRUE;
                    Wh_Log(L"Hotkey re-registered after taskbar restart");
                }
            }
        }
        TranslateMessage(&msg); 
        DispatchMessageW(&msg);
    }
    
    if (hotkeyRegistered)
        UnregisterHotKey(NULL, HOTKEY_ID);
    
    return 0;
}

// -------------------------------------------------------
// Cleanup
// -------------------------------------------------------
void SafeCleanup() {
    Wh_Log(L"SafeCleanup");
    if (InterlockedExchange(&g_Ctx.isUninitializing,1L)) return;
    RemoveTrayInterception();
    if (g_Ctx.dwHotkeyThreadId) PostThreadMessageW(g_Ctx.dwHotkeyThreadId,WM_QUIT,0,0);
    if (g_Ctx.hHotkeyThread) {
        WaitForSingleObject(g_Ctx.hHotkeyThread,3000);
        CloseHandle(g_Ctx.hHotkeyThread);
        g_Ctx.hHotkeyThread=NULL; g_Ctx.dwHotkeyThreadId=0;
    }
    if (g_hWndFlyout && IsWindow(g_hWndFlyout)) {
        BOOL pfEnabled = FALSE;
        if (DwmIsCompositionEnabled(&pfEnabled) == S_OK && pfEnabled) {
            MARGINS margins = {0, 0, 0, 0};
            DwmExtendFrameIntoClientArea(g_hWndFlyout, &margins);
        }
        SendMessageW(g_hWndFlyout,WM_SAFE_CLOSE,0,0);
        for (int i=0; i<50 && IsWindow(g_hWndFlyout); i++) {
            Sleep(100);
            MSG msg;
            while (PeekMessageW(&msg,NULL,0,0,PM_REMOVE)) { TranslateMessage(&msg); DispatchMessageW(&msg); }
        }
        if (IsWindow(g_hWndFlyout)) DestroyWindow(g_hWndFlyout);
    }
    if (g_Ctx.hWlanClient) { WlanCloseHandle(g_Ctx.hWlanClient,NULL); g_Ctx.hWlanClient=NULL; }
    if (g_pNLM) { g_pNLM->Release(); g_pNLM = NULL; }
    FreeSystemIcons();
    FreeGlobalFonts();
    g_hWndFlyout=g_hWndButtonConnect=g_hWndCheckboxConnect=NULL;
    g_Initialized=FALSE;
}

// -------------------------------------------------------
// Windhawk entry points
// -------------------------------------------------------
BOOL Wh_ModInit() {
    Wh_Log(L"=== Wh_ModInit v1.1.4 ===");
    LoadSettings();
    UpdateClassicThemeCache();
    ZeroMemory(&g_Ctx,sizeof(g_Ctx));
    InitializeCriticalSection(&g_Ctx.csLock);
    DetermineLocale();

    if (!IsExplorerProcess()) {
        Wh_Log(L"Not explorer.exe - init skipped");
        g_Initialized = TRUE;
        return TRUE;
    }

    InitGlobalFonts();
    LoadSystemIcons();
    InitRefreshButtonRect();
    RecalcArrowRect();

    InstallTrayInterception();

    if (g_Settings.redirectNetworkContextMenu) {
        HMODULE hUser32 = GetModuleHandleW(L"user32.dll");
        if (hUser32) {
            void* pFn = (void*)GetProcAddress(hUser32, "TrackPopupMenuEx");
            if (pFn) {
                Wh_SetFunctionHook(pFn, (void*)TrackPopupMenuEx_Hook, (void**)&g_origTrackPopupMenuEx);
                Wh_Log(L"TrackPopupMenuEx hooked");
            }
        }
    }

    DWORD dwMaxClient=2, dwCurVer=0;
    if (WlanOpenHandle(dwMaxClient,NULL,&dwCurVer,&g_Ctx.hWlanClient)==ERROR_SUCCESS) {
        Wh_Log(L"WLAN OK");
        WlanRegisterNotification(g_Ctx.hWlanClient, WLAN_NOTIFICATION_SOURCE_ALL, TRUE,
                                 WlanNotificationCallback, &g_Ctx, NULL, NULL);
    } else {
        Wh_Log(L"WLAN init failed");
    }

    g_Ctx.hHotkeyThread = CreateThread(NULL,0,HotkeyThreadProc,&g_Ctx,0,&g_Ctx.dwHotkeyThreadId);
    if (!g_Ctx.hHotkeyThread) {
        Wh_Log(L"Hotkey thread failed");
        if (g_Ctx.hWlanClient) { WlanCloseHandle(g_Ctx.hWlanClient,NULL); g_Ctx.hWlanClient=NULL; }
        DeleteCriticalSection(&g_Ctx.csLock);
        return FALSE;
    }
    g_Initialized=TRUE;
    Wh_Log(L"=== Wh_ModInit done ===");
    return TRUE;
}

void Wh_ModSettingsChanged() {
    LoadSettings();
    UpdateClassicThemeCache();

    if (SafeToAccessUI() && g_hWndFlyout) {
        if (g_RefreshTimer) {
            KillTimer(g_hWndFlyout, g_RefreshTimer);
            g_RefreshTimer = 0;
        }
        if (g_Settings.refreshInterval > 0) {
            g_RefreshTimer = SetTimer(g_hWndFlyout, 1000, g_Settings.refreshInterval, NULL);
            Wh_Log(L"Auto-refresh timer updated: %d ms", g_Settings.refreshInterval);
        }
        InvalidateRect(g_hWndFlyout,NULL,TRUE);
    }
}

void Wh_ModUninit() {
    Wh_Log(L"Wh_ModUninit");
    SafeCleanup();
    UnregisterClassW(L"Win7NetworkFlyoutSafe", GetModuleHandle(NULL));
    UnregisterClassW(L"Win7NetPwdClass", GetModuleHandle(NULL));
    DeleteCriticalSection(&g_Ctx.csLock);
}

@m417z

m417z commented Jun 16, 2026

Copy link
Copy Markdown
Member

The error is:
error: too many arguments to function call, expected 3, have 4

It's not a Windhawk bug or a compiler bug, it's an incorrect usage of the functions. The text says what's wrong.

@babamohammed2022

babamohammed2022 commented Jun 16, 2026

Copy link
Copy Markdown
Contributor Author

The error is: error: too many arguments to function call, expected 3, have 4

It's not a Windhawk bug or a compiler bug, it's an incorrect usage of the functions. The text says what's wrong.

My bad, I think that I've had some problems with my environment since I had some other compilation errors as well. I'll try to post a code that should fix the reported issue by Anixx too. If it doesn't work, I will revert to the previous version to find another way to solve it.
I've tested myself again and the redirect seems to work
image
(Tested on Windows 10 21H2)

@Anixx

Anixx commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

The recent version does not work, only the standard flyout is shown.

@babamohammed2022

Copy link
Copy Markdown
Contributor Author

The recent version does not work, only the standard flyout is shown.

OK thanks for telling me, could you just show me a picture of the mod's settings & logs just so that I can understand further?

@Anixx

Anixx commented Jun 16, 2026

Copy link
Copy Markdown
Contributor
изображение

20:10:28.525 10100 explorer.exe [WH] [local@win7-network-flyout-recreation] [2148:Wh_ModInit]: === Wh_ModInit v1.1.4 ===
20:10:28.526 10100 explorer.exe [WH] [local@win7-network-flyout-recreation] [190:LoadSettings]: Settings loaded - intercept:1 privacy:0 redirectCtx:1 refresh:3000 language:0 classicCorners:0
20:10:28.526 10100 explorer.exe [WH] [local@win7-network-flyout-recreation] [402:DetermineLocale]: Locale: English
20:10:28.535 10100 explorer.exe [WH] [local@win7-network-flyout-recreation] [1952:InstallTrayInterception]: Installing ToolbarWindow32 interception...
20:10:28.535 10100 explorer.exe [WH] [local@win7-network-flyout-recreation] [1986:InstallTrayInterception]: ToolbarWindow32 subclassed OK (0x0000000000020224)
20:10:28.535 10100 explorer.exe [WH] [local@win7-network-flyout-recreation] [1992:InstallTrayInterception]: Tray interception installed
20:10:28.535 10100 explorer.exe [WH] [local@win7-network-flyout-recreation] [2174:Wh_ModInit]: TrackPopupMenuEx hooked
20:10:28.536 10100 explorer.exe [WH] [local@win7-network-flyout-recreation] [2181:Wh_ModInit]: WLAN OK
20:10:28.537 10100 explorer.exe [WH] [local@win7-network-flyout-recreation] [2196:Wh_ModInit]: === Wh_ModInit done ===
20:10:28.713 10100 explorer.exe [WH] [local@win7-network-flyout-recreation] [2074:HotkeyThreadProc]: Hotkey registered - Ctrl+H

@Anixx

Anixx commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

By the way, it works via Ctrl+H, but does not via menu.

@Anixx

Anixx commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

Connect action does not work, as before, waits, then an error messagebox.
The connection name is truncated, just as before.
Disable rounded corners option does not work. And I would say, that the window should draw the native window frame (from the active theme) by default, not via some option.

@babamohammed2022

Copy link
Copy Markdown
Contributor Author

Why to check for "Network" in tooltip, if it is not there in any language?

The tooltip method doesn't look for the word "Network" specifically but it looks for keywords related to networking/internet in many languages, including Russian (сеть, интернет, подключение, доступ) and many languages like Dutch, Spanish, Italian, etc.
But you've already pointed out the real issue: on your system, the tooltip shows the connection name (SSID) followed by "Internet access" in Russian which could not always work.
Therefore, I've added Methods 4 and 7 as fallbacks. The tooltip method (method 2) is still there because it could still work on some systems.
With v1.5.0, if methods 1, 2, and 3 all fail on your system, methods 4 and 7 should take over and find the icon by style filtering and multi-variant icon comparison.

@babamohammed2022

Copy link
Copy Markdown
Contributor Author

What if the network is local (no Wi-Fi icon)?

I'll add Ethernet icon variants to the comparison so it works on wired-only systems too. I'll keep iterating until all reasonable cases are covered

@babamohammed2022

Copy link
Copy Markdown
Contributor Author

"Method 4 (style filter): It discards buttons with suspicious styles (marked or dropdown) that cannot be the network icon." - why is this? It cannot be drop-down and your problem is already filtering out too much, not including too much.

You're right with this If you think it's unnecessary complexity, I can remove it in the next version.

@Anixx

Anixx commented Jun 20, 2026

Copy link
Copy Markdown
Contributor

Why would you check for подключение or сеть, if we for sure know such words are not there? Fior sure. Even worse, it could bring the flyout to third-party tray buttons, such as network load indicators, internet downloaders, VPN, torrent, whatever.

@babamohammed2022

Copy link
Copy Markdown
Contributor Author

Why would you check for подключение or сеть, if we for sure know such words are not there? Fior sure. Even worse, it could bring the flyout to third-party tray buttons, such as network load indicators, internet downloaders, etc.

Well you're right with this but since I don't know Russian, I added context-related words I found rather than the exact tooltip text. You're right that this is both inaccurate and risky for false positives with third-party apps. I'll remove these 2

@Anixx

Anixx commented Jun 20, 2026

Copy link
Copy Markdown
Contributor

Only two? Currently it's "Доступ к Интернету", you are checking for just "интернет" (not capitalized). What about other languages? If you keep checking for the word "internet", lots of software would show your flyout.

@babamohammed2022

Copy link
Copy Markdown
Contributor Author

Only two? Currently it's "Доступ к Интернету", you are checking for just "интернет" (not capitalized). What about other languages? If you keep checking for the word "internet", lots of software would show your flyout.

OK, I'll directly remove this approach for now and post the version without this method and I won't add Ethernet detection for now

@babamohammed2022

Copy link
Copy Markdown
Contributor Author

Only two? Currently it's "Доступ к Интернету", you are checking for just "интернет" (not capitalized). What about other languages? If you keep checking for the word "internet", lots of software would show your flyout.

I've removed this approach, now the mod relies on ID 2 matching, multi-variant Wi-Fi icon comparison and system stock icon.

  • Tested on Windows 11 24H2

@Anixx

Anixx commented Jun 20, 2026

Copy link
Copy Markdown
Contributor

The detection works now, except the ethernet or remote access cases. Re-connect does not work.

Othe networks are not visible:

изображение

@babamohammed2022

babamohammed2022 commented Jun 20, 2026

Copy link
Copy Markdown
Contributor Author

The detection works now, except the ethernet or remote access cases. Re-connect does not work.

Othe networks are not visible:

изображение

I don't think Ethernet detection is useful here, the flyout is Wi-Fi-centric and an Ethernet-only machine has no wireless networks to show, it would be redundant in my opinion. But if you have a specific use case in mind, let me know.
About the reconnect, I don't think that I can do more to fix this anymore since there have been multiple attempts to do so and it can't always depend on my mod, same thing with the networks being not visibile. This could be more of an issue with Windows or the API used in the OS because sometimes I have a similar problem with connections disappearing for a while, but it also occurs with the modern flyout, which is usually caused by driver issues, power management settings, or software conflicts. I've checked multiple times and the issue occurs in some cases but trying to force-fix in every possible way this issue would be many times more complex than a Windhawk mod. If you have any suggestions to help me fix this issue, let me know.

@Anixx

Anixx commented Jun 20, 2026

Copy link
Copy Markdown
Contributor

No, it never happens with modern flyout. Also, currently your flyout is simply empty, the re-scan button does not work. If it can scan the networks on load, why it cannot some minutes later?

@babamohammed2022

Copy link
Copy Markdown
Contributor Author

No, it never happens with modern flyout. Also, currently your flyout is simply empty, the re-scan button does not work. If it can scan the networks on load, why it cannot some minutes later?

Your point is valid, I will investigate further to improve this behavior. However, I actually have the same issue in both flyouts in some cases.

@babamohammed2022

Copy link
Copy Markdown
Contributor Author

No, it never happens with modern flyout. Also, currently your flyout is simply empty, the re-scan button does not work. If it can scan the networks on load, why it cannot some minutes later?

I've tried to fix the issue by making the network list refresh more reliable, both automatically and when clicking the refresh button. Let me know if this solves the previous reported issue

@babamohammed2022

Copy link
Copy Markdown
Contributor Author

@Anixx if you want, please let me know if the issue of the networks disappearing has been fixed

@babamohammed2022

Copy link
Copy Markdown
Contributor Author

@m417z Hello! All 13 points from the June 16 review have been addressed. I've also removed the registry fallback entirely to make the mod lighter and also because the tray interception handles everything and the mod is now simpler. I've tested on Win10 21H2, Win11 23H2/24H2/25H2 with ExplorerPatcher. Could you take another look when you have time? Thank you.

@Anixx

Anixx commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

Yes, the disappearing is fixed, but re-connecting does not work, so the mod is useless as of now. Maybe it is because the mod still does not account for the network's index (for handling networks with the same name)? The standard flyout calls the network "Redmi 2", not just "Redmi"

@babamohammed2022

Copy link
Copy Markdown
Contributor Author

Yes, the disappearing is fixed, but re-connecting does not work, so the mod is useless as of now. Maybe it is because the mod still does not account for the network's index (for handling networks with the same name)? The standard flyout calls the network "Redmi 2", not just "Redmi"

Thanks for the feedback. I've tried to fix it. However, I currently don't have networks with duplicate names in my area, so I can't fully test this myself but now the mod now keeps separate entries for same-SSID networks with different security parameters, adds a numeric suffix like Windows does ('Redmi 2'). Can you test it and let me know if reconnect works now?

@Anixx

Anixx commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

It is not two networks with the same name at the same time, it is networks with the same name at different times. Windows remembers names.

@babamohammed2022

Copy link
Copy Markdown
Contributor Author

It is not two networks with the same name at the same time, it is networks with the same name at different times. Windows remembers names.

OK, the mod was finding the old profile, assuming it was valid, and failing because the security parameters didn't match the current network. I've posted a fix that should detect this and forces a connection when the profile doesn't match. Can you test v2.3.0 to see if it finally works? If both issues are addressed the mod should be ready.

@Anixx

Anixx commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

Does not work. Now it asks for a security key even for a known network. Even after entering it, fails to reconnect.
The "Hide characters" checkbox shows the characters, not hides.
The dialog uses wrong font.

@babamohammed2022

babamohammed2022 commented Jun 23, 2026

Copy link
Copy Markdown
Contributor Author

Does not work. Now it asks for a security key even for a known network. Even after entering it, fails to reconnect. The "Hide characters" checkbox shows the characters, not hides. The dialog uses wrong font.

immagine

I'm currently working on a fix for the reported issues.

@babamohammed2022

Copy link
Copy Markdown
Contributor Author

Now if the connection doesn't work it will show this error:
immagine

@babamohammed2022

Copy link
Copy Markdown
Contributor Author

Does not work. Now it asks for a security key even for a known network. Even after entering it, fails to reconnect. The "Hide characters" checkbox shows the characters, not hides. The dialog uses wrong font.

The UI issues should have been addressed and now it shouldn't ask for the security key even for a known network. Let me know if the issue with reconnection finally works. For now, I've tested on Windows 10 21H2 and the reconnect works for a known network and fails for networks with the wrong password saved but, according to Google and my analysis, this is also an issue with Windows which would require complex solutions to totally solve because this is actually the same behavior as the native Windows network flyout. I'd like to know your opinion if it's useful to put this as a limitation in the README of the mod.

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.

3 participants