Wie ermittle ich die Position eines TrayIcons?



  • Hallo,

    ich habe ein Problem mit der Bestimmung der Position eines TTrayIcons in der SysTray.

    Ich würde die Koordinaten des TrayIcons ermitteln, aber irgendwie gibt es keine Funktion oder Eigenschaft in der Klasse TTrayIcon.

    Kennt jemand einen Rat oder hat ne Idee?

    P.S. Also ich will es nicht mit GetCursorPos machen, sondern ohne irgendwelche Maus-Funktionen!



  • Hallo,

    die Top-Position kann man berechnen...

    Screen->Height - ((Screen->Height - Screen->WorkAreaHeight) / 2 - (ImageList1->Height) / 2); 
    /* ImageList1 ist die ImageList, aus der du das Icon für dein TrayIcon holst... */
    

    ...weiter wüsst ich auch nicht. Wofür brauchst du das denn? Und was spricht gegen die Nutzung der Mausposition.

    LG, Micha



  • Da es sich beim Systray um eine Komponente handelt, welche über Button-Elemente verfügt, besteht die Möglichkeit diese Auszulesen.

    Die Windows API bietet auch die Möglichkeit, die Position der Buttons zu Bestimmen und deren Grösse.

    Soviel ich in Erinnderung habe, handelt es sich bei diesen Befehlen um Messages welche mit dem Prefix "LB_" beginnen.

    Ich werden mal nachsehen. Vielleicht kann ich am Abend ein kleinen Beispielcode dazu liefern.



  • Wie versprochen hier der Quelltext einer kurzen Testanwendung.

    Folgendes wird gemacht.
    - Auslesen der sichtbaren Traysymbole
    - Speicherung in einem Map-Container
    - Ermitteln der Positionen in Bezug auf den oberen linken Punkt (0,0) des Monitors

    Getestet mit:
    - C++Builder 2009
    - Windows XP Service Pack 3

    Hier der Hauptcode:
    Main.h

    //---------------------------------------------------------------------------
    
    #ifndef mainH
    #define mainH
    //---------------------------------------------------------------------------
    #include <Classes.hpp>
    #include <Controls.hpp>
    #include <StdCtrls.hpp>
    #include <Forms.hpp>
    #include <ComCtrls.hpp>
    //---------------------------------------------------------------------------
    #include <map>
    #include "needStructs.h"
    //---------------------------------------------------------------------------
    
    class TF_Hauptformular : public TForm
    {
    __published:	// IDE-verwaltete Komponenten
    	TButton *BTN_Tray_Generate_List;
    	void __fastcall BTN_Tray_Generate_ListClick(TObject *Sender);
    private:	// Benutzer Deklarationen
    	void __fastcall vf_Generate_TrayList(HWND hwndTrayNotifyWND,std::map<int,structTrayItemInfo> *mapTrayItems);
    	void __fastcall vf_Generate_TrayPositions(HWND hwndTrayNotifyWND,std::map<int,structTrayItemInfo> *mapTrayItems);
    	HWND __fastcall Get_Tray_Handle();
    public:		// Benutzer Deklarationen
    	__fastcall TF_Hauptformular(TComponent* Owner);
    };
    //---------------------------------------------------------------------------
    extern PACKAGE TF_Hauptformular *F_Hauptformular;
    //---------------------------------------------------------------------------
    #endif
    

    Main.cpp

    //---------------------------------------------------------------------------
    
    #include <vcl.h>
    #pragma hdrstop
    
    #include "main.h"
    //---------------------------------------------------------------------------
    #pragma package(smart_init)
    #pragma resource "*.dfm"
    TF_Hauptformular *F_Hauptformular;
    //---------------------------------------------------------------------------
    __fastcall TF_Hauptformular::TF_Hauptformular(TComponent* Owner)
    	: TForm(Owner)
    {
    }
    //---------------------------------------------------------------------------
    
    void __fastcall TF_Hauptformular::BTN_Tray_Generate_ListClick(TObject *Sender)
    {
    	std::map<int,structTrayItemInfo> mapTrayItems;
    	std::map<int,structTrayItemInfo>::iterator mapItTrayItems;
    	HWND hwndTray;
    
    	mapTrayItems.clear();
    
    	hwndTray=Get_Tray_Handle();
    	vf_Generate_TrayList(hwndTray,&mapTrayItems);
    	vf_Generate_TrayPositions(hwndTray,&mapTrayItems);
    }
    //---------------------------------------------------------------------------
    
    void __fastcall TF_Hauptformular::vf_Generate_TrayList(HWND hwndTrayNotifyWND,std::map<int,structTrayItemInfo> *mapTrayItems)
    {
    	//Struct welches die Daten aufnimmt
    	structTrayItemInfo stiiTrayInformations;
    	int iAnzahlItems;
    
    	//Definition der Variabeln zur Auslesung der Informationen
    	TBBUTTONINFOA tbBtnInfo, *_tbBtnInfo; 	//Struktur zur Auslesung der Button Informationen
    	char cButtonText[256],*_cButtonText;  	//Speichert den Text des Buttons
    
    	//Definition der Processvariabeln
    	NMHDR nmMessageStructure;
    	HANDLE hwProcess;
    	unsigned long ulProcessID;
    
    	//Auslesen der Anzahl Elemente
    	iAnzahlItems=SendMessage(hwndTrayNotifyWND,TB_BUTTONCOUNT,0,0);
    
    	//Auslesen der ProcessorID des Fensters (Speicherung in ulProcessID)
    	//Starten eines neuen Prozesses mit lese und schreibrechten
    	//Das Ganze in einer Informationsabfrage
    	GetWindowThreadProcessId(hwndTrayNotifyWND, &ulProcessID);
    	hwProcess=OpenProcess(PROCESS_VM_OPERATION|PROCESS_VM_READ|
    					 PROCESS_VM_WRITE|PROCESS_QUERY_INFORMATION, FALSE, ulProcessID);
    
    	//Freigabe des Speichers (Buffer) für das Element und den Text
    	_tbBtnInfo=(TBBUTTONINFOA*)VirtualAllocEx(hwProcess, NULL, sizeof(TBBUTTONINFOA),
    							  MEM_COMMIT, PAGE_READWRITE);
    	_cButtonText=(char*)VirtualAllocEx(hwProcess, NULL, 256, MEM_COMMIT,
    							 PAGE_READWRITE);
    
    	for (int i = 0; i < iAnzahlItems; i++) {
    		//dwMask (TBIF_COMMAND) -> Ermitteln mittels eindeutigen Identifikator
    		//dwMask (TBIF_TEXT) -> Auslesen des Textes
    		//dwMask (TBIF_STYLE) -> Auslesen des Styles, damit Gruppierbuttons nicht beachtet werden
    		//(pszText) Angabe der Textgrösse; (pszText) Pointer zur Variabel welche den Text aufnimmt
    		//(cbSize) Übergabe der Grösse der Struktur, damit die Informationen abgeholt werden können
    		tbBtnInfo.dwMask=TBIF_COMMAND | TBIF_BYINDEX | TBIF_STATE | TBIF_TEXT;
    		tbBtnInfo.cchText=256;
    		tbBtnInfo.pszText=_cButtonText;
    		tbBtnInfo.cbSize=sizeof(TBBUTTONINFOA);
    
    		//Freigabe des Buffers, damit die Informationen reingeschrieben werden können
    		WriteProcessMemory(hwProcess, _tbBtnInfo, &tbBtnInfo, sizeof(TBBUTTONINFOA), NULL);
    		//Senden der Informationen, welche gelesen werden sollen
    		SendMessage(hwndTrayNotifyWND, TB_GETBUTTONINFO, (WPARAM)i, (LPARAM)_tbBtnInfo);
    		//Auslesen der Angaben des Textes und der anderen Informationen
    		ReadProcessMemory(hwProcess, _tbBtnInfo, &tbBtnInfo, sizeof(TBBUTTONINFOA), NULL);
    		ReadProcessMemory(hwProcess, _cButtonText, cButtonText, 256, NULL);
    
    		//Der Button wird nur hinzugefügt, wenn dieser auch sichtbar ist
    		if (static_cast<BOOL>(tbBtnInfo.fsStyle & TBSTATE_HIDDEN)==false) {
    			//Hinzufügen des Eintrags zur Map
    			stiiTrayInformations.~structTrayItemInfo();
    			stiiTrayInformations.iID=tbBtnInfo.idCommand;
    			stiiTrayInformations.sCaption=static_cast<UnicodeString>(cButtonText);
    
    			mapTrayItems->insert(std::pair<int,structTrayItemInfo>(stiiTrayInformations.iID,stiiTrayInformations));
    		}
    	}
    
    	//Freigeben des Reservierten Speichers
    	VirtualFreeEx(hwProcess, _tbBtnInfo, 0, MEM_RELEASE);
    	VirtualFreeEx(hwProcess, _cButtonText, 0, MEM_RELEASE);
    }
    //---------------------------------------------------------------------------
    
    void __fastcall TF_Hauptformular::vf_Generate_TrayPositions(HWND hwndTrayNotifyWND,std::map<int,structTrayItemInfo> *mapTrayItems)
    {
    	std::map<int,structTrayItemInfo>::iterator mapItTrayItems;
    	POINT ptPosition;
    
    	unsigned long pid;
    	HANDLE process;
    	char cTaskText[512];
    	char *_cTaskText;
    
    	NMHDR MessageStructure;
    
    	RECT *_Rechteck,Rechteck;
    
    	GetWindowThreadProcessId(hwndTrayNotifyWND, &pid);
    	process=OpenProcess(PROCESS_VM_OPERATION|PROCESS_VM_READ|
    					 PROCESS_VM_WRITE|PROCESS_QUERY_INFORMATION, FALSE, pid);
    
    	_Rechteck=(RECT*)VirtualAllocEx(process, NULL, 512, MEM_COMMIT,
    							 PAGE_READWRITE);
    
    	mapItTrayItems=mapTrayItems->begin();
    	while (mapItTrayItems!=mapTrayItems->end()){
    		WriteProcessMemory(process, _Rechteck, &Rechteck, sizeof(RECT), NULL);
    		SendMessage(hwndTrayNotifyWND, TB_GETITEMRECT, (WPARAM)mapItTrayItems->first, (LPARAM)_Rechteck);
    		ReadProcessMemory(process, _Rechteck, &Rechteck, sizeof(RECT), NULL);
    
    		//Konvertieren der Position
    		ptPosition.x=Rechteck.left;
    		ptPosition.y=Rechteck.top;
    		::ClientToScreen(hwndTrayNotifyWND,&ptPosition);
    		mapItTrayItems->second.iXPos=ptPosition.x;
    		mapItTrayItems->second.iYPos=ptPosition.y;
    		mapItTrayItems->second.iWidth=Rechteck.right-Rechteck.left;
    		mapItTrayItems->second.iHeight=Rechteck.bottom-Rechteck.top;
    
    		++mapItTrayItems;
    	}
    
    	VirtualFreeEx(process, _Rechteck, 0, MEM_RELEASE);
    }
    //---------------------------------------------------------------------------
    
    HWND __fastcall TF_Hauptformular::Get_Tray_Handle()
    {
    	HWND hwndReturnValue;
    
    	//Als erstel wir die komplette Taskleiste ermittels
    	hwndReturnValue=FindWindow("Shell_TrayWnd", NULL);
    	//Anschliessend wird der 2 Bereich gesucht, welcher die Traysymbole beinhaltet
    	hwndReturnValue=FindWindowEx(hwndReturnValue,NULL,"TrayNotifyWnd",NULL);
    	//Nun kann die Leiste mit den Traysymbolen ermittelt werden
    	hwndReturnValue=FindWindowEx(hwndReturnValue,NULL,"Syspager",NULL);
    	//Und nochmals ein unterelement
    	hwndReturnValue=FindWindowEx(hwndReturnValue,NULL,NULL,"Infobereich");
    
    	return hwndReturnValue;
    }
    //---------------------------------------------------------------------------
    

    Und hier noch das Struct in einer separaten Datei welches ich für den Map-Container verwendet habe.
    neesStructs.h

    //---------------------------------------------------------------------------
    
    #include <vcl.h>
    
    #ifndef needStructsH
    #define needStructsH
    //---------------------------------------------------------------------------
    
    struct structTrayItemInfo{
    	int iID;
    	UnicodeString sCaption;
    
    	int iXPos;
    	int iYPos;
    	int iWidth;
    	int iHeight;
    
    	structTrayItemInfo();
    };
    
    //---------------------------------------------------------------------------
    #endif
    

    needStructs.cpp

    //---------------------------------------------------------------------------
    #pragma hdrstop
    #include "needStructs.h"
    //---------------------------------------------------------------------------
    
    #pragma package(smart_init)
    
    structTrayItemInfo::structTrayItemInfo(){
    	iID=-1;
    	sCaption=EmptyStr;
    
    	iXPos=-1;
    	iYPos=-1;
    	iWidth=-1;
    	iHeight=-1;
    };
    

    Ich hoffe, dir mit diesem Beispiel geholfen zu haben.
    Falls du mit Position die Position des Items in der Liste meinst (nicht koordinaten). So kannst du "i" in der for-Schleife für die Position verwenden. (Prozedur [vf_Generate_TrayList]). Denn das Tray wird von rechts nach links ausgelesen.

    Für die eindeutige Identifikation des Buttons dient die ID (kann einen anderen Wert haben als die Position).

    Unter "Link war ungültig. Der neue steht in meinem nächsten Beitrag" könntest du sonst auch das Beispielprojekt herunterladen.



  • Wow! Herzlichen Dank! Es ist genau das, was ich gesucht hatte...

    Ich werde es mal ausprobieren!

    👍



  • Ich habe noch kurz einen kleinen Fehler korrigiert, welcher aufgetreten wäre, wenn eines der Icons geschlossen worden wäre, denn dann hätten nicht mehr alle ausgelesen werden können.

    Die korrigierte Zeile ist in der Main.cpp
    -> Eigenschaft hinzugefügt (TBIF_BYINDEX)

    tbBtnInfo.dwMask=TBIF_COMMAND | TBIF_BYINDEX | TBIF_STATE | TBIF_TEXT;
    

    Anmerkung.
    Für Symbole die ausgeblendet wurden, sollte bei der Position und grösse -1 oder sonst ein ungültiger wert erscheinen. (das hab ich jetzt nicht abgefangen).

    ps: der download wurde auch nochmals angepass.

    !!! Edit:
    Der Link heisst nun http://rrworldfiles.foroomy.com/download/file.php?id=6


Anmelden zum Antworten