求一个禁止 微信、QQ、钉钉等通讯软件 接收消息后,闪烁自己的 托盘区图标的方法

求一个禁止 微信、QQ、钉钉等通讯软件 接收消息后,闪烁自己的 托盘区图标的方法

众所周知,基本上所有的通讯软件都没有接入window的通知,
接收了消息后,会在window的托盘区 闪烁自己的图标,有没有办法关闭这个托盘区的 闪烁效果呢?

我厌恶的是微信来新消息时会跟随鼠标位置弹窗,火绒弹窗拦截都管不住,只能用ahk无脑杀

飞书倒是可以右键关闭这个特性。

哇,我试着搜索一下,没想到模组也有NoFlashWindow - Windhawk
我还以为23年没用了,还有效的, 它提到Windows似乎有内置的此选项
在 Windows 11 中启用或禁用任务栏应用中的显示闪烁 |Windows 11 论坛

这个针对的是任务栏,不是针对托盘区吧。
(针对任务栏图标禁止闪烁,Win11 原生就有,但针对托盘区图标没有这个功能。)

翻到了是这一个 禁用托盘图标 - Windhawk Disable Tray Icons - Windhawk
自定义应用在:高级设置 - 自定义进程包含列表,应用要重启打开,就有效了

好奇问下,win11的托盘自动折叠的,为啥会嫌闪啊?

我不是要隐藏微信这类通讯软件,我还是要微信这类通讯软件在托盘区显示啊,方便打开

放在托盘区方便打开啊,任务栏图标本来就够满了

这个应该是看个人习惯吧,毕竟微软给了个指定哪些折叠 哪些不折叠的设置项。

我自己喜欢把那种好多天都不一定开一次前台窗口的后台程序折叠起来;然后对于办公软件、状态展示类的图标、高频唤起前台窗口的后台程序,我都会不折叠。

比如这样:

可以实现。让AI补充了一个修改,我测试没什么问题,你先试试吧

WindhawkMod
// ==WindhawkMod==
// @id              suppress-tray-flashing
// @name            禁止托盘图标闪烁
// @description     阻止指定应用频繁切换或闪烁系统托盘图标,同时完整保留托盘图标与常规功能。
// @version         1.0.0
// @author          User
// @include         none
// @compilerOptions -luser32 -lshell32
// ==/WindhawkMod==

// ==WindhawkModReadme==
/*
# 功能与使用说明

本模组专门用于解决即时通讯或办公软件(如 QQ、企业微信、飞书、钉钉、邮件客户端等)在收到消息时在任务栏托盘区域无休止跳动、交替闪烁的问题。

### 核心特性
- 保留图标不隐藏:图标依然固定常驻在系统托盘,右键菜单、左键双击唤出窗口、正常退出等功能完全不受影响。
- 杜绝高频闪烁:从源头锁定应用的最初静态图标,屏蔽高频切换空白帧或黄色高亮帧的动作。
- 零后台开销:无后台轮询线程,不驻留监控定时器,仅在 API 触发瞬间执行数纳秒的条件匹配。
- 安全沙盒隔离:默认不注入任何系统进程,绝不影响 Windows 桌面与资源管理器稳定性。

### 配置步骤
1. 安装并编译保存本模组。
2. 进入模组详情页,点击“高级 (Advanced)”选项卡。
3. 找到“Custom process inclusion list”(自定义包含进程列表)。
4. 在输入框中逐行添加需要禁止闪烁的程序进程名(例如 QQ.exe、WeChat.exe)。
5. 点击保存设置,并完全重启一次对应的软件即可生效。
*/
// ==/WindhawkModReadme==

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

struct TrayKey {
    HWND hWnd;
    UINT uID;

    bool operator==(const TrayKey& other) const {
        return hWnd == other.hWnd && uID == other.uID;
    }
};

struct TrayKeyHash {
    size_t operator()(const TrayKey& k) const {
        return std::hash<HWND>()(k.hWnd) ^ (std::hash<UINT>()(k.uID) << 1);
    }
};

static std::unordered_map<TrayKey, HICON, TrayKeyHash> g_iconMap;
static SRWLOCK g_lock = SRWLOCK_INIT;

using Shell_NotifyIconW_t = BOOL(WINAPI *)(DWORD dwMessage, PNOTIFYICONDATAW lpData);
static Shell_NotifyIconW_t pOriginal_Shell_NotifyIconW = nullptr;

using Shell_NotifyIconA_t = BOOL(WINAPI *)(DWORD dwMessage, PNOTIFYICONDATAA lpData);
static Shell_NotifyIconA_t pOriginal_Shell_NotifyIconA = nullptr;

template <typename TData, typename TOrigFunc>
static BOOL Handle_Shell_NotifyIcon(DWORD dwMessage, TData* lpData, TOrigFunc pOriginalFunc) {
    if (!lpData) {
        return pOriginalFunc(dwMessage, lpData);
    }

    TrayKey key = { lpData->hWnd, lpData->uID };

    switch (dwMessage) {
        case NIM_ADD: {
            AcquireSRWLockExclusive(&g_lock);
            auto it = g_iconMap.find(key);
            if (it != g_iconMap.end()) {
                if (it->second) {
                    DestroyIcon(it->second);
                }
                g_iconMap.erase(it);
            }
            if ((lpData->uFlags & NIF_ICON) && lpData->hIcon) {
                HICON hCloned = CopyIcon(lpData->hIcon);
                if (hCloned) {
                    g_iconMap[key] = hCloned;
                }
            }
            ReleaseSRWLockExclusive(&g_lock);
            return pOriginalFunc(dwMessage, lpData);
        }

        case NIM_DELETE: {
            AcquireSRWLockExclusive(&g_lock);
            auto it = g_iconMap.find(key);
            if (it != g_iconMap.end()) {
                if (it->second) {
                    DestroyIcon(it->second);
                }
                g_iconMap.erase(it);
            }
            ReleaseSRWLockExclusive(&g_lock);
            return pOriginalFunc(dwMessage, lpData);
        }

        case NIM_MODIFY: {
            if (lpData->uFlags & NIF_ICON) {
                AcquireSRWLockShared(&g_lock);
                auto it = g_iconMap.find(key);
                HICON hLocked = (it != g_iconMap.end()) ? it->second : nullptr;
                ReleaseSRWLockShared(&g_lock);

                if (!hLocked && lpData->hIcon) {
                    AcquireSRWLockExclusive(&g_lock);
                    auto retryIt = g_iconMap.find(key);
                    if (retryIt == g_iconMap.end() || !retryIt->second) {
                        hLocked = CopyIcon(lpData->hIcon);
                        if (hLocked) {
                            g_iconMap[key] = hLocked;
                        }
                    } else {
                        hLocked = retryIt->second;
                    }
                    ReleaseSRWLockExclusive(&g_lock);
                }

                TData safeData = *lpData;
                if (hLocked) {
                    safeData.hIcon = hLocked;
                } else {
                    safeData.uFlags &= ~NIF_ICON;
                }
                return pOriginalFunc(dwMessage, &safeData);
            }
            return pOriginalFunc(dwMessage, lpData);
        }

        default:
            break;
    }

    return pOriginalFunc(dwMessage, lpData);
}

