You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Windows 10中通过WinAPI正确获取通知窗口句柄?

嘿,这个多语言适配的坑我之前也踩过——靠硬编码不同语言的窗口标题确实太蠢了,不仅维护起来要命,Windows一更新说不定就失效了。给你几个更靠谱的方案,彻底避开标题依赖:

方案1:用窗口类名替代标题判断

ShellExperienceHost的通知气泡窗口有固定的类名ToastNotificationWindow,这个名称不会随系统语言变化,比标题靠谱得多。步骤如下:

  1. 先获取ShellExperienceHost.exe的进程ID(PID)
  2. 枚举所有窗口,筛选出属于该PID的窗口
  3. 对这些窗口获取类名,匹配ToastNotificationWindow即可找到目标窗口

给你一段C++示例代码:

#include <windows.h>
#include <tlhelp32.h>
#include <wchar.h>

DWORD GetShellExperienceHostPID() {
    PROCESSENTRY32 pe32;
    pe32.dwSize = sizeof(PROCESSENTRY32);
    HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    if (hSnapshot == INVALID_HANDLE_VALUE) return 0;

    DWORD pid = 0;
    if (Process32First(hSnapshot, &pe32)) {
        do {
            if (_wcsicmp(pe32.szExeFile, L"ShellExperienceHost.exe") == 0) {
                pid = pe32.th32ProcessID;
                break;
            }
        } while (Process32Next(hSnapshot, &pe32));
    }
    CloseHandle(hSnapshot);
    return pid;
}

BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam) {
    DWORD windowPid;
    GetWindowThreadProcessId(hwnd, &windowPid);
    DWORD* data = (DWORD*)lParam;
    DWORD targetPid = data[0];
    HWND* resultHwnd = (HWND*)&data[1];

    if (windowPid != targetPid) return TRUE;

    WCHAR className[256];
    GetClassName(hwnd, className, 256);
    if (_wcsicmp(className, L"ToastNotificationWindow") == 0) {
        *resultHwnd = hwnd;
        return FALSE; // 找到目标后停止枚举
    }
    return TRUE;
}

HWND FindToastNotificationWindow() {
    DWORD sehPid = GetShellExperienceHostPID();
    if (sehPid == 0) return NULL;

    DWORD data[2] = {sehPid, 0};
    EnumWindows(EnumWindowsProc, (LPARAM)data);
    return (HWND)data[1];
}
方案2:使用UI Automation框架(更稳定)

如果要更稳妥,推荐用微软官方的UI Automation框架——这是专门用来自动化UWP/现代UI应用的工具,不需要依赖窗口句柄枚举,还能直接获取通知的内容等信息。

给你一段C#的示例代码(C++也有对应的API,思路类似):

using System;
using System.Diagnostics;
using System.Windows.Automation;

public static AutomationElement LocateToastNotification()
{
    // 从桌面根元素开始遍历
    AutomationElement desktopRoot = AutomationElement.RootElement;
    
    // 筛选所有窗口类型的元素
    var windowCondition = new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Window);
    var allWindows = desktopRoot.FindAll(TreeScope.Children, windowCondition);

    foreach (AutomationElement window in allWindows)
    {
        // 获取窗口所属进程ID
        if (window.GetCurrentPropertyValue(AutomationElement.ProcessIdProperty) is int pid)
        {
            try
            {
                var process = Process.GetProcessById(pid);
                // 确认是ShellExperienceHost进程
                if (process.ProcessName.Equals("ShellExperienceHost", StringComparison.OrdinalIgnoreCase))
                {
                    // 检查窗口类名是否为通知窗口
                    if (window.GetCurrentPropertyValue(AutomationElement.ClassNameProperty) is string className &&
                        className.Equals("ToastNotificationWindow", StringComparison.OrdinalIgnoreCase))
                    {
                        return window;
                    }
                }
            }
            catch (ArgumentException)
            {
                // 进程可能已经退出,跳过
                continue;
            }
        }
    }
    return null;
}
注意事项
  • 关于权限:普通用户权限足够,不需要管理员权限
  • 版本兼容性:ToastNotificationWindow这个类名在Windows 10和Windows 11上都是稳定的,暂时没有发现版本差异
  • 如果是Windows 8及更早版本,类名可能不同,但现在应该很少有人用了,不用太担心

内容的提问来源于stack exchange,提问作者Zbigniew Rębacz

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 08:51:06