Skip to content

Instantly share code, notes, and snippets.

@cw2k
Last active February 15, 2026 20:36
Show Gist options
  • Select an option

  • Save cw2k/26e1818303e0061e38d6a612ac52b1d5 to your computer and use it in GitHub Desktop.

Select an option

Save cw2k/26e1818303e0061e38d6a612ac52b1d5 to your computer and use it in GitHub Desktop.
Windows10: Removes Nag "Activate Windows" in Windows 10
<#
RemoveNag2_ActivateWindows.ps1
--------------------------------
Purpose:
Removes that "Activate Windows" watermark created by explorer.exe
on Windows 10 systems.
The script attempts a clean shutdown of the hidden Worker Window
hosting the watermark by sending WM_DESTROY and WM_CLOSE.
If the window survives, it falls back to SW_HIDE.
Source Reference:
Self: https://gist.github.com/cw2k/26e1818303e0061e38d6a612ac52b1d5
Based on the technique described by Paul π on SuperUser:
https://superuser.com/a/1908410
Retrieved 2026‑02‑14, License: CC BY‑SA 4.0
Auto‑Run Instructions:
1. Create a shortcut (*.lnk) to this script.
2. Edit the shortcut target and prepend:
powershell.exe -ExecutionPolicy Bypass -File
3. Test the shortcut.
4. Move it into the Startup folder:
shell:startup
Notes:
• This script does NOT modify system files or licensing components.
• It only interacts with the watermark window created by explorer.exe.
• WM_DESTROY does not fully destroy the window; WM_CLOSE is required.
• If explorer recreates the watermark, the script can be run again.
#>
function Hide-Watermark {
# Load Win32 API definitions once
if (-not ([System.Management.Automation.PSTypeName]'Win32').Type) {
Add-Type @"
using System;
using System.Runtime.InteropServices;
public class Win32
{
[DllImport("user32.dll")]
public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll", SetLastError=true)]
public static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll")]
public static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
}
"@
}
$className = "Worker Window"
$windowTitle = ""
# Try to locate the watermark window
$hWnd = [Win32]::FindWindow($className, $windowTitle)
if ($hWnd -ne [IntPtr]::Zero) {
$hex = ("0x{0:X}" -f $hWnd.ToInt64())
"Class: '$className' handle: $hex"
"Sending WM_DESTROY and WM_CLOSE ..."
$WM_DESTROY = 0x0002
$WM_CLOSE = 0x0010
[void][Win32]::SendMessage($hWnd, $WM_DESTROY, [IntPtr]::Zero, [IntPtr]::Zero)
[void][Win32]::SendMessage($hWnd, $WM_CLOSE, [IntPtr]::Zero, [IntPtr]::Zero)
$hWnd = [Win32]::FindWindow($className, $windowTitle)
if ($hWnd -eq [IntPtr]::Zero) {
"'Activate Windows' watermark successfully destroyed."
}
else {
"Fallback: hiding watermark window ..."
$SW_SHOW = 5
$SW_HIDE = 0
[Win32]::ShowWindow($hWnd, $SW_HIDE)
}
}
else {
# If watermark not found, check if Explorer is running
$hWnd = [Win32]::FindWindow("Shell_TrayWnd", $windowTitle)
if ($hWnd -eq [IntPtr]::Zero) {
Write-Error "Explorer.exe is not running."
}
else {
"'Activate Windows' watermark not found or already removed."
}
}
}
Hide-Watermark
@cw2k
Copy link
Author

cw2k commented Feb 15, 2026

And here the WndProc from the M$ Explorer.exe for the Watermark

<#		
	Alle relevanten WM\_ Codes aus deinem WndProc
	Ich gebe dir die komplette Liste, exakt wie _NotificationWndProc sie verarbeitet.
	
	🔹 Standard Windows Messages
	Name	Hex	Bedeutung
	WM_DESTROY	0x0002	Fenster zerstört
	WM_CLOSE	0x0010	Fenster schließen

	WM_COMMAND	0x0111	Klick / Menü -WParam 0x64
	WM_TIMER	0x0113	Timer -WParam 0x65
	WM_MOUSEFIRST	0x0200	Mausbewegung
	WM_THEMECHANGED	0x031A	Theme geändert
	WM_DISPLAYCHANGE	0x007E	Display geändert
	WM_SETTINGCHANGE	0x001A	System‑Einstellungen
	WM_SYSCOLORCHANGE	0x0015	Farben geändert
	WM_PAINT	0x000F	Repaint
	WM_ACTIVATE	0x0006	Aktivierung
	WM_ACTIVATEAPP	0x001C	App‑Aktivierung
	WM_KEYDOWN	0x0100	Taste gedrückt -WParam 0x0D (ENTER)
	WM_DRAWITEM	0x002B	Owner‑draw
	WM_ERASEBKGND	0x0014	Hintergrund löschen

	🔹 Custom Messages (Shell / Watermark)
	Name	Hex	Bedeutung
	DDM_DRAW	0x500	Watermark zeichnen
	DDM_END	0x501	Lizenz‑Änderung
	NIN_BALLOONUSERCLICK	0x405	Klick auf Balloon *Closes WM-Label
	NIN_POPUPOPEN	0x406	Tray‑Popup geöffnet
	NIN_POPUPCLOSE	0x407	Tray‑Popup geschlossen