static BOOL WINAPI Hook_Shell_NotifyIconW(DWORD dwMessage, PNOTIFYICONDATAW lpData) {
    return Handle_Shell_NotifyIcon(dwMessage, lpData, pOriginal_Shell_NotifyIconW);
}

static BOOL WINAPI Hook_Shell_NotifyIconA(DWORD dwMessage, PNOTIFYICONDATAA lpData) {
    return Handle_Shell_NotifyIcon(dwMessage, lpData, pOriginal_Shell_NotifyIconA);
}

BOOL Wh_ModInit() {
    HMODULE hShell32 = GetModuleHandleW(L"shell32.dll");
    if (!hShell32) {
        hShell32 = LoadLibraryW(L"shell32.dll");
    }

    if (hShell32) {
        void* pTargetW = (void*)GetProcAddress(hShell32, "Shell_NotifyIconW");
        if (pTargetW) {
            Wh_SetFunctionHook(pTargetW, (void*)Hook_Shell_NotifyIconW, (void**)&pOriginal_Shell_NotifyIconW);
        }

        void* pTargetA = (void*)GetProcAddress(hShell32, "Shell_NotifyIconA");
        if (pTargetA) {
            Wh_SetFunctionHook(pTargetA, (void*)Hook_Shell_NotifyIconA, (void**)&pOriginal_Shell_NotifyIconA);
        }
        return TRUE;
    }

    return FALSE;
}

void Wh_ModUninit() {
    AcquireSRWLockExclusive(&g_lock);
    for (auto& pair : g_iconMap) {
        if (pair.second) {
            DestroyIcon(pair.second);
        }
    }
    g_iconMap.clear();
    ReleaseSRWLockExclusive(&g_lock);
}

ok 感谢大佬 我晚点拿电脑测试一下

话说大佬 做这个用的哪个AI 以及主要投的是哪些相关的资料给AI呢

因为国内网上关于windhawk开发的相关资料并不是那么丰富和完善
且很多AI对windhawk的资料的训练好像也不多导致经常答非所问

能帮到你就好,大佬算不上,有几个有详细的样式指南
常用的任务栏样式定制集合:
A collection of commonly requested taskbar styling customizations for Windows 11
Windows 11 常用的开始菜单样式定制集合:
A collection of commonly requested start menu styling customizations for Windows 11
Windows 11 常用的通知中心样式定制集合:
A collection of commonly requested notification center styling customizations for Windows 11
Windows 11 常用的文件资源管理器样式定制集合:
A collection of commonly requested file explorer styling customizations for Windows 11

最后开发的话,最好看它们论坛或国外社区看看分享与提问,不熟悉直接用弄ai从零出来有一些未知问题的
国内宣传都偏向美化部分,其实它都有很多功能模组优化,还有意想不到的发现

大佬,我加了个屏蔽消息来时 屏蔽 鼠标悬浮图标时 弹出的气泡弹窗
好像不太行
能帮忙看看是怎么回事吗

windhawkMod
// ==WindhawkMod==
// @id              suppress-tray-flashing
// @name            禁止托盘图标闪烁
// @description     阻止指定应用频繁切换或闪烁系统托盘图标,并可屏蔽托盘提示文字与气球通知。
// @version         1.3.0
// @author          User
// @include         none
// @compilerOptions -luser32 -lshell32
// ==/WindhawkMod==

// ==WindhawkModSettings==
/*
- BlockFlashing: true
  $name: 屏蔽托盘图标闪烁
  $description: 锁定托盘图标为首次添加时的静态图标,阻止应用频繁切换图标造成闪烁
- BlockTooltip: true
  $name: 屏蔽托盘提示文字
  $description: 清空鼠标悬浮在托盘图标上时弹出的提示文字,以及应用推送的气球通知内容
*/
// ==/WindhawkModSettings==

// ==WindhawkModReadme==
/*
# 功能与使用说明

本模组用于解决即时通讯或办公软件在收到消息时,任务栏托盘图标无休止跳动、交替闪烁的问题。

### 核心特性
- 保留图标不隐藏:图标依然固定常驻系统托盘,右键菜单、左键双击唤出窗口、正常退出等功能不受影响。
- 杜绝高频闪烁:锁定应用首次添加的静态图标,屏蔽后续高频切换空白帧或高亮帧的动作。
- 可选屏蔽提示:可单独关闭鼠标悬浮提示文字与右下角气球通知。
- 零后台开销:无后台轮询线程,不驻留监控定时器,仅在 API 触发瞬间执行极短的条件匹配。
- 安全沙盒隔离:默认不注入任何系统进程,绝不影响 Windows 桌面与资源管理器稳定性。

### 配置步骤
1. 安装并编译保存本模组。
2. 进入模组详情页,点击“设置 (Settings)”选项卡,按需打开或关闭两个开关(默认都打开)。
3. 进入“高级 (Advanced)”选项卡。
4. 找到“Custom process inclusion list”(自定义包含进程列表)。
5. 在输入框中逐行添加需要禁止闪烁的程序进程名(例如 QQ.exe、WeChat.exe)。
6. 点击保存设置,并完全重启一次对应的软件即可生效。
*/
// ==/WindhawkModReadme==

