I saw this question posted to the list a few times, and just wrote a solution. There are certainly no API calls / sane way to do this in Windows, but where there is a will there is a way.
The basic idea is, turn your icon black, scan the screen for a black square, and then turn it back to your icon. In practice, this can be done quick enough for the user to not notice. If the loops were in C, it would definitely be too fast to notice It would be nice to just hunt for the icon itself, but unfortunately, various versions of windows (win2k especially) play with the coloring too heavily for this method to be at all reliable.
the ico file i use as the âblackâ icon while Iâm searching can be downloaded from here: http://dl.getdropbox.com/u/100516/blank1.ico
this implementation uses pywin32. If you are not using pywin32 and donât want to add a dependency on it, it should be relatively easy to use ctypes to make the win32 api calls. Note that YOU need to fill in the code that sets/unsets the black icon!
Iâve used this successfully on win2k, xp, vista, and windows 7 (when the bar is showing). If your icon isnât showing, itâs not gonna work In that case, you could fall back to using the rect of the whole notification area (see the first line of the function get_screen_rect. note that win32âs GetWindowRect returns (left, top, right, bottom) NOT (left, top, width, height)). The rect returned assumes your icon is 16x16, if the user has âbig iconsâ turned on, then you may have to detect that in a more fancy way!!
¡¡¡
################
import win32gui
def get_notification_hwnd():
tray_window_class = âShell_TrayWndâ
tray_hwnd = win32gui.FindWindowEx(0, 0,
tray_window_class, ââ)
assert tray_hwnd, âCouldnât get tray hwndâ
notification_window_class = âTrayNotifyWndâ
notification_hwnd = win32gui.FindWindowEx(tray_hwnd, 0,
notification_window_class, ââ)
return notification_hwnd
This function tries its best to return a rect similar to the GetScreenRect function from wx.Window
the rect returned is (left, top, width, height)
Returns None if there were problems, excepts if you donât have a notification area at all
def get_screen_rect():
notification_rect = win32gui.GetWindowRect(get_notification_hwnd())
screendc = win32gui.GetDC(None)
be lenient about what we call âblackâ, windows messes with colors sometimes
potential_blacks = set()
for r in xrange(4):
for g in xrange(4):
for b in xrange(4):
potential_blacks.add(r | (g << 8) | (b << 16))
!!! SET YOUR ICON TO BLACK.ICO HERE !!!
try:
the_rect = None
for y in xrange(notification_rect[3]-3, notification_rect[1], -1):
num_consecutive_black = 0
for x in xrange(notification_rect[0], notification_rect[2], 1):
if all((win32gui.GetPixel(screendc,
x,
y+yoff) in potential_blacks) \
for yoff in (-2, 0, 2)):
num_consecutive_black += 1
else:
num_consecutive_black = 0
if num_consecutive_black > 8: # p sure this is us
the_rect = (x-8, y-13, 16, 16)
break
if the_rect is not None:
break
return the_rect
finally:
!!! RESTORE YOUR ICON TO WHATEVER IT WAS HERE !!!
################
Note: this is a dirty, dirty hack. Donât blame me if it doesnât work. This is /certainly/ not the ârightâ way to do it, but it just might be the /only/ way. There is an even more fun hack that works up to xp, involving allocating memory in the notifcation process itself, but Vistaâs UIPI has ended that as a possibility. WOOOO.
Happy to answer any questions you may have.
Hope this helps! Happy hacking,
Mike