Что нового

Получение уведомлений от Tray Tip

Yashied

Модератор
Команда форума
Глобальный модератор
Сообщения
5,379
Репутация
2,724
AutoIt: 3.3.6.1
Версия: 1.0

Категория: Окна и диалоги

Описание: Я думаю, что многие из вас хотели бы произвести какие-либо действия (например вызвать диалоговое окно), когда пользователь кликает на Tray Tip. Вот простой способ реализовать это. К сожалению, написать алгоритм без .dll не представляется возможным. Важно, только AutoIt окно может быть зарегистрировано в качестве исходного окна для получения уведомлений (см. пример).

Файл(ы): AITray.zip

Пример:
Код:
#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

Скриншот:

AITray.png

Источник: Tray notifications redirector (оффициальный форум)
Автор: Yashied
 

CreatoR

Must AutoIt!
Команда форума
Администратор
Сообщения
8,671
Репутация
2,481
Yashied [?]
К сожалению, написать алгоритм без .dll не представляется возможным.
Ну почему, вот первый набросок, грубый, но вроде работает:

Код:
#include <Misc.au3>
#include <Array.au3>

Global $hTrayTip_Wnd
Global $iTrayTip_Close = 0
Global $iTrayTip_Click = 0
Global $iTrayTip_Hide = 0

_TrayTipEx('Tip', 'This is a tray tip, click here.', 5, 1)
Sleep(15000)

Func _TrayTipEx($sTitle, $sText, $iTimeOut, $iOptions = 0, $sEventFunc = '_TrayTipEvent')
	Local  $aBefore_WL, $aAfter_WL
	
	$aBefore_WL = _WinListVisible('[CLASS:tooltips_class32]')
	TrayTip($sTitle, $sText, $iTimeOut, $iOptions)
	WinWait('[CLASS:tooltips_class32]')
	$aAfter_WL = _WinListVisible('[CLASS:tooltips_class32]')
	
	For $i = 1 To $aAfter_WL[0][0]
		If _ArraySearch($aBefore_WL, $aAfter_WL[$i][1], 1, 0, 0, 0, 1, 1) = -1 Then
			$hTrayTip_Wnd = $aAfter_WL[$i][1]
			AdlibRegister($sEventFunc, 10)
			Return 1
		EndIf
	Next
	
	Return 0
EndFunc

Func _TrayTipEvent()
	Local $iOld_Opt_MCM = Opt("MouseCoordMode", 1)
	Local $aWinPos = WinGetPos($hTrayTip_Wnd)
	Local $aMousePos = MouseGetPos()
	Local $ahWnd = DllCall("user32.dll", "int", "WindowFromPoint", "long", $aMousePos[0], "long", $aMousePos[1])
	Local $iEvent = 0
	
	Opt("MouseCoordMode", $iOld_Opt_MCM)
	
    If $ahWnd[0] = $hTrayTip_Wnd And _IsPressed(1) Then
		If $aMousePos[0] > ($aWinPos[0]+$aWinPos[2] - 25) And $aMousePos[0] < ($aWinPos[0]+$aWinPos[2] - 10) And $aMousePos[1] < ($aWinPos[1] + 25) And $aMousePos[1] > ($aWinPos[1] + 10) Then
			$iTrayTip_Close = 1
			$iEvent = 1
		Else
			$iTrayTip_Click = 1
			$iEvent = 2
		EndIf
	ElseIf Not $iTrayTip_Close And Not $iTrayTip_Click And Not $iTrayTip_Hide And Not BitAND(WinGetState($hTrayTip_Wnd), 2) Then
		$iTrayTip_Hide = 1
		$iEvent = 3
	EndIf
	
	Switch $iEvent
		Case 1 ;TrayTip close
			ConsoleWrite('TrayTip close' & @LF)
			Exit
		Case 2 ;TrayTip click
			ConsoleWrite('TrayTip click' & @LF)
		Case 3 ;TrayTip hide
			ConsoleWrite('TrayTip hide' & @LF)
	EndSwitch
EndFunc

Func _WinListVisible($sTitle, $sText = '')
	Local $aWinList = WinList($sTitle, $sText)
	Local $aRetList[UBound($aWinList)][2]
	
	For $i = 1 To UBound($aWinList)-1
		If BitAND(WinGetState($aWinList[$i][1]), 2) Then
			$aRetList[0][0] += 1
			$aRetList[$aRetList[0][0]][0] = $aWinList[$i][0]
			$aRetList[$aRetList[0][0]][1] = $aWinList[$i][1]
		EndIf
	Next
	
	ReDim $aRetList[$aRetList[0][0]+1][2]
	Return $aRetList