#include <windows.h>
#include <shellapi.h>
#include <unordered_map>
#include <cstring>
#include <cstddef>

// ---------------------------------------------------------------------------
// 全局开关:从 Windhawk 设置中读取,默认都为 true
// ---------------------------------------------------------------------------
static bool g_blockFlashing = true;   // 是否屏蔽图标闪烁
static bool g_blockTooltip  = true;   // 是否屏蔽悬浮提示与气球通知

// ---------------------------------------------------------------------------
// 托盘图标缓存 Key:一个托盘图标由 (窗口句柄, 图标ID) 唯一确定
// ---------------------------------------------------------------------------
struct TrayKey {
    HWND hWnd;
    UINT uID;

    bool operator==(const TrayKey& other) const {
        return hWnd == other.hWnd && uID == other.uID;
    }
};

struct TrayKeyHash {
    size_t operator()(const TrayKey& k) const {
        return std::hash<HWND>()(k.hWnd) ^ (std::hash<UINT>()(k.uID) << 1);
    }
};

// 缓存:记录每个托盘图标“第一次出现时的静态图标”
static std::unordered_map<TrayKey, HICON, TrayKeyHash> g_iconMap;

// 读写缓存用的轻量读写锁,避免多线程同时操作 unordered_map
static SRWLOCK g_lock = SRWLOCK_INIT;

// 原始函数指针:Hook 后用来调用真正的 Shell_NotifyIcon
using Shell_NotifyIconW_t = BOOL(WINAPI *)(DWORD dwMessage, PNOTIFYICONDATAW lpData);
static Shell_NotifyIconW_t pOriginal_Shell_NotifyIconW = nullptr;

using Shell_NotifyIconA_t = BOOL(WINAPI *)(DWORD dwMessage, PNOTIFYICONDATAA lpData);
static Shell_NotifyIconA_t pOriginal_Shell_NotifyIconA = nullptr;

// ---------------------------------------------------------------------------
// 工具函数
// ---------------------------------------------------------------------------

// 清空整个图标缓存,并销毁所有保存的 HICON
static void ClearIconCache() {
    AcquireSRWLockExclusive(&g_lock);
    for (auto& pair : g_iconMap) {
        if (pair.second) {
            DestroyIcon(pair.second);
        }
    }
    g_iconMap.clear();
    ReleaseSRWLockExclusive(&g_lock);
}

// 把一个图标复制并存入缓存;如果该 Key 已有旧图标,先销毁旧的
static void CacheIcon(const TrayKey& key, HICON hIcon) {
    if (!hIcon) return;

    AcquireSRWLockExclusive(&g_lock);

    auto it = g_iconMap.find(key);
    if (it != g_iconMap.end()) {
        if (it->second) {
            DestroyIcon(it->second);
        }
        g_iconMap.erase(it);
    }

    // 必须 CopyIcon,不能直接保存应用传来的 HICON,
    // 因为应用可能在调用完成后立刻销毁它。
    HICON hCloned = CopyIcon(hIcon);
    if (hCloned) {
        g_iconMap[key] = hCloned;
    }

    ReleaseSRWLockExclusive(&g_lock);
}

// 从缓存中移除并销毁指定图标
static void RemoveCachedIcon(const TrayKey& key) {
    AcquireSRWLockExclusive(&g_lock);

    auto it = g_iconMap.find(key);
    if (it != g_iconMap.end()) {
        if (it->second) {
            DestroyIcon(it->second);
        }
        g_iconMap.erase(it);
    }

    ReleaseSRWLockExclusive(&g_lock);
}

// 清空悬浮提示文字和气球通知内容。
//
// 原理:
//   - szTip 是鼠标悬停时显示的文字,配合 NIF_TIP 标志。
//     把 szTip[0] 设为 0,并保留 NIF_TIP,Shell 就会用空字符串覆盖旧提示。
//   - szInfo / szInfoTitle 是右下角气球通知的文字,配合 NIF_INFO 标志。
//     清空它们并去掉 NIF_INFO,Shell 就不会弹出气球通知。
template <typename TData>
static void StripTooltip(TData* pData) {
    // 清空悬浮提示,但保留 NIF_TIP,让 Shell 主动把空提示写进去
    pData->szTip[0] = 0;
    pData->uFlags |= NIF_TIP;

    // 清空气球通知,并移除 NIF_INFO,Shell 就不会弹通知
    pData->szInfo[0] = 0;
    pData->szInfoTitle[0] = 0;
    pData->uFlags &= ~NIF_INFO;
}

// 按 cbSize 安全复制 NOTIFYICONDATA 结构体。
//
// 应用可能传入不同版本的 NOTIFYICONDATA,cbSize 表示它实际分配的大小。
// 直接 `TData safeData = *lpData;` 可能读到调用者没分配的内存,造成越界。
// 这里只复制 cbSize 指定的字节数,剩余部分清零,保证 safeData 完整且安全。
template <typename TData>
static void SafeCopyNotifyData(TData* dst, const TData* src) {
    size_t copySize = src->cbSize;
    if (copySize > sizeof(TData)) {
        copySize = sizeof(TData);
    }

    memcpy(dst, src, copySize);

    if (copySize < sizeof(TData)) {
        memset((BYTE*)dst + copySize, 0, sizeof(TData) - copySize);
    }
}