#>
LRESULT __fastcall CImmersiveWatermark::_NotificationWndProc(
    struct_a1_Struct* a1,
    UINT msg,
    WPARAM wParam,
    LPARAM lParam)
{
    const LRESULT Discard = 0;

    switch (msg)
    {
        case WM_COMMAND:
            // User clicked the watermark or a related UI element.
            // Attempt to open the activation settings page.		
            if (CImmersiveWatermark::_Watermark_OpenSettingsPageActivate__WPARAM__0x64(a1, wParam) < 0)
			  return Discard;

        case WM_TIMER:
            // Timer-driven state machine (fade-in, fade-out, repositioning, etc.)		
            if (CImmersiveWatermark::Watermark_Timer(a1, wParam) >= 0)
                return Discard;

        case WM_MOUSEFIRST:
            // First mouse event after the watermark becomes visible.
            if (a1->latch_MOUSEFIRST)
            {
                a1->latch_MOUSEFIRST = 0;
                InvalidateRect(a1->hWnd_SendMessage, 0, 0);
                return Discard;
            }

        case DDM_DRAW:
            // Custom Shell message: render the watermark.
            CImmersiveWatermark::_Activation_Watermark_Render(a1);
            return Discard;

        case DDM_END:
            // Licensing state changed (activation, grace period, etc.)
            CImmersiveWatermark::_LicenseChangeNotification(a1);
            return Discard;

        case NIN_BALLOONUSERCLICK:
            if (CImmersiveWatermark::UpdateRegTimestamp(a1, lParam) < 0)
                return Discard;
            return 1;

        case NIN_POPUPOPEN:
            // Tray popup opened → watermark may need to update.
            CImmersiveWatermark::_NIN_POPUPOPEN(a1);
            return Discard;

        case NIN_POPUPCLOSE:
            // Tray popup closed → register licensing policy watcher.
			//  ~ See other me other post~
            CImmersiveWatermark::_RegisterLicensingPolicyChangeEvent(a1);
            return Discard;

        case WM_THEMECHANGED:
        case WM_DISPLAYCHANGE:
        case WM_SYSCOLORCHANGE:
            // DPI, theme, or system settings changed.
            if (CImmersiveWatermark::_OnDisplayChange(a1) >= 0)
                return Discard;

        case WM_SETTINGCHANGE:
            if (wParam == 47)
            {
                if ((a1->ExStyle == 2 || a1->ExStyle == 3) && a1->byte101_flag1_WM_SETTINGCHANGE)
                {
                    if (CImmersiveWatermark::UpdateLayeredWindo(a1) >= 0)
                        return Discard;
                }
            }
            else if (wParam == 67)
            {
                if (CImmersiveWatermark::_OnDisplayChange(a1) >= 0)
                    return Discard;
            }

            if (E_FAIL >= 0)
                return Discard;

        case WM_KEYDOWN:
            // Forward keyboard events to the "SendMessage" window.
            if (wParam == VK_TAB) //9
            {
                SetFocus(a1->hWnd_SendMessage);
                return Discard;
            }
            if (wParam == VK_RETURN  || wParam == VK_ADD) //13 43
            {
                SendMessageW(a1->hWnd_SendMessage, WM_KEYDOWN, wParam, lParam);
                return Discard;
            }

        case WM_DRAWITEM:
            // Owner-draw rendering for specific UI elements.
            if (((DRAWITEMSTRUCT*)lParam)->CtlID == 4)
            {
                if (CImmersiveWatermark::____WM_DRAWITEM(a1, lParam) >= 0)
                    return Discard;
            }

        case WM_ACTIVATEAPP:
        case WM_ACTIVATE:
            if (a1->ExStyle != 1)
                goto DefWindowProc
            if (!wParam && a1->byte100_flag0_SendNIN_POPUPOPEN)
                SetTimer(a1->hWnd_Messages, 0x65, 500, 0);
            return Discard;

        case WM_PAINT:
            // Only paint if the watermark is in the correct mode.
            if (a1->ExStyle != 1)
                goto DefWindowProc
            if (CImmersiveWatermark::PAint(a1) >= 0)
                return Discard;

        case WM_DESTROY:
            // Cleanup internal resources, but does NOT destroy the window.
            if (a1->hWnd_Messages)
            {
                a1->byte101_flag1_WM_SETTINGCHANGE = 0;
                SetWindowLongPtrW(a1->hWnd_Messages, -21, 0);
                CImmersiveWatermark::send_DDM_BEGIN(a1);
            }

        case WM_CLOSE:
            return Discard;

        case WM_ERASEBKGND:
            return 1;

        default:
    }
	DefWindowProc:
	return DefWindowProcW(a1->hWnd_Messages, msg, wParam, lParam);

}

