Jump to content

Tray notifications redirector


Yashied
 Share

Recommended Posts

LAST VERSION - 1.0

19-May-12

I think many of you would like to perform some action (for example, to call the dialog box) when a user clicks on the tray tip. Here is a simple way to do it. Unfortunately, without .dll do not seem possible. Note, only AutoIt window can be registered as a source window, see example.

AITray.png

AITray.dll (x86 and x64)

Previous downloads: 74

AITray.zip

Example

#Include <WindowsConstants.au3>

Opt('TrayAutoPause', 0)
Opt('WinTitleMatchMode', 3)
Opt('WinWaitDelay', 0)

Global Const $NIN_BALLOONSHOW = $WM_USER + 2
Global Const $NIN_BALLOONHIDE = $WM_USER + 3
Global Const $NIN_BALLOONUSERCLICK = $WM_USER + 5
Global Const $NIN_BALLOONTIMEOUT = $WM_USER + 4

$hForm = GUICreate('')

If @AutoItX64 Then
    $hDll = DllOpen(@ScriptDir & '\AITray_x64.dll')
Else
    $hDll = DllOpen(@ScriptDir & '\AITray.dll')
EndIf
If $hDll <> -1 Then
    $Ret = DllCall($hDll, 'int', 'AISetTrayRedirection', 'hwnd', WinGetHandle(AutoItWinGetTitle()), 'hwnd', $hForm)
    If (@Error) Or (Not $Ret[0]) Then
        DllClose($hDll)
        Exit
    EndIf
Else
    Exit
EndIf

GUIRegisterMsg($WM_USER + 1, 'WM_TRAYNOTIFY')

TrayTip('Tip', 'This is a tray tip, click here.', 10, 1)

While 1
    Sleep(1000)
WEnd

DllCall($hDll, 'int', 'AIRemoveTrayRedirection')
DllClose($hDll)

Func WM_TRAYNOTIFY($hWnd, $iMsg, $wParam, $lParam)
    Switch $hWnd
        Case $hForm
            Switch $lParam
                Case $NIN_BALLOONSHOW
                    ConsoleWrite('Balloon tip show.' & @CR)
                Case $NIN_BALLOONHIDE
                    ConsoleWrite('Balloon tip hide.' & @CR)
                Case $NIN_BALLOONUSERCLICK
                    ConsoleWrite('Balloon tip click.' & @CR)
                Case $NIN_BALLOONTIMEOUT
                    ConsoleWrite('Balloon tip close.' & @CR)
            EndSwitch
    EndSwitch
EndFunc   ;==>WM_TRAYNOTIFY
Edited by Yashied
Link to comment
Share on other sites

And about this DLL?

#WM_TRAYNOTIFY = #WM_USER + 1

Prototype.l SetWindowSubclass(*HWnd, *HProc, ID.i = 1000, IParam.i = 0)
Prototype.l RemoveWindowSubclass(*HWnd, *HProc, ID.i = 1000)