// ---------------------------------------------------------------------------
// Hook 主逻辑:所有 Shell_NotifyIconW / Shell_NotifyIconA 都会进入这里
// ---------------------------------------------------------------------------
template <typename TData, typename TOrigFunc>
static BOOL Handle_Shell_NotifyIcon(DWORD dwMessage, TData* lpData, TOrigFunc pOriginalFunc) {
    if (!lpData) {
        return pOriginalFunc(dwMessage, lpData);
    }

    // 两个开关都关闭时,完全透传,不做任何额外操作
    if (!g_blockFlashing && !g_blockTooltip) {
        return pOriginalFunc(dwMessage, lpData);
    }

    TrayKey key = { lpData->hWnd, lpData->uID };

    switch (dwMessage) {
        // -------------------------------------------------------------------
        // NIM_ADD:应用首次添加托盘图标
        // -------------------------------------------------------------------
        case NIM_ADD: {
            TData safeData;
            TData* pData = lpData;

            // 如果需要屏蔽提示,先复制一份结构体并清空提示字段
            if (g_blockTooltip) {
                SafeCopyNotifyData(&safeData, lpData);
                StripTooltip(&safeData);
                pData = &safeData;
            }

            // 调用原始函数,让 Shell 真正添加图标
            BOOL result = pOriginalFunc(dwMessage, pData);

            // 如果需要屏蔽闪烁,原函数成功后再缓存首次图标
            if (g_blockFlashing && result && (lpData->uFlags & NIF_ICON) && lpData->hIcon) {
                CacheIcon(key, lpData->hIcon);
            }

            return result;
        }

        // -------------------------------------------------------------------
        // NIM_DELETE:应用删除托盘图标
        // -------------------------------------------------------------------
        case NIM_DELETE: {
            BOOL result = pOriginalFunc(dwMessage, lpData);

            // 删除成功后,从缓存里也移除对应图标
            if (g_blockFlashing && result) {
                RemoveCachedIcon(key);
            }

            return result;
        }

        // -------------------------------------------------------------------
        // NIM_MODIFY:应用修改图标、提示文字或气球通知
        // -------------------------------------------------------------------
        case NIM_MODIFY: {
            // 判断是否需要锁定图标:
            //   - 开启了屏蔽闪烁
            //   - 本次调用带有 NIF_ICON 标志
            //   - cbSize 足够大,能安全访问 hIcon 字段
            bool needIconLock = false;
            if (g_blockFlashing) {
                bool hasIconFlag = (lpData->uFlags & NIF_ICON) != 0;
                bool canModifyIcon = lpData->cbSize >= offsetof(TData, hIcon) + sizeof(HICON);
                needIconLock = hasIconFlag && canModifyIcon;
            }

            // 既不需要锁图标,也不需要清提示,直接透传
            if (!needIconLock && !g_blockTooltip) {
                return pOriginalFunc(dwMessage, lpData);
            }

            HICON hUse = nullptr;

            if (needIconLock) {
                // 锁内只做查找和复制,尽快释放锁。
                // 不能持锁调用原函数,因为 Shell_NotifyIcon 可能耗时较长,
                // 会阻塞其他线程的托盘操作,导致“不够及时”。
                AcquireSRWLockExclusive(&g_lock);

                auto it = g_iconMap.find(key);
                if (it != g_iconMap.end() && it->second) {
                    // 已有缓存:复制一份给本次调用使用
                    hUse = CopyIcon(it->second);
                } else if (lpData->hIcon) {
                    // 没有缓存:把当前图标存为锁定图标
                    HICON hCached = CopyIcon(lpData->hIcon);
                    if (hCached) {
                        g_iconMap[key] = hCached;
                        // 再复制一份给本次调用,避免锁外缓存被其他线程销毁
                        hUse = CopyIcon(hCached);
                    }
                }

                ReleaseSRWLockExclusive(&g_lock);
            }

            // 安全复制结构体
            TData safeData;
            SafeCopyNotifyData(&safeData, lpData);

            // 如果需要屏蔽提示,清空悬浮提示和气球通知
            if (g_blockTooltip) {
                StripTooltip(&safeData);
            }

            // 如果需要锁定图标,用缓存中的静态图标替换应用传来的图标
            if (needIconLock) {
                if (hUse) {
                    safeData.hIcon = hUse;
                    safeData.uFlags |= NIF_ICON;
                } else {
                    // 复制失败时移除 NIF_ICON,避免把无效句柄传给 Shell
                    safeData.uFlags &= ~NIF_ICON;
                }
            }

            // 锁外调用原函数,不阻塞其他托盘操作
            BOOL result = pOriginalFunc(dwMessage, &safeData);

            // 原函数返回后,临时副本即可销毁
            if (hUse) {
                DestroyIcon(hUse);
            }

            return result;
        }

        default:
            break;
    }

    // 其他消息(NIM_SETFOCUS、NIM_SETVERSION 等)直接透传
    return pOriginalFunc(dwMessage, lpData);
}

// ---------------------------------------------------------------------------
// Hook 入口:分别对应 Unicode 和 ANSI 两个版本
// ---------------------------------------------------------------------------
static BOOL WINAPI Hook_Shell_NotifyIconW(DWORD dwMessage, PNOTIFYICONDATAW lpData) {
    return Handle_Shell_NotifyIcon(dwMessage, lpData, pOriginal_Shell_NotifyIconW);
}

static BOOL WINAPI Hook_Shell_NotifyIconA(DWORD dwMessage, PNOTIFYICONDATAA lpData) {
    return Handle_Shell_NotifyIcon(dwMessage, lpData, pOriginal_Shell_NotifyIconA);
}

// ---------------------------------------------------------------------------
// 设置读取与生命周期
// ---------------------------------------------------------------------------

// 从 Windhawk 设置中读取两个开关
static void LoadSettings() {
    g_blockFlashing = Wh_GetIntSetting(L"BlockFlashing") != 0;
    g_blockTooltip  = Wh_GetIntSetting(L"BlockTooltip") != 0;
}

BOOL Wh_ModInit() {
    // 先读设置,再装 Hook
    LoadSettings();

    HMODULE hShell32 = GetModuleHandleW(L"shell32.dll");
    if (!hShell32) {
        hShell32 = LoadLibraryW(L"shell32.dll");
    }

    if (hShell32) {
        bool hookW = false;
        bool hookA = false;

        // Hook Unicode 版本
        void* pTargetW = (void*)GetProcAddress(hShell32, "Shell_NotifyIconW");
        if (pTargetW) {
            if (Wh_SetFunctionHook(pTargetW,
                                   (void*)Hook_Shell_NotifyIconW,
                                   (void**)&pOriginal_Shell_NotifyIconW)) {
                hookW = true;
            }
        }

        // Hook ANSI 版本(部分老程序仍在使用)
        void* pTargetA = (void*)GetProcAddress(hShell32, "Shell_NotifyIconA");
        if (pTargetA) {
            if (Wh_SetFunctionHook(pTargetA,
                                   (void*)Hook_Shell_NotifyIconA,
                                   (void**)&pOriginal_Shell_NotifyIconA)) {
                hookA = true;
            }
        }

        // 至少成功 Hook 一个版本,模组才算初始化成功
        return hookW || hookA;
    }

    return FALSE;
}