EndFunc
 

CreatoR

Must AutoIt!
Команда форума
Администратор
Сообщения
8,671
Репутация
2,481
Нашёл решение на оф. форуме, с использованием subclass (немного изменил для упрощения):

Код:
;Author: rover 07/04/09
;MSDN reference:
;Shell_NotifyIcon Function
;http://msdn.microsoft.com/en-us/library/bb762159(VS.85).aspx

#include <Constants.au3>
#include <WindowsConstants.au3>
#include <WinAPI.au3>

Global $hProcNew = 0, $hProcOld = 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

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

;get handle to AutoIt v3 hidden gui
$hAutoIt = WinGetHandle(AutoItWinGetTitle())
_SubclassWin($hAutoIt, $hProcNew, $hProcOld)

While 1
    Sleep(100)
WEnd

;NOTE: _WinAPI_SetWindowLong() in WinAPI.au3 consistently returns 'The specified procedure could not be found' error 127
;error is due to the call being SetWindowLong instead of SetWindowLongW,
;using SetWindowLongW the error message is 'The operation completed successfully.
Func _SubclassWin($hWnd, ByRef $hProcNew, ByRef $hProcOld)
    If $hProcNew <> 0 Or $hProcOld <> 0 Then Return SetError(1, 0, 0)
	
    $hProcNew = DllCallbackRegister("WM_TRAYNOTIFY", "int", "hwnd;uint;wparam;lparam")
    If @error Or $hProcNew = 0 Then Return SetError(2, 0, 0)
    
	$hProcOld = DllCall("User32.dll", "int", "SetWindowLongW", "hwnd", $hWnd, "int", $GWL_WNDPROC, "ptr", DllCallbackGetPtr($hProcNew))
    
	If @error Or $hProcOld[0] = 0 Then
        $hProcOld = 0
        Return SetError(3, 0, 0)
    EndIf
	
    $hProcOld = $hProcOld[0]
    Return SetError(0, 0, 1)
EndFunc

Func _RestoreWndProc($hWnd, ByRef $hProcNew, ByRef $hProcOld)
    If $hProcOld <> 0 Then _WinAPI_SetWindowLong($hWnd, $GWL_WNDPROC, $hProcOld)
    If $hProcNew <> 0 Then DllCallbackFree($hProcNew)
	
    $hProcNew = 0
    $hProcOld = 0
EndFunc

Func WM_TRAYNOTIFY($hWnd, $iMsg, $wParam, $lParam)
    Switch $iMsg
        Case $WM_USER + 1 ;AutoIt callback message value for tray icon (1025), can be retrieved with ReadProcessMemory and TRAYDATA struct
             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/timeout.' & @CR)
					Exit
            EndSwitch
	EndSwitch
	
    ; pass the unhandled messages to default WindowProc
    Return _WinAPI_CallWindowProc($hProcOld, $hWnd, $iMsg, $wParam, $lParam)
EndFunc

Func OnAutoItExit()
    _RestoreWndProc($hAutoIt, $hProcNew, $hProcOld)
EndFunc
 
Автор
Yashied

Yashied

Модератор
Команда форума
Глобальный модератор
Сообщения
5,379
Репутация
2,724
CreatoR, ты думаешь я не пробывал DllCallbackRegister()?

:smile:

Сабклассинг окна AutoIt делает скрипт практически неработоспособным, что и наблюдается в твоем примере. Попробуй выйти из программы с первого раза... Если добавить GUI, то количество глюков только увеличится, вплоть до полного зависания скрипта. Это реакция на DllCallbackRegister(), она очень медленная, т.к. не является .dll в полном смысле этого слова. Подобные глюки можно наблюдать, если делать сабклассинг ListView, TreeView и др. элементов GUI, в зависимости от сложности функции обработки.



Добавлено:
Сообщение автоматически объединено:

По поводу первого примера, я думаю, что ты и сам понимаешь, что это "неправильный" подход.


Добавлено:
Сообщение автоматически объединено:

Можно обойтись без .dll, если функцию сабклассинга написать в машинных кодах.
 

musicstashall

Знающий
Сообщения
322
Репутация
7
Как сделать то же самое, но без иконки в трее?? Если ставить #NoTrayIcon, то функция перестает выполняться.


Добавлено:
Сообщение автоматически объединено:

И еще, скрипт, запущенный в x64 битном исполнении, не работает, автовыход:
AutoIt3.exe ended.rc:-1073741819
 
Верх