Windows11 Python非阻塞并发弹出多个系统通知的方案咨询
问题根因
报错来自两个核心问题:
- 所有通知进程固定使用相同的窗口类名
PythonTaskbar、固定托盘图标ID0,多进程同时启动时会出现托盘资源互相覆盖,销毁阶段调用Shell_NotifyIcon(NIM_DELETE)找不到对应合法资源,直接抛出未指定错误。 - 原代码用
time.sleep硬阻塞等待超时后直接销毁窗口,没有启动Windows标准消息循环,托盘相关的系统回调消息无法按正确时序处理,多实例并发时消息处理逻辑乱序触发异常。
修复方案
方案1:改造原有原生win32api代码
核心修改点:为每个通知实例生成唯一的窗口类名、托盘图标ID避免资源冲突;用窗口定时器+标准消息循环替代time.sleep硬阻塞,保证消息按正确时序处理;清理阶段加异常捕获兜底偶发资源释放失败问题。
修复后的balloontip.py完整代码:
from win32api import * from win32gui import * import win32con import sys, os import random import string # 生成随机字符串做实例唯一标识 def gen_random_str(length=8): return ''.join(random.choice(string.ascii_letters) for _ in range(length)) class WindowsBalloonTip: def __init__(self, title, msg, timeout): self.hwnd = None self.hinst = GetModuleHandle(None) self.class_name = f"PythonTaskbar_{gen_random_str()}" self.icon_id = random.randint(1, 10000) message_map = { win32con.WM_DESTROY: self.OnDestroy, win32con.WM_TIMER: self.OnTimer, } wc = WNDCLASS() wc.hInstance = self.hinst wc.lpszClassName = self.class_name wc.lpfnWndProc = message_map RegisterClass(wc) style = win32con.WS_OVERLAPPED | win32con.WS_SYSMENU self.hwnd = CreateWindow( self.class_name, "Taskbar", style, 0, 0, win32con.CW_USEDEFAULT, win32con.CW_USEDEFAULT, 0, 0, self.hinst, None) UpdateWindow(self.hwnd) iconPathName = os.path.abspath(os.path.join(sys.path[0], "balloontip.ico")) icon_flags = win32con.LR_LOADFROMFILE | win32con.LR_DEFAULTSIZE try: hicon = LoadImage(self.hinst, iconPathName, win32con.IMAGE_ICON, 0, 0, icon_flags) except: hicon = LoadIcon(0, win32con.IDI_APPLICATION) flags = NIF_ICON | NIF_MESSAGE | NIF_TIP nid = (self.hwnd, self.icon_id, flags, win32con.WM_USER+20, hicon, "tooltip") Shell_NotifyIcon(NIM_ADD, nid) Shell_NotifyIcon(NIM_MODIFY, (self.hwnd, self.icon_id, NIF_INFO, win32con.WM_USER+20, hicon, "Balloon tooltip", msg, 200, title)) # 定时器替代阻塞sleep,保证消息循环正常运行 SetTimer(self.hwnd, 1, int(timeout*1000), None) PumpMessages() def OnTimer(self, hwnd, msg, wparam, lparam): KillTimer(hwnd, 1) DestroyWindow(hwnd) def OnDestroy(self, hwnd, msg, wparam, lparam): nid = (self.hwnd, self.icon_id) # 清理操作加异常兜底,避免偶发资源不存在报错 try: Shell_NotifyIcon(NIM_DELETE, nid) except Exception: pass PostQuitMessage(0) def balloon_tip(title, msg, timeout): WindowsBalloonTip(title, msg, timeout) if __name__ == '__main__': balloon_tip(sys.argv[1], sys.argv[2], float(sys.argv[3]))
改完后无需修改主业务代码,不管是两个独立脚本同时触发通知,还是单脚本移除wait()连续启动多个通知进程,都不会触发报错,所有通知会按系统规则排队弹出,主进程也不需要等待通知执行完成就能继续跑后续逻辑。
方案2:使用封装好的通知库(成本更低)
如果不想手动维护win32api底层逻辑,可以直接用适配Win10/11的通知库,天生支持多通知并发,不需要自己处理资源冲突、消息循环问题。
先安装依赖:pip install win11toast
后续不需要单独维护balloontip.py子进程脚本,直接在主代码里调用即可,调用不会阻塞主逻辑运行:
from win11toast import toast # 连续触发多个通知无冲突,不需要wait等待 toast('alala', 'code 1') toast('alala', 'code 2') toast('alala', 'code 3')
该库底层已经做好了资源隔离和消息处理,短时间内触发大量通知也不会抛错,稳定性比手动封装win32api更高。
内容的提问来源于stack exchange,提问作者Digital Farmer
相关产品推荐
相关产品推荐