// 用户在 Windhawk 设置页修改开关后,会触发这个回调
void Wh_ModSettingsChanged() {
    bool prevFlashing = g_blockFlashing;
    LoadSettings();

    // 如果从“屏蔽闪烁”切到“不屏蔽”,需要清空缓存,
    // 让应用下一次 NIM_MODIFY 能正常更新图标。
    if (prevFlashing && !g_blockFlashing) {
        ClearIconCache();
    }
}

// 模组卸载时清理所有缓存的图标句柄
void Wh_ModUninit() {
    ClearIconCache();
}

微信弹出这个黑底悬浮卡片有点特殊,查看还是能解决的,还有个设置里选项:鼠标悬浮时的系统托盘提示文本与气泡,你试试吧
我只测试了微信,如果其他的没效,那我也不知道它们采用怎么实现的了,可能要另外加

WindhawkMod
// ==WindhawkMod==
// @id              suppress-tray-flashing-custom
// @name            禁止托盘图标闪烁与消息悬浮窗
// @description     阻止指定应用频繁切换闪烁托盘图标,并彻底屏蔽微信等软件弹出的消息预览悬停浮窗。
// @version         1.1.0
// @author          User
// @include         none
// @compilerOptions -luser32 -lshell32
// ==/WindhawkMod==

// ==WindhawkModReadme==
/*
# 功能与使用说明

本模组用于消除指定应用在托盘区域的打扰行为:
1. 锁定原始静态托盘图标,禁止换图闪烁。
2. 屏蔽微信等软件自绘的消息预览悬浮卡片(带“暂不处理”等按钮的浮窗)。
3. 可选鼠标悬浮时的系统托盘提示文本与气泡。

### 配置步骤
1. 保存并编译本模组。
2. 在“设置 (Settings)”选项卡中按需开关对应拦截项。
3. 进入“高级 (Advanced)”选项卡。
4. 找到“Custom process inclusion list”(自定义包含进程列表),例如:WeChat.exeWeixin.exe、QQ.exe
5. 点击保存,彻底退出并重启目标软件即可生效。
*/
// ==/WindhawkModReadme==

// ==WindhawkModSettings==
/*
- suppressPreviewToast: true
  $name: 屏蔽软件弹出的消息预览悬停浮窗
  $description: 拦截微信等软件收到消息或鼠标悬停时弹出的“暂不处理”等独立悬浮卡片窗口。
- suppressSystemTooltips: true
  $name: 托盘系统提示文本与气泡
  $description: 关闭后剥离托盘注册数据中的提示文本与通知气泡,彻底阻止鼠标悬浮时触发系统气泡。
*/
// ==/WindhawkModSettings==

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

struct ModSettings {
    bool suppressPreviewToast;
    bool suppressSystemTooltips;
} g_settings;

struct TrayKey {
    HWND hWnd;
    UINT uID;

    bool operator==(const TrayKey& other) const {
        return hWnd == other.hWnd && uID == other.uID;
    }
};

struct TrayKeyHash {
    size_t operator()(const TrayKey& k) const {
        return std::hash<HWND>()(k.hWnd) ^ (std::hash<UINT>()(k.uID) << 1);
    }
};

static std::unordered_map<TrayKey, HICON, TrayKeyHash> g_iconMap;
static SRWLOCK g_lock = SRWLOCK_INIT;

using Shell_NotifyIconW_t = BOOL(WINAPI *)(DWORD dwMessage, PNOTIFYICONDATAW lpData);
static Shell_NotifyIconW_t pOriginal_Shell_NotifyIconW = nullptr;

using Shell_NotifyIconA_t = BOOL(WINAPI *)(DWORD dwMessage, PNOTIFYICONDATAA lpData);
static Shell_NotifyIconA_t pOriginal_Shell_NotifyIconA = nullptr;

using ShowWindow_t = BOOL(WINAPI *)(HWND hWnd, int nCmdShow);
static ShowWindow_t pOriginal_ShowWindow = nullptr;

using FlashWindowEx_t = BOOL(WINAPI *)(PFLASHWINFO pfwi);
static FlashWindowEx_t pOriginal_FlashWindowEx = nullptr;

static void LoadModSettings() {
    g_settings.suppressPreviewToast = Wh_GetIntSetting(L"suppressPreviewToast") != 0;
    g_settings.suppressSystemTooltips = Wh_GetIntSetting(L"suppressSystemTooltips") != 0;
}

static inline bool IsWeChatTipWindow(HWND hWnd) {
    if (!hWnd || !g_settings.suppressPreviewToast) {
        return false;
    }

    LONG_PTR style = GetWindowLongPtrW(hWnd, GWL_STYLE);
    if ((style & WS_CHILD) || (style & WS_CAPTION)) {
        return false;
    }

    LONG_PTR exStyle = GetWindowLongPtrW(hWnd, GWL_EXSTYLE);
    if (!(exStyle & (WS_EX_TOOLWINDOW | WS_EX_TOPMOST))) {
        return false;
    }

    RECT rc;
    if (!GetWindowRect(hWnd, &rc)) {
        return false;
    }

    int width = rc.right - rc.left;
    int height = rc.bottom - rc.top;
    if (width <= 50 || width >= 480 || height <= 30 || height >= 320) {
        return false;
    }

    wchar_t className[64] = {0};
    if (GetClassNameW(hWnd, className, 64) > 0) {
        if (wcsstr(className, L"WeChatTipWnd") || 
            wcsstr(className, L"ToolTip") || 
            wcsstr(className, L"TrayTip") ||
            wcsstr(className, L"NotificationWnd")) {
            return true;
        }
    }

    return (exStyle & WS_EX_TOOLWINDOW) && (exStyle & WS_EX_TOPMOST);
}