Global Comctl32.l = OpenLibrary(#PB_Any, "comctl32.dll")
Global User32.l = OpenLibrary(#PB_Any, "user32.dll")

Global AutoIt.i = 0
Global Target.i = 0

Procedure.i SubclassProc(*HWnd, Msg.l, *WParam, *LParam, *ID, *IParam)
    Select Msg
        Case #WM_TRAYNOTIFY
            Select *LParam
                Case #NIN_BALLOONSHOW, #NIN_BALLOONHIDE, #NIN_BALLOONUSERCLICK, #NIN_BALLOONTIMEOUT
                    If Target
                        CallFunction(User32, "PostMessageW", Target, Msg, *WParam, *LParam)
                    EndIf
            EndSelect
    EndSelect
    ProcedureReturn CallFunction(Comctl32, "DefSubclassProc", *HWnd, Msg, *WParam, *LParam)
EndProcedure

ProcedureDLL.l AIRemoveTrayRedirection()
    If Not AutoIt
        ProcedureReturn 0
    EndIf
    RemoveWindowSubclass.RemoveWindowSubclass = GetFunction(Comctl32, "RemoveWindowSubclass")
    If IsWindow(AutoIt)
        If Not RemoveWindowSubclass(AutoIt, @SubclassProc())
            ProcedureReturn 0
        EndIf
    EndIf
    Target = 0
    AutoIt = 0
    ProcedureReturn 1
EndProcedure

ProcedureDLL.l AISetTrayRedirection(*HAutoIt, *HTarget)
    If (Not *HAutoIt) Or (Not *HTarget)
        ProcedureReturn 0
    EndIf
    If AutoIt
        If Not AIRemoveTrayRedirection()
            ProcedureReturn 0
        EndIf
    EndIf
    SetWindowSubclass.SetWindowSubclass = GetFunction(Comctl32, "SetWindowSubclass")
    If Not SetWindowSubclass(*HAutoIt, @SubclassProc())
        ProcedureReturn 0
    EndIf
    AutoIt = *HAutoIt
    Target = *HTarget
    ProcedureReturn 1
EndProcedure
Link to comment
Share on other sites

No, DllCallbackRegister() kill the script.

We just need a small ASM-stub to hook the window procedure and forwards WM_TRAYNOTIFY when the TrayClick is registered.

Edit: Does this seem about right? Values not yet read from stack, only symbols:

use32
test uMsg, WM_TRAYNOTIFY
jz skip_notify
push lparam
push wparam
push uMsg
push hWndRedirect
call PostMessage
skip_notify: push lparam
push wparam
push uMsg
push hWnd
call DefSubclassProc
ret 24
Edited by ProgAndy

*GERMAN* [note: you are not allowed to remove author / modified info from my UDFs]My UDFs:[_SetImageBinaryToCtrl] [_TaskDialog] [AutoItObject] [Animated GIF (GDI+)] [ClipPut for Image] [FreeImage] [GDI32 UDFs] [GDIPlus Progressbar] [Hotkey-Selector] [Multiline Inputbox] [MySQL without ODBC] [RichEdit UDFs] [SpeechAPI Example] [WinHTTP]UDFs included in AutoIt: FTP_Ex (as FTPEx), _WinAPI_SetLayeredWindowAttributes

Link to comment
Share on other sites

Ok, Is there any chance of making entirely in Autoit?

No, DllCallbackRegister() kill the script.

Pure AutoIt works for me...

#include <windowsconstants.au3>

Opt('TrayAutoPause', 0)
Opt('WinTitleMatchMode', 3)
Opt('WinWaitDelay', 0)
Opt('TrayMenuMode', 3)

Global Const $WM_TRAYNOTIFY = $WM_USER + 1
Global Const $NIN_BALLOONSHOW = $WM_USER + 2
Global Const $NIN_BALLOONHIDE = $WM_USER + 3
Global Const $NIN_BALLOONUSERCLICK = $WM_USER + 5
Global Const $NIN_BALLOONTIMEOUT = $WM_USER + 4
Global Const $subID = 1234
Global $iTip = 2
Global $psub = DllCallbackRegister("_subclass", "lresult", "hwnd;uint;wparam;lparam;uint_ptr;dword_ptr")
Global $hForm = GUICreate('')

DllCall("comctl32.dll", "bool", "SetWindowSubclass", "hwnd", WinGetHandle(AutoItWinGetTitle()), "ptr", DllCallbackGetPtr($psub), "uint_ptr", $subID, "dword_ptr", 0)

GUIRegisterMsg($WM_TRAYNOTIFY, 'WM_TRAYNOTIFY')

Global $iShow = TrayCreateItem("Show New Tip")
TrayCreateItem("")
Global $iExit = TrayCreateItem("Exit")

TrayTip('Tip', 'This is a tray tip, click here.', 10, 1)

While 1
    Switch TrayGetMsg()
        Case $iShow
            TrayTip('Tip', 'This is a tray tip, click here. [ ' & $iTip & ' ]', 10, 1)
            $iTip += 1
        Case $iExit
            ExitLoop
    EndSwitch
WEnd

DllCall("comctl32.dll", "bool", "RemoveWindowSubclass", "hwnd", WinGetHandle(AutoItWinGetTitle()), "ptr", DllCallbackGetPtr($psub), "uint_ptr", $subID)
DllCallbackFree($psub)

Func WM_TRAYNOTIFY($hWnd, $iMsg, $wParam, $lParam)
    Switch $hWnd
        Case $hForm
            Switch $lParam
                Case $NIN_BALLOONSHOW
                    ConsoleWrite('Balloon tip show.' & @CR)
                Case $NIN_BALLOONHIDE
                    ConsoleWrite('Balloon tip hide.' & @CR)
                Case $NIN_BALLOONUSERCLICK
                    ConsoleWrite('Balloon tip click.' & @CR)
                Case $NIN_BALLOONTIMEOUT
                    ConsoleWrite('Balloon tip close.' & @CR)
            EndSwitch
    EndSwitch
EndFunc   ;==>WM_TRAYNOTIFY

Func _subclass($hwnd, $uMsg, $wParam, $lParam, $uIdSubclass, $dwRefData)
    #forceref $hwnd, $uMsg, $wParam, $lParam, $uIdSubclass, $dwRefData
    Switch $uMsg
        Case $WM_TRAYNOTIFY
            Switch $uIdSubclass
                Case $subID
                    Switch $lParam
                        Case $NIN_BALLOONSHOW, $NIN_BALLOONHIDE, $NIN_BALLOONUSERCLICK, $NIN_BALLOONTIMEOUT
                            If $hForm Then
                                DllCall("user32.dll", "bool", "PostMessageW", "hwnd", $hForm, "uint", $uMsg, "wparam", $wParam, "lparam", $lParam)
                            EndIf
                    EndSwitch
            EndSwitch
    EndSwitch
    Local $ret = DllCall("comctl32.dll", "lresult", "DefSubclassProc", "hwnd", $hwnd, "uint", $uMsg, "wparam", $wParam, "lparam", $lParam)
    Return $ret[0]
EndFunc

However as noted in MSDN in the comments, there is no differentiation between a timeout and a close event. Seems like a Windows bug.

Edited by wraithdu
Link to comment
Share on other sites

Link to comment
Share on other sites

wraithdu, please comment the following lines and try to close the script from the tray.

;Opt('TrayAutoPause', 0)
Opt('WinTitleMatchMode', 3)
Opt('WinWaitDelay', 0)
;Opt('TrayMenuMode', 3)
Edited by Yashied
Link to comment
Share on other sites

Can confirm that wraithdu's code works properly on my machine (Win7 x64 running it at x86 & x64 mode)!

Br,

UEZ

Please don't send me any personal message and ask for support! I will not reply!

Selection of finest graphical examples at Codepen.io

The own fart smells best!
Her 'sikim hıyar' diyene bir avuç tuz alıp koşma!
¯\_(ツ)_/¯  ٩(●̮̮̃•̃)۶ ٩(-̮̮̃-̃)۶ૐ

Link to comment
Share on other sites

wraithdu, please comment the following lines and try to close the script from the tray.

;Opt('TrayAutoPause', 0)
Opt('WinTitleMatchMode', 3)
Opt('WinWaitDelay', 0)
;Opt('TrayMenuMode', 3)

Why would I purposely break it? If it is required to change the menu mode so you can exit properly, then do it! You must unsubclass and unregister the callback. You can't do that from the default tray menu, unless you want to add a function with OnAutoItExitRegister (didn't try, but would probably also work). Edited by wraithdu
Link to comment
Share on other sites

So do it this way:

#include <windowsconstants.au3>

Opt('TrayAutoPause', 0)
Opt('WinTitleMatchMode', 3)
Opt('WinWaitDelay', 0)
Opt('TrayMenuMode', 2)

Global Const $WM_TRAYNOTIFY = $WM_USER + 1
Global Const $NIN_BALLOONSHOW = $WM_USER + 2
Global Const $NIN_BALLOONHIDE = $WM_USER + 3
Global Const $NIN_BALLOONUSERCLICK = $WM_USER + 5
Global Const $NIN_BALLOONTIMEOUT = $WM_USER + 4
Global Const $subID = 1234
Global $iTip = 2
Global $psub = DllCallbackRegister("_subclass", "lresult", "hwnd;uint;wparam;lparam;uint_ptr;dword_ptr")
Global $hForm = GUICreate('')

DllCall("comctl32.dll", "bool", "SetWindowSubclass", "hwnd", WinGetHandle(AutoItWinGetTitle()), "ptr", DllCallbackGetPtr($psub), "uint_ptr", $subID, "dword_ptr", 0)

GUIRegisterMsg($WM_TRAYNOTIFY, 'WM_TRAYNOTIFY')
OnAutoItExitRegister("SafeExit")

Global $iShow = TrayCreateItem("Show New Tip")
;~ TrayCreateItem("")
;~ Global $iExit = TrayCreateItem("Exit")

TrayTip('Tip', 'This is a tray tip, click here.', 10, 1)

While 1
    Switch TrayGetMsg()
        Case $iShow
            TrayTip('Tip', 'This is a tray tip, click here. [ ' & $iTip & ' ]', 10, 1)
            $iTip += 1
;~         Case $iExit
;~             ExitLoop
    EndSwitch
;~  Sleep(1000)
WEnd

;~ DllCall("comctl32.dll", "bool", "RemoveWindowSubclass", "hwnd", WinGetHandle(AutoItWinGetTitle()), "ptr", DllCallbackGetPtr($psub), "uint_ptr", $subID)
;~ DllCallbackFree($psub)

Func SafeExit()
    ConsoleWrite("exiting..." & @CRLF)
    DllCall("comctl32.dll", "bool", "RemoveWindowSubclass", "hwnd", WinGetHandle(AutoItWinGetTitle()), "ptr", DllCallbackGetPtr($psub), "uint_ptr", $subID)
    DllCallbackFree($psub)
EndFunc

Func WM_TRAYNOTIFY($hWnd, $iMsg, $wParam, $lParam)
    Switch $hWnd
        Case $hForm
            Switch $lParam
                Case $NIN_BALLOONSHOW
                    ConsoleWrite('Balloon tip show.' & @CR)
                Case $NIN_BALLOONHIDE
                    ConsoleWrite('Balloon tip hide.' & @CR)
                Case $NIN_BALLOONUSERCLICK
                    ConsoleWrite('Balloon tip click.' & @CR)
                Case $NIN_BALLOONTIMEOUT
                    ConsoleWrite('Balloon tip close.' & @CR)
            EndSwitch
    EndSwitch
EndFunc   ;==>WM_TRAYNOTIFY

Func _subclass($hwnd, $uMsg, $wParam, $lParam, $uIdSubclass, $dwRefData)
    #forceref $hwnd, $uMsg, $wParam, $lParam, $uIdSubclass, $dwRefData
    Switch $uMsg
        Case $WM_TRAYNOTIFY
            Switch $uIdSubclass
                Case $subID
                    Switch $lParam
                        Case $NIN_BALLOONSHOW, $NIN_BALLOONHIDE, $NIN_BALLOONUSERCLICK, $NIN_BALLOONTIMEOUT
                            If $hForm Then
                                DllCall("user32.dll", "bool", "PostMessageW", "hwnd", $hForm, "uint", $uMsg, "wparam", $wParam, "lparam", $lParam)
                            EndIf
                    EndSwitch
            EndSwitch
    EndSwitch
    Local $ret = DllCall("comctl32.dll", "lresult", "DefSubclassProc", "hwnd", $hwnd, "uint", $uMsg, "wparam", $wParam, "lparam", $lParam)
    Return $ret[0]
EndFunc
Edited by wraithdu
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

×
×
  • Create New...