An here if you ever wondered what
HKCU\Software\Microsoft\Windows\CurrentVersion\Watermark
Timestamp
is used for:

__int64 __fastcall CImmersiveWatermark__UpdateRegTimestamp(struct_a1_Struct* a1, int a2)
{

    a1->TimestampForETWLogging = GetTickCount64();

    int v5 = a2 & 0xF;

    if ((v5 == a1->BitmapType && a1->ActivationWatermark)
        || (a1->BitmapType = v5, a1->WatermarkType = a2, !a1->hWnd_Messages)
        || ((v4 = CImmersiveWatermark::_Activation_Watermark_Render(a1)), v4 >= 0))
    {
        if (a1->BitmapType == 2 || a1->BitmapType == 3)
        {
            CImmersiveWatermark__UpdateLayeredWindo(a1);
        }
        else
        {
            a1->byte100_flag0_SendNIN_POPUPOPEN = 0;
            ShowWindow (a1->hWnd_SendMessage, 0);
            ShowWindow( a1->hWnd_Messages, 1);
            SetFocus(   a1->hWnd_Messages);
        }

        a1->byte101_flag1_WM_SETTINGCHANGE = 1;

        if (ETWLogging > 5 && tlgKeywordOn(&ETWLogging, 0x200000000000LL))
        {
            DisplayTime = GetTickCount64() - a1->TimestampForETWLogging;
            BitmapType = a1->BitmapType;

            tlgWriteTransfer_EventWriteTransfer__(
                &ETWLogging,
                &ActivationWatermark,
                0,
                0,
                &BitmapType,
                &DisplayTime);
        }

        if (CImmersiveWatermark___RegTimestampCheck(a1))
            CImmersiveWatermark___RegTimestampSet();
    }

    return v4;
}

... it is triggered through msg NIN_BALLOONUSERCLICK
And just updates every 24 hour the timestampvalue
(maybe to read out by some other programm)

LSTATUS __fastcall CImmersiveWatermark___RegTimestampSet()
{
    result = RegCreateKeyExW(
        HKEY_CURRENT_USER,
        L"Software\\Microsoft\\Windows\\CurrentVersion\\Watermark",
        0, nullptr, 0,
        KEY_WRITE | KEY_QUERY_VALUE,
        nullptr, &hKey, nullptr);

    if (!result)
    {
        GetSystemTimeAsFileTime(&ft);

        result = RegSetKeyValueW(
            hKey, nullptr,
            L"Timestamp",
            REG_BINARY,
            &ft,sizeof(ft));

        if (!result && ETWLogging > 5)
        {
            if (tlgKeywordOn(&ETWLogging, 0x200000000000LL))
                tlgWriteTransfer_EventWriteTransfer(
                    0, &ActivationWatermarkDisplayed,
                    0,0, 2,&UserData);
        }
    }

    if (hKey)
        RegCloseKey(hKey);

    return result;
}
bool __fastcall CImmersiveWatermark___RegTimestampCheck(struct_a1_Struct* a1)
{
    DWORD status = 8;
    unsigned __int64 dataIn = 0;
    FILETIME ft;

    if (RegGetValueW(
            HKEY_CURRENT_USER,
            L"Software\\Microsoft\\Windows\\CurrentVersion\\Watermark",
            L"Timestamp",
            RRF_RT_REG_BINARY, 0,
            &dataIn,
            &status))
        return true;

    if (status != 8)
        return true;

    GetSystemTimeAsFileTime(&ft);

    if (*(unsigned __int64*)&ft <= dataIn)
        return true;
	
	// 24 hours as seconds = 864 000 000 000
    if (*(unsigned __int64*)&ft - dataIn >= 864000000000ULL)
        return true;

    return false;
}

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