static BOOL WINAPI Hook_ShowWindow(HWND hWnd, int nCmdShow) {
    if (nCmdShow != SW_HIDE && IsWeChatTipWindow(hWnd)) {
        return FALSE;
    }
    return pOriginal_ShowWindow(hWnd, nCmdShow);
}

static BOOL WINAPI Hook_FlashWindowEx(PFLASHWINFO pfwi) {
    return TRUE;
}

template <typename TData, typename TOrigFunc>
static BOOL Handle_Shell_NotifyIcon(DWORD dwMessage, TData* lpData, TOrigFunc pOriginalFunc) {
    if (!lpData) {
        return pOriginalFunc(dwMessage, lpData);
    }

    TrayKey key = { lpData->hWnd, lpData->uID };

    switch (dwMessage) {
        case NIM_ADD: {
            AcquireSRWLockExclusive(&g_lock);
            auto it = g_iconMap.find(key);
            if (it != g_iconMap.end()) {
                if (it->second) {
                    DestroyIcon(it->second);
                }
                g_iconMap.erase(it);
            }
            if ((lpData->uFlags & NIF_ICON) && lpData->hIcon) {
                HICON hCloned = CopyIcon(lpData->hIcon);
                if (hCloned) {
                    g_iconMap[key] = hCloned;
                }
            }
            ReleaseSRWLockExclusive(&g_lock);

            if (g_settings.suppressSystemTooltips) {
                TData safeData = *lpData;
                safeData.uFlags &= ~(NIF_TIP | NIF_INFO);
                safeData.szTip[0] = 0;
                safeData.szInfo[0] = 0;
                return pOriginalFunc(dwMessage, &safeData);
            }
            return pOriginalFunc(dwMessage, lpData);
        }

        case NIM_DELETE: {
            AcquireSRWLockExclusive(&g_lock);
            auto it = g_iconMap.find(key);
            if (it != g_iconMap.end()) {
                if (it->second) {
                    DestroyIcon(it->second);
                }
                g_iconMap.erase(it);
            }
            ReleaseSRWLockExclusive(&g_lock);
            return pOriginalFunc(dwMessage, lpData);
        }

        case NIM_MODIFY: {
            TData safeData = *lpData;

            if (safeData.uFlags & NIF_ICON) {
                AcquireSRWLockShared(&g_lock);
                auto it = g_iconMap.find(key);
                HICON hLocked = (it != g_iconMap.end()) ? it->second : nullptr;
                ReleaseSRWLockShared(&g_lock);

                if (!hLocked && safeData.hIcon) {
                    AcquireSRWLockExclusive(&g_lock);
                    auto retryIt = g_iconMap.find(key);
                    if (retryIt == g_iconMap.end() || !retryIt->second) {
                        hLocked = CopyIcon(safeData.hIcon);
                        if (hLocked) {
                            g_iconMap[key] = hLocked;
                        }
                    } else {
                        hLocked = retryIt->second;
                    }
                    ReleaseSRWLockExclusive(&g_lock);
                }

                if (hLocked) {
                    safeData.hIcon = hLocked;
                } else {
                    safeData.uFlags &= ~NIF_ICON;
                }
            }

            if (g_settings.suppressSystemTooltips) {
                safeData.uFlags &= ~(NIF_TIP | NIF_INFO);
                safeData.szTip[0] = 0;
                safeData.szInfo[0] = 0;
            }

            return pOriginalFunc(dwMessage, &safeData);
        }

        default:
            break;
    }

    return pOriginalFunc(dwMessage, lpData);
}

static BOOL WINAPI Hook_Shell_NotifyIconW(DWORD dwMessage, PNOTIFYICONDATAW lpData) {
    return Handle_Shell_NotifyIcon(dwMessage, lpData, pOriginal_Shell_NotifyIconW);
}

static BOOL WINAPI Hook_Shell_NotifyIconA(DWORD dwMessage, PNOTIFYICONDATAA lpData) {
    return Handle_Shell_NotifyIcon(dwMessage, lpData, pOriginal_Shell_NotifyIconA);
}

void Wh_ModSettingsChanged() {
    LoadModSettings();
}

BOOL Wh_ModInit() {
    LoadModSettings();

    HMODULE hShell32 = GetModuleHandleW(L"shell32.dll");
    if (!hShell32) {
        hShell32 = LoadLibraryW(L"shell32.dll");
    }

    if (hShell32) {
        void* pTargetW = (void*)GetProcAddress(hShell32, "Shell_NotifyIconW");
        if (pTargetW) {
            Wh_SetFunctionHook(pTargetW, (void*)Hook_Shell_NotifyIconW, (void**)&pOriginal_Shell_NotifyIconW);
        }

        void* pTargetA = (void*)GetProcAddress(hShell32, "Shell_NotifyIconA");
        if (pTargetA) {
            Wh_SetFunctionHook(pTargetA, (void*)Hook_Shell_NotifyIconA, (void**)&pOriginal_Shell_NotifyIconA);
        }
    }

    HMODULE hUser32 = GetModuleHandleW(L"user32.dll");
    if (!hUser32) {
        hUser32 = LoadLibraryW(L"user32.dll");
    }

    if (hUser32) {
        void* pTargetShow = (void*)GetProcAddress(hUser32, "ShowWindow");
        if (pTargetShow) {
            Wh_SetFunctionHook(pTargetShow, (void*)Hook_ShowWindow, (void**)&pOriginal_ShowWindow);
        }

        void* pTargetFlash = (void*)GetProcAddress(hUser32, "FlashWindowEx");
        if (pTargetFlash) {
            Wh_SetFunctionHook(pTargetFlash, (void*)Hook_FlashWindowEx, (void**)&pOriginal_FlashWindowEx);
        }
    }

    return TRUE;
}

void Wh_ModUninit() {
    AcquireSRWLockExclusive(&g_lock);
    for (auto& pair : g_iconMap) {
        if (pair.second) {
            DestroyIcon(pair.second);
        }
    }
    g_iconMap.clear();
    ReleaseSRWLockExclusive(&g_lock);
}

