Page 1 of 2

Proper notifications from a SystrayIcon balloon message

Posted: Fri Feb 03, 2012 3:20 pm
by merendo
Cheers!

I've put together some code, which displays a Balloon message for a given SystrayIcon. Now I know this Balloon message can hand me back some user events, which I'd like to process, but I'm too clumsy with Window callbacks, so I'd appreciate some help here.

The MSDN documentation for NOTIFYICONDATA does say something about the events being parsed through the lParam, but it doesn't say which values I should look for when I want to know, for example, when the user has clicked the X on the Balloon message.

EDIT: I just learned that I wanna look for the event values #NIN_BALLOONTIMEOUT, #NIN_BALLOONHIDE and #NIN_BALLOONUSERCLICK, but I still don't know how or where to receive them.

Code: Select all

Structure MY_NOTIFYICONDATA
  cbSize.l
  hWnd.i
  uID.l
  uFlags.l
  uCallbackMessage.l
  hIcon.i
  szTip.s{128}
  dwState.l
  dwStateMark.l
  szInfo.s{256}
  StructureUnion
    uTimeout.l
    uVersion.l
  EndStructureUnion
  szInfoTitle.s{64}
  dwInfoFlags.l
EndStructure

Procedure MyCallback(WindowID, Message, wParam, lParam)
  If WindowID = WindowID(0)
    
    ; I assume I wanna process the event the system sends me here.
    
  EndIf
EndProcedure



