1

I generate a basic notifications on my Windows 11 like this:

from win32api import *
from win32gui import *
import win32con
import sys, os
import struct
import time

class WindowsBalloonTip:
    def __init__(self, title, msg, timeout):
        message_map = {
                win32con.WM_DESTROY: self.OnDestroy,
        }
        wc = WNDCLASS()
        hinst = wc.hInstance = GetModuleHandle(None)
        wc.lpszClassName = "PythonTaskbar"
        wc.lpfnWndProc = message_map
        classAtom = RegisterClass(wc)

        style = win32con.WS_OVERLAPPED | win32con.WS_SYSMENU
        self.hwnd = CreateWindow( classAtom, "Taskbar", style, \
                0, 0, win32con.CW_USEDEFAULT, win32con.CW_USEDEFAULT, \
                0, 0, 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(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, 0, flags, win32con.WM_USER+20, hicon, "tooltip")
        Shell_NotifyIcon(NIM_ADD, nid)
        Shell_NotifyIcon(NIM_MODIFY, \
                         (self.hwnd, 0, NIF_INFO, win32con.WM_USER+20,\
                          hicon, "Balloon  tooltip",title,200,msg))

        time.sleep(timeout)
        DestroyWindow(self.hwnd)
    def OnDestroy(self, hwnd, msg, wparam, lparam):
        nid = (self.hwnd, 0)
        Shell_NotifyIcon(NIM_DELETE, nid)
        PostQuitMessage(0)
def balloon_tip(title, msg, timeout):
    w=WindowsBalloonTip(msg, title, timeout)

if __name__ == '__main__':
    balloon_tip(sys.argv[1], sys.argv[2], float(sys.argv[3]))

I keep two separate codes running simultaneously in a loop every 30 seconds and throughout the day, in case they both generate a notification at the same time (to shorten the codes in the question, I didn't put the looping part, just the subprocess call):

code_1.py:

import subprocess
import sys

subprocess.Popen([sys.executable, "balloontip.py", 'alala', 'code 1', '1'])

code_2.py:

import subprocess
import sys

subprocess.Popen([sys.executable, "balloontip.py", 'alala', 'code 2', '1'])

This error is encountered:

Python WNDPROC handler failed
Traceback (most recent call last):
  File "C:\Users\Computador\Desktop\Tests\balloontip.py", line 42, in OnDestroy
    Shell_NotifyIcon(NIM_DELETE, nid)
pywintypes.error: (-2147467259, 'Shell_NotifyIcon', 'Unspecified error')

And let's say a single code at some point tries to create multiple calls to the notifications:

When waiting for each of the notifications to finish executing, no error is found:

import subprocess
import sys

one = '1'
two = '2'
three = '3'

if one == '1':
    p1 = subprocess.Popen([sys.executable, "balloontip.py", 'alala', one, '1'])
    p1.wait()

if two == '2':
    p2 = subprocess.Popen([sys.executable, "balloontip.py", 'alala', two, '1'])
    p2.wait()

if three == '3':
    p3 = subprocess.Popen([sys.executable, "balloontip.py", 'alala', three, '1'])
    p3.wait()

# rest of code...

But I can't wait for the notifications executions finish to continue the code, so I need to remove the wait:

import subprocess
import sys

one = '1'
two = '2'
three = '3'

if one == '1':
    p1 = subprocess.Popen([sys.executable, "balloontip.py", 'alala', one, '1'])

if two == '2':
    p2 = subprocess.Popen([sys.executable, "balloontip.py", 'alala', two, '1'])

if three == '3':
    p3 = subprocess.Popen([sys.executable, "balloontip.py", 'alala', three, '1'])

# rest of code...

This error is encountered:

Python WNDPROC handler failed
Traceback (most recent call last):
  File "C:\Users\Computador\Desktop\Tests\balloontip.py", line 42, in OnDestroy
    Shell_NotifyIcon(NIM_DELETE, nid)
pywintypes.error: (-2147467259, 'Shell_NotifyIcon', 'Unspecified error')

Is there a way to receive these notifications without having to wait for each one to finish running?

Digital Farmer
  • 1,705
  • 5
  • 17
  • 67
  • 3
    The Windows.UI.Notifications API is the new way of doing toast notifications and this presumably would let you queue up multiples (at least it looks like it does). No idea if that can be accessed via Python or not. If the legacy API won't let you queue multiples the only suggestion I have is to spawn a separate thread which does them one at a time and waits in between each one. – Jonathan Potter Jun 16 '22 at 21:09
  • 1
    You are using the same code as [this question](https://stackoverflow.com/questions/17262941/) (but without [this fix](https://stackoverflow.com/a/17262942/65863)). Your `OnDestroy` error is failing to remove the tray icon, make sure the unused fields of `nid` are zeroed out. That said, each invocation of your script should be running in its own process and creating its own tray icon, so issuing multiple notifications at a time without waits is fine. However, Windows simply won't display more than 1 balloon notification at a time, this is stated as much in `Shell_NotifyIcon()`'s documentation. – Remy Lebeau Jun 17 '22 at 01:00
  • Hi @RemyLebeau Unfortunately, even with the modifications on this fix, the error remains when more than one notification is sent, I honestly can't understand the reason for this, since in theory each subprocess generates a new call. With Jonathan Potter said it is possible to send as many calls as you want without generating the error, but I still couldn't understand how to set the timeout and also how not to keep the trace of the notification archived. – Digital Farmer Jun 17 '22 at 01:29
  • 1
    @DigitalFarmer "*the error remains when more than one notification is sent*" - then you are just going to have to debug the code for yourself. Did you see my earlier comment about [zeroing out `nid`](https://stackoverflow.com/questions/8050904/)? "*I still couldn't understand how to ...*" - you can't, when using the `Shell_NotifyIcon()` API. Jonathan was telling you to use a *different* API for that purpose. – Remy Lebeau Jun 17 '22 at 01:35
  • 1
    @JonathanPotter "*If the legacy API won't let you queue multiples the only suggestion I have is to spawn a separate thread which does them one at a time and waits in between each one.*" - that won't help. `Shell_NotifyIcon()` can only display one balloon at a time, regardless of how many threads are asking. And making multiple threads wait on each other just to serialize access to `Shell_NotifyIcon()` is really no better than simply calling `Shell_NotifyIcon()` in a loop in the main thread by itself. – Remy Lebeau Jun 17 '22 at 01:39
  • @RemyLebeau Sorry for my misunderstanding of English, I actually understand your comment now. I'm really migrating, I'm waiting for help to understand how to use [Windows::Foundation::DateTime][1] [1]: https://stackoverflow.com/questions/72652569/windows-ui-notifications-api-%e2%86%92-add-specific-expirationtime-in-toastnotification – Digital Farmer Jun 17 '22 at 01:39

0 Answers0