感谢大佬,我发现对win32真的是一窍不通,想用ai深入研究改改mod确实有点难 :joy:
平时有时ai用的顺手可能就是我本来对那方面就比较清楚 :joy:

可能要抽空看看win32的机制了
这里稍微优化了下,加入了是否支持闪烁的开关
(刚才我发的有bug,现已修复)

WindhawkMod
// ==WindhawkMod==
// @id              suppress-tray-flashing-custom
// @name            禁止托盘图标闪烁与消息悬浮窗
// @description     阻止指定应用频繁切换闪烁托盘图标,并彻底屏蔽微信等软件弹出的消息预览悬停浮窗。
// @version         1.2.1
// @author          User
// @include         none
// @compilerOptions -luser32 -lshell32
// ==/WindhawkMod==

// ==WindhawkModReadme==
/*
# 功能与使用说明

本模组用于消除指定应用在托盘区域的打扰行为:
1. 锁定原始静态托盘图标,禁止换图闪烁(默认开启,可在设置中关闭)。
2. 屏蔽微信等软件自绘的消息预览悬浮卡片(带“暂不处理”等按钮的浮窗)。
3. 可选鼠标悬浮时的系统托盘提示文本与气泡。

### 配置步骤
1. 保存并编译本模组。
2. 在“设置 (Settings)”选项卡中按需开关对应拦截项。其中“锁定原始静态托盘图标,禁止换图闪烁”默认开启。
3. 进入“高级 (Advanced)”选项卡。
4. 找到“Custom process inclusion list”(自定义包含进程列表),逐行填入:
   WeChat.exe
   WeChatAppEx.exe
   QQ.exe
5. 点击保存,彻底退出并重启目标软件即可生效。
*/
// ==/WindhawkModReadme==

// ==WindhawkModSettings==
/*
- lockTrayIcon: true
  $name: 锁定原始静态托盘图标,禁止换图闪烁
  $description: 保存应用首次注册的托盘图标,并在后续修改图标时强制使用该图标,阻止通过切换图标产生闪烁。
- suppressPreviewToast: true
  $name: 屏蔽软件弹出的消息预览悬停浮窗
  $description: 拦截微信等软件收到消息或鼠标悬停时弹出的“暂不处理”等独立悬浮卡片窗口。
- suppressSystemTooltips: true
  $name: 托盘系统提示文本与气泡
  $description: 关闭后剥离托盘注册数据中的提示文本与通知气泡,彻底阻止鼠标悬浮时触发系统气泡。
*/
// ==/WindhawkModSettings==

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

struct ModSettings {
    bool lockTrayIcon;
    bool suppressPreviewToast;
    bool suppressSystemTooltips;
} g_settings;

struct TrayKey {
    HWND hWnd;
    UINT uID;

    bool operator==(const TrayKey& other) const {
        return hWnd == other.hWnd && uID == other.uID;
    }
};

struct TrayKeyHash {
    size_t operator()(const TrayKey& k) const {
        return std::hash<HWND>()(k.hWnd) ^ (std::hash<UINT>()(k.uID) << 1);
    }
};

static std::unordered_map<TrayKey, HICON, TrayKeyHash> g_iconMap;
static SRWLOCK g_lock = SRWLOCK_INIT;

using Shell_NotifyIconW_t = BOOL(WINAPI *)(DWORD dwMessage, PNOTIFYICONDATAW lpData);
static Shell_NotifyIconW_t pOriginal_Shell_NotifyIconW = nullptr;

using Shell_NotifyIconA_t = BOOL(WINAPI *)(DWORD dwMessage, PNOTIFYICONDATAA lpData);
static Shell_NotifyIconA_t pOriginal_Shell_NotifyIconA = nullptr;

using ShowWindow_t = BOOL(WINAPI *)(HWND hWnd, int nCmdShow);
static ShowWindow_t pOriginal_ShowWindow = nullptr;

using FlashWindowEx_t = BOOL(WINAPI *)(PFLASHWINFO pfwi);
static FlashWindowEx_t pOriginal_FlashWindowEx = nullptr;

static void LoadModSettings() {
    g_settings.lockTrayIcon = Wh_GetIntSetting(L"lockTrayIcon") != 0;
    g_settings.suppressPreviewToast = Wh_GetIntSetting(L"suppressPreviewToast") != 0;
    g_settings.suppressSystemTooltips = Wh_GetIntSetting(L"suppressSystemTooltips") != 0;
}

static inline bool IsWeChatTipWindow(HWND hWnd) {
    if (!hWnd || !g_settings.suppressPreviewToast) {
        return false;
    }

    LONG_PTR style = GetWindowLongPtrW(hWnd, GWL_STYLE);
    if ((style & WS_CHILD) || (style & WS_CAPTION)) {
        return false;
    }

    LONG_PTR exStyle = GetWindowLongPtrW(hWnd, GWL_EXSTYLE);
    if (!(exStyle & (WS_EX_TOOLWINDOW | WS_EX_TOPMOST))) {
        return false;
    }

    RECT rc;
    if (!GetWindowRect(hWnd, &rc)) {
        return false;
    }

    int width = rc.right - rc.left;
    int height = rc.bottom - rc.top;
    if (width <= 50 || width >= 480 || height <= 30 || height >= 320) {
        return false;
    }

    wchar_t className[64] = {0};
    if (GetClassNameW(hWnd, className, 64) > 0) {
        // 排除系统菜单窗口
        if (wcscmp(className, L"#32768") == 0) {
            return false;
        }
        if (wcsstr(className, L"WeChatTipWnd") || 
            wcsstr(className, L"ToolTip") || 
            wcsstr(className, L"TrayTip") ||
            wcsstr(className, L"NotificationWnd")) {
            return true;
        }
    }

    // 移除过于宽泛的兜底判断
    return false;
}

static BOOL WINAPI Hook_ShowWindow(HWND hWnd, int nCmdShow) {
    if (nCmdShow != SW_HIDE && IsWeChatTipWindow(hWnd)) {
        return FALSE;
    }
    return pOriginal_ShowWindow(hWnd, nCmdShow);
}

