Position eines TrayIcons auslesen (_Shell_NotifyIconGetRect)

  • Nachdem UEZ in der SB gefragt hat wie man die Position des TrayItems in der Taskleiste herausfinden kann, habe ich Google befragt und bin auf eine von 2 Möglichkeiten gestoßen wie sich das umsetzen lässt.
    Eine davon wurde bereits für AutoIt umgesetzt. Hierbei wird auf den Speicherbereich der Toolbar zugegriffen, welche die Icons enthält, um über eine Fensternachricht an die Position zu kommen.

    Ich habe eine weitere Möglichkeit gefunden und (scheinbar) als erster in AutoIt umgesetzt weil ich nicht wusste, dass es die andere Möglichkeit bereits für AutoIt gibt :D.
    Man kann die Position eines selbsterstellten TrayIcons auch über die, eigens dafür bereitgestellte, Funktion Shell_NotifyIconGetRect aus der Shell32.dll auslesen.

    Das ganze funktioniert allerdings im Moment nur mit einigen Einschränkungen, weswegen man wohl trotzdem auf Möglichkeit #1 zurückgreifen sollte:
    - Es funktioniert nur unter Windows 7. In Vista existiert die Funktion noch nicht.
    - Da mir kein guter Weg einfällt um an die GUID oder das Fensterhandle + ID des existierenden Standard TrayIcons heranzukommen, funktioniert das im Moment nur mit selbst registrierten TrayIcons.

    Um TrayIcons selbst zu erstellen (in beliebiger Anzahl und mit vielen Einstellungsmöglichkeiten) gibt es bereits eine UDF. Wenn man diese Methode verwendet lässt sich sogar eine eigene Windows Message ID für jedes TrayIcon registrieren. Über diese eigene WM kann man dann mit GUIRegisterMsg MessageHandler Funktionen registrieren und alle Events für das TrayIcon abfangen und selbst verwalten.

    Hier ist ein erstes Beispielscript um Shell_NotifyIconGetRect zu verwenden (wenn ich Zeit habe mache ich evtl. eine ordentliche Funktion/Mini-UDF draus):

    Spoiler anzeigen
    [autoit]

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

    [/autoit] [autoit][/autoit] [autoit]

    ; -Author: name22 (http://www.autoit.de)

    [/autoit] [autoit][/autoit] [autoit]

    ;Auszug aus WinAPIEx.au3:
    Global Const $NIM_ADD = 0x00
    Global Const $NIM_MODIFY = 0x01
    Global Const $NIM_DELETE = 0x02
    Global Const $NIM_SETFOCUS = 0x03
    Global Const $NIM_SETVERSION = 0x04

    [/autoit] [autoit][/autoit] [autoit]

    Global Const $NIF_MESSAGE = 0x01
    Global Const $NIF_ICON = 0x02
    Global Const $NIF_TIP = 0x04
    Global Const $NIF_STATE = 0x08
    Global Const $NIF_INFO = 0x10
    Global Const $NIF_GUID = 0x20
    Global Const $NIF_REALTIME = 0x40
    Global Const $NIF_SHOWTIP = 0x80

    [/autoit] [autoit][/autoit] [autoit]

    Global Const $NIS_HIDDEN = 0x01
    Global Const $NIS_SHAREDICON = 0x02

    [/autoit] [autoit][/autoit] [autoit]

    Global Const $NIIF_NONE = 0x00
    Global Const $NIIF_INFO = 0x01
    Global Const $NIIF_WARNING = 0x02
    Global Const $NIIF_ERROR = 0x03
    Global Const $NIIF_USER = 0x04
    Global Const $NIIF_NOSOUND = 0x10
    Global Const $NIIF_LARGE_ICON = 0x10
    Global Const $NIIF_RESPECT_QUIET_TIME = 0x80
    Global Const $NIIF_ICON_MASK = 0x0F

    [/autoit] [autoit][/autoit] [autoit]

    Global Const $tagNOTIFYICONDATA = 'dword Size;hwnd hWnd;uint ID;uint Flags;uint CallbackMessage;ptr hIcon;wchar Tip[128];dword State;dword StateMask;wchar Info[256];uint Version;wchar InfoTitle[64];dword InfoFlags;' ;aus WinAPIEx.au3
    Global Const $tagNOTIFYICONIDENTIFIER = "DWORD cbSize;HWND hWnd;UINT uID;BYTE guidItem[16]" ;Struktur um ein TrayIcon eindeutig zu identifizieren (http://msdn.microsoft.com/en-us/library/…v=vs.85%29.aspx)
    Global Const $WM_NOTIFYICONCALLBACK = _WinAPI_RegisterWindowMessage("WM_NOTIFYICONCALLBACK") ;Registriert eine WM mit dem angegebenen String die garantiert noch frei ist.

    [/autoit] [autoit][/autoit] [autoit]

    Global $hIcon, $hGUI, $cLabel_Pos, $tIconData, $tIconData_Delete, $tIconIdentifier
    Global $iNotifyIconID = 1 ;Beliebige ID für das neue TrayIcon (darf noch nicht in Benutzung sein)

    [/autoit] [autoit][/autoit] [autoit]

    GUIRegisterMsg($WM_NOTIFYICONCALLBACK, "_IconCallback") ;Registriert den MessageHandler über unsere eigene WM

    [/autoit] [autoit][/autoit] [autoit]

    $hIcon = _WinAPI_ExtractIcon(@AutoItExe, 0, True) ;Lädt das AutoIt Icon aus der AutoIt3.exe

    [/autoit] [autoit][/autoit] [autoit]

    $hGUI = GUICreate("IconParent", 200, 100) ;Ein Fenster muss vorhanden sein um TrayIcon zu registrieren.
    $cLabel_Pos = GUICtrlCreateLabel("", 5, 5, 190, 25)
    GUISetState()

    [/autoit] [autoit][/autoit] [autoit]

    $tIconData = DllStructCreate($tagNOTIFYICONDATA) ;Enthält alle Daten um TrayIcons zu erstellen/modifizieren/löschen (http://msdn.microsoft.com/en-us/library/…v=vs.85%29.aspx)
    DllStructSetData($tIconData, "Size", DllStructGetSize($tIconData))
    DllStructSetData($tIconData, "hWnd", $hGUI)
    DllStructSetData($tIconData, "ID", $iNotifyIconID)
    DllStructSetData($tIconData, "Flags", BitOR($NIF_ICON, $NIF_MESSAGE)) ;Flags um Icon zu setzen und eine CallbackMessage zu registrieren
    DllStructSetData($tIconData, "CallbackMessage", $WM_NOTIFYICONCALLBACK)
    DllStructSetData($tIconData, "hIcon", $hIcon) ;Iconhandle für das Icon des TrayItems

    [/autoit] [autoit][/autoit] [autoit]

    $tIconIdentifier = DllStructCreate($tagNOTIFYICONIDENTIFIER)
    DllStructSetData($tIconIdentifier, "cbSize", DllStructGetSize($tIconIdentifier))
    DllStructSetData($tIconIdentifier, "hWnd", $hGUI)
    DllStructSetData($tIconIdentifier, "uID", $iNotifyIconID)
    DllStructSetData($tIconIdentifier, "guidItem", 0)

    [/autoit] [autoit][/autoit] [autoit]

    _WinAPI_ShellNotifyIcon($NIM_ADD, $tIconData) ;Erstellt unser TrayIcon

    [/autoit] [autoit][/autoit] [autoit]

    AdlibRegister("_GetPosition", 500)

    [/autoit] [autoit][/autoit] [autoit]

    While True
    Switch GUIGetMsg()
    Case $GUI_EVENT_CLOSE
    ;Struktur mit Daten um TrayIcon zu löschen
    $tIconData_Delete = DllStructCreate($tagNOTIFYICONDATA)
    DllStructSetData($tIconData_Delete, "Size", DllStructGetSize($tIconData))
    DllStructSetData($tIconData, "hWnd", $hGUI)
    DllStructSetData($tIconData, "ID", $iNotifyIconID)

    [/autoit] [autoit][/autoit] [autoit]

    _WinAPI_ShellNotifyIcon($NIM_DELETE, $tIconData_Delete) ;Löscht unser TrayIcon
    _WinAPI_DestroyIcon($hIcon) ;Icon aus RAM entfernen
    Exit
    EndSwitch
    WEnd

    [/autoit] [autoit][/autoit] [autoit]

    Func _IconCallback($hWnd, $iMsg, $wParam, $lParam) ;Callback Funktion
    ConsoleWrite($iMsg & @TAB & $lParam & @CRLF)
    EndFunc

    [/autoit] [autoit][/autoit] [autoit]

    Func _GetPosition()
    $tRect = DllStructCreate($tagRECT) ;Rect Struktur die Position erhalten wird
    _Shell_NotifyIconGetRect(DllStructGetPtr($tIconIdentifier), DllStructGetPtr($tRect)) ;Identifier identifiziert das TrayIcon, Rect empfängt die Positionsdaten
    GUICtrlSetData($cLabel_Pos, "X: " & DllStructGetData($tRect, "left") & @TAB & @TAB & "Y: " & DllStructGetData($tRect, "top"))
    EndFunc

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    Func _Shell_NotifyIconGetRect($pIdentifier, $pRect)
    ;Liest die Postition eines TrayIcons aus und schreibt sie in eine RECT Struktur (http://msdn.microsoft.com/en-us/library/…v=vs.85%29.aspx)
    ;Rückgabewert: Bei Erfolg = 0, sonst HRESULT Error Code (http://blogs.msdn.com/b/eldar/archiv…sult-codes.aspx)
    ; -Author: name22 (http://www.autoit.de)

    [/autoit] [autoit][/autoit] [autoit]

    $aRet = DllCall("shell32.dll", "LONG", "Shell_NotifyIconGetRect", "PTR", $pIdentifier, "PTR", $pRect)
    Return $aRet[0]
    EndFunc

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    ; #FUNCTION# ====================================================================================================================
    ; Name...........: _WinAPI_ShellNotifyIcon
    ; Description....: Sends a message to the taskbar's status area.
    ; Syntax.........: _WinAPI_ShellNotifyIcon ( $iMessage, $tNOTIFYICONDATA )
    ; Parameters.....: $iMessage - The variable that specifies the action to be taken. It can have one of the following values.
    ;
    ; $NIM_ADD
    ; $NIM_MODIFY
    ; $NIM_DELETE
    ; $NIM_SETFOCUS
    ; $NIM_SETVERSION
    ;
    ; $tNOTIFYICONDATA - $tagNOTIFYICONDATA structure. The content and size of this structure depends on the value
    ; of the $iMessage and version of the operating system.
    ; Return values..: Success - 1.
    ; Failure - 0 and sets the @error flag to non-zero.
    ; Author.........: Yashied
    ; Modified.......:
    ; Remarks........: None
    ; Related........:
    ; Link...........: @@MsdnLink@@ Shell_NotifyIcon
    ; Example........: Yes
    ; ===============================================================================================================================

    [/autoit] [autoit][/autoit] [autoit]

    Func _WinAPI_ShellNotifyIcon($iMessage, $tNOTIFYICONDATA)

    [/autoit] [autoit][/autoit] [autoit]

    Local $Ret = DllCall('shell32.dll', 'int', 'Shell_NotifyIconW', 'dword', $iMessage, 'ptr', DllStructGetPtr($tNOTIFYICONDATA))

    [/autoit] [autoit][/autoit] [autoit]

    If (@error) Or (Not $Ret[0]) Then
    Return SetError(1, 0, 0)
    EndIf
    Return 1
    EndFunc ;==>_WinAPI_ShellNotifyIcon

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    ; #FUNCTION# ====================================================================================================================
    ; Name...........: _WinAPI_ExtractIcon
    ; Description....: Extracts an icon from the specified executable file, DLL, or icon file.
    ; Syntax.........: _WinAPI_ExtractIcon ( $sIcon, $iIndex [, $fSmall] )
    ; Parameters.....: $sIcon - The name of an executable file, DLL, or icon file from which icons will be extracted.
    ; $iIndex - The zero-based index of the icon to extract. If this value is a negative number, the function extracts
    ; the icon whose resource identifier is equal to the absolute value of $iIndex.
    ; $fSmall - Specifies whether extract a small icon, valid values:
    ; |TRUE - Extract a small icon.
    ; |FALSE - Extract a large icon. (Default)
    ; Return values..: Success - Handle to the extracted icon.
    ; Failure - 0 and sets the @error flag to non-zero.
    ; Author.........: Yashied
    ; Modified.......:
    ; Remarks........: When you are finished using the icon, destroy it using the _WinAPI_DestroyIcon() function.
    ; Related........:
    ; Link...........: @@MsdnLink@@ ExtractIconEx
    ; Example........: Yes
    ; ===============================================================================================================================

    [/autoit] [autoit][/autoit] [autoit]

    Func _WinAPI_ExtractIcon($sIcon, $iIndex, $fSmall = 0)

    [/autoit] [autoit][/autoit] [autoit]

    Local $pLarge, $pSmall, $tPtr = DllStructCreate('ptr')

    [/autoit] [autoit][/autoit] [autoit]

    If $fSmall Then
    $pLarge = 0
    $pSmall = DllStructGetPtr($tPtr)
    Else
    $pLarge = DllStructGetPtr($tPtr)
    $pSmall = 0
    Endif

    [/autoit] [autoit][/autoit] [autoit]

    Local $Ret = DllCall('shell32.dll', 'uint', 'ExtractIconExW', 'wstr', $sIcon, 'int', $iIndex, 'ptr', $pLarge, 'ptr', $pSmall, 'uint', 1)

    [/autoit] [autoit][/autoit] [autoit]

    If (@error) Or (Not $Ret[0]) Then
    Return SetError(1, 0, 0)
    EndIf
    Return DllStructGetData($tPtr, 1)
    EndFunc ;==>_WinAPI_ExtractIcon

    [/autoit]


    Ich hoffe das Script ist ausreichend kommentiert. Falls nicht bin ich offen gegenüber Fragen und sonstigen Rückmeldungen ;).

  • Vielen Dank name22! :thumbup:

    Mir geht es darum, den Pseudo TrayTip richtig zu positionieren, d.h. die Sprechblase soll auf das eigene Tray Icon zeigen.

    Ich schaue mir das gleich an, ob's passt!

    Gruß,
    UEZ

    Auch am Arsch geht ein Weg vorbei...

    ¯\_(ツ)_/¯