OpenWindow(0, 0, 0, 200, 40, "Icontest", #PB_Window_SystemMenu)
ButtonGadget(0, 5, 5, 190, 30, "Show balloon message")


; You might wanna specify a path which corresponds to any valid .ico file on your system.
LoadImage(0, "C:\Programme\PureBasic\Examples\Sources - Advanced\Waponez II\Waponez.ico")

AddSysTrayIcon(0, WindowID(0), ImageID(0))
SysTrayIconToolTip(0, "Naah, I ain't no systray icon, I'm just cleanin' down 'ere.")


;SetWindowCallback(@MyCallback(), 0)  ; Uncomment me and - voilá - the button disappears. WTF?


NIData.MY_NOTIFYICONDATA
NIData\cbSize = SizeOf(MY_NOTIFYICONDATA)
NIData\hWnd = WindowID(0)
NIData\uFlags = #NIF_INFO
NIData\szInfo = "I'm just a small speech bubble."
NIData\uTimeout = 15000
NIData\uVersion = #NOTIFYICON_VERSION
NIData\szInfoTitle = "Hey there!"
NIData\dwInfoFlags = #NIIF_INFO

Repeat

  event = WaitWindowEvent()

  If event = #PB_Event_Gadget
    If EventGadget() = 0 
      Shell_NotifyIcon_(#NIM_MODIFY, NIData)
    EndIf
  EndIf

Until event = #PB_Event_CloseWindow
Weird enough, once I assign my window the callback procedure (incomplete), the button disappears. Anyone got an idea why that is?

Help appreciated!
merendo

Re: Proper notifications from a SystrayIcon balloon message

Posted: Fri Feb 03, 2012 5:49 pm
by merendo
Persistence leads to victory, isn't that right? I worked out most issues, here's my updated code:

Code: Select all

Structure MY_NOTIFYICONDATA
  cbSize.l
  hWnd.i
  uID.l
  uFlags.l
  uCallbackMessage.l
  hIcon.i
  szTip.s{128}
  dwState.l
  dwStateMark.l
  szInfo.s{256}
  StructureUnion
    uTimeout.l
    uVersion.l
  EndStructureUnion
  szInfoTitle.s{64}
  dwInfoFlags.l
EndStructure

#WINDOW = 0
Enumeration
#BUTTON_SHOW
#BUTTON_HIDE
#SYSTRAYICON
EndEnumeration


Procedure MyCallback(WindowID, Message, wParam, lParam)
  If WindowID = WindowID(0)
    
    If Message = 99999 ; Does that make sense?
      If wParam = #SYSTRAYICON
        Select lParam
          Case #NIN_BALLOONTIMEOUT
            Debug "Balloon timed out or was closed by the user"
          Case #NIN_BALLOONUSERCLICK
            Debug "Balloon got clicked by the user"
          Case #NIN_BALLOONHIDE
            Debug "Balloon got hidden"
        EndSelect
      EndIf
    EndIf
    
  EndIf
  ProcedureReturn #PB_ProcessPureBasicEvents 
EndProcedure



OpenWindow(#WINDOW, 0, 0, 200, 75, "Icontest", #PB_Window_SystemMenu)
ButtonGadget(#BUTTON_SHOW, 5, 5, 190, 30, "Show balloon message")
ButtonGadget(#BUTTON_HIDE, 5, 40, 190, 30, "Hide balloon message")


; You might wanna specify a path which corresponds to any valid .ico file on your system.
LoadImage(0, "C:\Programme\PureBasic\Examples\Sources - Advanced\Waponez II\Waponez.ico")

AddSysTrayIcon(#SYSTRAYICON, WindowID(0), ImageID(0))
SysTrayIconToolTip(#SYSTRAYICON, "Naah, I ain't no systray icon, I'm just cleanin' down 'ere.")

SetWindowCallback(@MyCallback(), 0)

NIData.MY_NOTIFYICONDATA
NIData\cbSize = SizeOf(MY_NOTIFYICONDATA)
NIData\hWnd = WindowID(0)
NIData\uID = #SYSTRAYICON
NIData\uFlags = #NIF_INFO | #NIF_MESSAGE
NIData\uCallbackMessage = 99999
NIData\uTimeout = 10
NIData\uVersion = #NOTIFYICON_VERSION
NIData\szInfoTitle = "Hey there!"
NIData\dwInfoFlags = #NIIF_INFO

Repeat

  event = WaitWindowEvent()

  If event = #PB_Event_Gadget
    Select EventGadget() 
      Case #BUTTON_SHOW:
        NIData\szInfo = "I'm just a small speech bubble."
        Shell_NotifyIcon_(#NIM_MODIFY, NIData)
      Case #BUTTON_HIDE:
        NIData\szInfo = ""
        Shell_NotifyIcon_(#NIM_MODIFY, NIData)
    EndSelect
  EndIf

Until event = #PB_Event_CloseWindow
I'm still not sure about which NOTIFYICONDATA\uCallbackMessage code to use, though. I used 99999 here because I figured that is likely to be available. According to MSDN, message is "An application-defined message identifier. The system uses this identifier to send notification messages to the window identified in hWnd." Can I just freely use any number I want here??

Re: Proper notifications from a SystrayIcon balloon message

Posted: Fri Feb 03, 2012 6:55 pm
by c4s
Nice code, looks useful to me!
I'd really say you can define any id you want to as the quoted description of MSDN says so.

And a small tip. Use #PB_Compiler_Home for the icon path, like this:

Code: Select all

LoadImage(0, #PB_Compiler_Home + "\Examples\Sources - Advanced\Waponez II\Waponez.ico")

Re: Proper notifications from a SystrayIcon balloon message

Posted: Sat Feb 04, 2012 10:44 am
by merendo
Well, I'm not sure now. When I add the line 'debug Message' in the Callback procedure (inside the If hWnd = WindowID() block), I get all sorts of messages whenever I interact with the window (like hovering over the window with the mouse), so it seems there are at least a good handfull of reserved codes.

Perhaps there is one, which I can safely use or even one which is meant for exactly this purpose? All you Callback geeks, where are you? 8)

Re: Proper notifications from a SystrayIcon balloon message

Posted: Sat Feb 04, 2012 11:45 am
by Kwai chang caine
Works great on XP
Thanks for sharing

Re: Proper notifications from a SystrayIcon balloon message

Posted: Tue Feb 07, 2012 2:38 pm
by merendo
You're very welcome. However, my original questions still remains unanswered: Which callbackMessage can I safely use without running the risk of interfering with existing codes? 99999 probably works, but this number is completely random and a shot in the dark - not a satisfactory solution to me.

Re: Proper notifications from a SystrayIcon balloon message

Posted: Tue Feb 07, 2012 5:54 pm
by c4s
I'd say using #WM_APP+1 is save.

Re: Proper notifications from a SystrayIcon balloon message

Posted: Tue Feb 07, 2012 6:03 pm
by merendo
Great. I know who to come back to if it doesn't work :twisted:

Thank you!

Btw: I got carried away and converted my example into a complete app. http://www.mediafire.com/?v9x45sjg4e1gz10 (Yeah, I know there is a better way to capture those Icons, but at the time I wrote this, I didn't care.)

Re: Proper notifications from a SystrayIcon balloon message

Posted: Mon Feb 20, 2012 5:51 pm
by merendo
Okay, problem. Please run this code:

Code: Select all

    Structure MY_NOTIFYICONDATA
      cbSize.l
      hWnd.i
      uID.l
      uFlags.l
      uCallbackMessage.l
      hIcon.i
      szTip.s{128}
      dwState.l
      dwStateMark.l
      szInfo.s{256}
      StructureUnion
        uTimeout.l
        uVersion.l
      EndStructureUnion
      szInfoTitle.s{64}
      dwInfoFlags.l
    EndStructure

    #WINDOW = 0
    Enumeration
    #BUTTON_SHOW
    #BUTTON_HIDE
    #SYSTRAYICON
    EndEnumeration


    Procedure MyCallback(WindowID, Message, wParam, lParam)
      If WindowID = WindowID(0)
       
        If Message = 99999 ; Does that make sense?
          If wParam = #SYSTRAYICON
            Select lParam
              Case #NIN_BALLOONTIMEOUT
                Debug "Balloon timed out or was closed by the user"
              Case #NIN_BALLOONUSERCLICK
                Debug "Balloon got clicked by the user"
              Case #NIN_BALLOONHIDE
                Debug "Balloon got hidden"
            EndSelect
          EndIf
        EndIf
       
      EndIf
      ProcedureReturn #PB_ProcessPureBasicEvents
    EndProcedure

    OpenWindow(#WINDOW, 0, 0, 200, 75, "Icontest", #PB_Window_SystemMenu)
    ButtonGadget(#BUTTON_SHOW, 5, 5, 190, 30, "Show balloon message")
    ButtonGadget(#BUTTON_HIDE, 5, 40, 190, 30, "Hide balloon message")


    ; You might wanna specify a path which corresponds to any valid .ico file on your system.
    LoadImage(0, #PB_Compiler_Home + "\Examples\Sources - Advanced\Waponez II\Waponez.ico")

    AddSysTrayIcon(#SYSTRAYICON, WindowID(0), ImageID(0))
    SysTrayIconToolTip(#SYSTRAYICON, "Naah, I ain't no systray icon, I'm just cleanin' down 'ere.")

    SetWindowCallback(@MyCallback(), 0)

    NIData.MY_NOTIFYICONDATA
    NIData\cbSize = SizeOf(MY_NOTIFYICONDATA)
    NIData\hWnd = WindowID(0)
    NIData\uID = #SYSTRAYICON
    NIData\uFlags = #NIF_INFO | #NIF_MESSAGE
    NIData\uCallbackMessage = 99999
    NIData\uTimeout = 10
    NIData\uVersion = #NOTIFYICON_VERSION
    NIData\szInfoTitle = "Hey there!"
    NIData\dwInfoFlags = #NIIF_INFO

    Repeat

      event = WaitWindowEvent()

      If event = #PB_Event_Gadget
        Select EventGadget()
          Case #BUTTON_SHOW:
            NIData\szInfo = "I'm just a small speech bubble."
            Shell_NotifyIcon_(#NIM_MODIFY, NIData)
          Case #BUTTON_HIDE:
            NIData\szInfo = ""
            Shell_NotifyIcon_(#NIM_MODIFY, NIData)
        EndSelect
      EndIf
      
			If event = #PB_Event_SysTray
				If EventGadget() = #SYSTRAYICON
					MessageRequester("Message", "Systrayicon got clicked.")
				EndIf
			EndIf

    Until event = #PB_Event_CloseWindow
First, try clicking on the systrayicon before first displaying a balloon message (the messagerequester should appear), then display a balloon message, then try clicking the systrayicon again (no messagerequester should appear). I have worked out that whenever I set the #NIF_MESSAGE flag in NIData\uFlags (meaning that the NIData\uCallbackMessage is valid), the PureBasic event handler somehow won't receive events from that systrayicon anymore. Does anybody know how to restore proper handling of these events?

Re: Proper notifications from a SystrayIcon balloon message

Posted: Mon Feb 20, 2012 7:06 pm
by RASHAD
Change

Code: Select all

    NIData.MY_NOTIFYICONDATA
    NIData\cbSize = SizeOf(MY_NOTIFYICONDATA)
    NIData\hWnd = WindowID(0)
    NIData\uID = #SYSTRAYICON
    NIData\uFlags = #NIF_INFO
    NIData\uCallbackMessage = 99999
    NIData\uTimeout = 10
    NIData\uVersion = #NOTIFYICON_VERSION
    NIData\szInfoTitle = "Hey there!"
    NIData\dwInfoFlags = #NIIF_INFO

Re: Proper notifications from a SystrayIcon balloon message

Posted: Mon Feb 20, 2012 7:19 pm
by merendo
If I do that (i.e. remove the #NIF_MESSAGE flag from NIData\uFlags), the events for the info bubble don't get processed anymore...

Re: Proper notifications from a SystrayIcon balloon message

Posted: Mon Feb 20, 2012 9:13 pm
by RASHAD
After using Shell_NotifyIcon_() the PB #PB_Event_SysTray change permanent
Next is a workaround for time being till we find another way

Code: Select all

    Structure MY_NOTIFYICONDATA
      cbSize.l
      hWnd.i
      uID.l
      uFlags.l
      uCallbackMessage.l
      hIcon.i
      szTip.s{128}
      dwState.l
      dwStateMark.l
      szInfo.s{256}
      StructureUnion
        uTimeout.l
        uVersion.l
      EndStructureUnion
      szInfoTitle.s{64}
      dwInfoFlags.l
    EndStructure

    #WINDOW = 0
    Enumeration
    #BUTTON_SHOW
    #BUTTON_HIDE
    #SYSTRAYICON
    EndEnumeration


Procedure MyCallback(WindowID, Message, wParam, lParam)
  If WindowID = WindowID(0)   
    If Message = #WM_NOTIFYICON ; Does that make sense?
      If wParam = #SYSTRAYICON
        Select lParam
          Case #NIN_BALLOONTIMEOUT
            Debug "Balloon timed out or was closed by the user"
            AddSysTrayIcon(#SYSTRAYICON, WindowID(0), ImageID(0)) 
          Case #NIN_BALLOONUSERCLICK
            Debug "Balloon got clicked by the user"
            AddSysTrayIcon(#SYSTRAYICON, WindowID(0), ImageID(0)) 
          Case #NIN_BALLOONHIDE
            Debug "Balloon got hidden"
            AddSysTrayIcon(#SYSTRAYICON, WindowID(0), ImageID(0))            
        EndSelect
      EndIf
    EndIf   
  EndIf              
  ProcedureReturn #PB_ProcessPureBasicEvents
EndProcedure

    OpenWindow(#WINDOW, 0, 0, 200, 75, "Icontest", #PB_Window_SystemMenu|#PB_Window_ScreenCentered)
    ButtonGadget(#BUTTON_SHOW, 5, 5, 190, 30, "Show balloon message")
    ButtonGadget(#BUTTON_HIDE, 5, 40, 190, 30, "Hide balloon message")


    ; You might wanna specify a path which corresponds to any valid .ico file on your system.
    LoadImage(0, #PB_Compiler_Home + "\Examples\Sources - Advanced\Waponez II\Waponez.ico")

    AddSysTrayIcon(#SYSTRAYICON, WindowID(0), ImageID(0))
    SysTrayIconToolTip(#SYSTRAYICON, "Naah, I ain't no systray icon, I'm just cleanin' down 'ere.")

    SetWindowCallback(@MyCallback(), 0)

    NIData.MY_NOTIFYICONDATA
    NIData\cbSize = SizeOf(MY_NOTIFYICONDATA)
    NIData\hWnd = WindowID(0)
    NIData\uID = #SYSTRAYICON
    NIData\uFlags = #NIF_INFO | #NIF_MESSAGE
    NIData\uCallbackMessage = #WM_NOTIFYICON
    NIData\uTimeout = 2
    NIData\uVersion = #NOTIFYICON_VERSION
    NIData\szInfoTitle = "Hey there!"
    NIData\dwInfoFlags = #NIIF_INFO

    Repeat

      event = WaitWindowEvent()

      If event = #PB_Event_Gadget
        Select EventGadget()
          Case #BUTTON_SHOW:
            NIData\szInfo = "I'm just a small speech bubble."
            Shell_NotifyIcon_(#NIM_MODIFY, NIData)
          Case #BUTTON_HIDE:
            NIData\szInfo = ""
            Shell_NotifyIcon_(#NIM_MODIFY, NIData)
        EndSelect
      EndIf
      
     If event = #PB_Event_SysTray
        If EventGadget() = #SYSTRAYICON
           MessageRequester("Message", "Systrayicon got clicked.")
        EndIf
     EndIf

    Until event = #PB_Event_CloseWindow


Re: Proper notifications from a SystrayIcon balloon message

Posted: Mon Feb 20, 2012 9:21 pm
by merendo
Seems to work fine for me now. Thank you very much!

Re: Proper notifications from a SystrayIcon balloon message

Posted: Mon Feb 20, 2012 10:56 pm
by RASHAD
Hi merendo
I did not like the previous workaround

Change

Code: Select all

Procedure MyCallback(WindowID, Message, wParam, lParam)
  If WindowID = WindowID(0)   
    If Message = #WM_NOTIFYICON ; Does that make sense?
      If wParam = #SYSTRAYICON
        Select lParam
          Case #NIN_BALLOONTIMEOUT
            Debug "Balloon timed out or was closed by the user"
            
          Case #NIN_BALLOONUSERCLICK
            Debug "Balloon got clicked by the user"
            
          Case #NIN_BALLOONHIDE
            Debug "Balloon got hidden"
                     
          Case #WM_LBUTTONUP
             MessageRequester("Message", "Systrayicon got clicked.")
        EndSelect

      EndIf
    EndIf   
  EndIf              
  ProcedureReturn #PB_ProcessPureBasicEvents
EndProcedure


Re: Proper notifications from a SystrayIcon balloon message

Posted: Tue Feb 21, 2012 7:38 am
by merendo
That works even better. Your efforts are much appreciated, thanks!

Perhaps when someone from the PB team reads this posting, they could tell us which uCallbackMessage the Notifyicon needs to be reassigned in order to restore the systray icon to it's original function, but until then, the workaround provides a satisfactory solution.