static BOOL WINAPI Hook_FlashWindowEx(PFLASHWINFO pfwi) {
    return TRUE;
}

template <typename TData, typename TOrigFunc>
static BOOL Handle_Shell_NotifyIcon(DWORD dwMessage, TData* lpData, TOrigFunc pOriginalFunc) {
    if (!lpData) {
        return pOriginalFunc(dwMessage, lpData);
    }

    TrayKey key = { lpData->hWnd, lpData->uID };

    switch (dwMessage) {
        case NIM_ADD: {
            if (g_settings.lockTrayIcon) {
                AcquireSRWLockExclusive(&g_lock);
                auto it = g_iconMap.find(key);
                if (it != g_iconMap.end()) {
                    if (it->second) {
                        DestroyIcon(it->second);
                    }
                    g_iconMap.erase(it);
                }
                if ((lpData->uFlags & NIF_ICON) && lpData->hIcon) {
                    HICON hCloned = CopyIcon(lpData->hIcon);
                    if (hCloned) {
                        g_iconMap[key] = hCloned;
                    }
                }
                ReleaseSRWLockExclusive(&g_lock);
            }

            if (g_settings.suppressSystemTooltips) {
                TData safeData = *lpData;
                safeData.uFlags &= ~(NIF_TIP | NIF_INFO);
                safeData.szTip[0] = 0;
                safeData.szInfo[0] = 0;
                return pOriginalFunc(dwMessage, &safeData);
            }
            return pOriginalFunc(dwMessage, lpData);
        }

        case NIM_DELETE: {
            AcquireSRWLockExclusive(&g_lock);
            auto it = g_iconMap.find(key);
            if (it != g_iconMap.end()) {
                if (it->second) {
                    DestroyIcon(it->second);
                }
                g_iconMap.erase(it);
            }
            ReleaseSRWLockExclusive(&g_lock);
            return pOriginalFunc(dwMessage, lpData);
        }

        case NIM_MODIFY: {
            TData safeData = *lpData;

            if (g_settings.lockTrayIcon) {
                if (safeData.uFlags & NIF_ICON) {
                    AcquireSRWLockShared(&g_lock);
                    auto it = g_iconMap.find(key);
                    HICON hLocked = (it != g_iconMap.end()) ? it->second : nullptr;
                    ReleaseSRWLockShared(&g_lock);

                    if (!hLocked && safeData.hIcon) {
                        AcquireSRWLockExclusive(&g_lock);
                        auto retryIt = g_iconMap.find(key);
                        if (retryIt == g_iconMap.end() || !retryIt->second) {
                            hLocked = CopyIcon(safeData.hIcon);
                            if (hLocked) {
                                g_iconMap[key] = hLocked;
                            }
                        } else {
                            hLocked = retryIt->second;
                        }
                        ReleaseSRWLockExclusive(&g_lock);
                    }

                    if (hLocked) {
                        safeData.hIcon = hLocked;
                    } else {
                        safeData.uFlags &= ~NIF_ICON;
                    }
                }
            }

            if (g_settings.suppressSystemTooltips) {
                safeData.uFlags &= ~(NIF_TIP | NIF_INFO);
                safeData.szTip[0] = 0;
                safeData.szInfo[0] = 0;
            }

            return pOriginalFunc(dwMessage, &safeData);
        }

        default:
            break;
    }

    return pOriginalFunc(dwMessage, lpData);
}

static BOOL WINAPI Hook_Shell_NotifyIconW(DWORD dwMessage, PNOTIFYICONDATAW lpData) {
    return Handle_Shell_NotifyIcon(dwMessage, lpData, pOriginal_Shell_NotifyIconW);
}

static BOOL WINAPI Hook_Shell_NotifyIconA(DWORD dwMessage, PNOTIFYICONDATAA lpData) {
    return Handle_Shell_NotifyIcon(dwMessage, lpData, pOriginal_Shell_NotifyIconA);
}

void Wh_ModSettingsChanged() {
    LoadModSettings();
}

BOOL Wh_ModInit() {
    LoadModSettings();

    HMODULE hShell32 = GetModuleHandleW(L"shell32.dll");
    if (!hShell32) {
        hShell32 = LoadLibraryW(L"shell32.dll");
    }

    if (hShell32) {
        void* pTargetW = (void*)GetProcAddress(hShell32, "Shell_NotifyIconW");
        if (pTargetW) {
            Wh_SetFunctionHook(pTargetW, (void*)Hook_Shell_NotifyIconW, (void**)&pOriginal_Shell_NotifyIconW);
        }

        void* pTargetA = (void*)GetProcAddress(hShell32, "Shell_NotifyIconA");
        if (pTargetA) {
            Wh_SetFunctionHook(pTargetA, (void*)Hook_Shell_NotifyIconA, (void**)&pOriginal_Shell_NotifyIconA);
        }
    }

    HMODULE hUser32 = GetModuleHandleW(L"user32.dll");
    if (!hUser32) {
        hUser32 = LoadLibraryW(L"user32.dll");
    }

    if (hUser32) {
        void* pTargetShow = (void*)GetProcAddress(hUser32, "ShowWindow");
        if (pTargetShow) {
            Wh_SetFunctionHook(pTargetShow, (void*)Hook_ShowWindow, (void**)&pOriginal_ShowWindow);
        }

        void* pTargetFlash = (void*)GetProcAddress(hUser32, "FlashWindowEx");
        if (pTargetFlash) {
            Wh_SetFunctionHook(pTargetFlash, (void*)Hook_FlashWindowEx, (void**)&pOriginal_FlashWindowEx);
        }
    }

    return TRUE;
}

void Wh_ModUninit() {
    AcquireSRWLockExclusive(&g_lock);
    for (auto& pair : g_iconMap) {
        if (pair.second) {
            DestroyIcon(pair.second);
        }
    }
    g_iconMap.clear();
    ReleaseSRWLockExclusive(&g_lock);
}

把这些加到允许Windows通知,再打开勿扰就解决了,只会弹窗显示消息内容(右下角)