Shell_NotifyIcon Callback
-
Mein Problem besteht darin, dass ich nicht weis wie ich herausfinden soll, ob gerade auf mein TrayIcon geklickt wurde.
Hier mal mein Code:
//zusätzlich definiert #include <shellapi.h> #define WM_MYMESSAGE (WM_USER + 1) NOTIFYICONDATA nid; HWND hwnd; //--------------------------------------------------------------------------- void __fastcall TForm1::FormCreate(TObject *Sender) { hwnd = FindWindow(0, L"Form1"); nid.cbSize = sizeof(NOTIFYICONDATA); nid.hWnd = hwnd; nid.uID = 100; nid.uVersion = NOTIFYICON_VERSION; nid.uCallbackMessage = WM_MYMESSAGE; nid.hIcon = LoadIcon(NULL, IDI_QUESTION); wcscpy_s(nid.szTip, 256, L"Tray Icon"); nid.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP; Shell_NotifyIconW(NIM_ADD, &nid); }
In DevCpp konnte ich die Nachricht ganz einfach abfangen:
LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) /* handle the messages */ { case WM_DESTROY: PostQuitMessage (0); /* send a WM_QUIT to the message queue */ break; case WM_MYMESSAGE: if (lParam == WM_LBUTTONDBLCLK) MessageBoxA(NULL, "Doppelklick!", "Nachricht", 0); break; default: /* for messages that we don't deal with */ return DefWindowProc (hwnd, message, wParam, lParam); } return 0; }
Aber wie bekommt man das mit Embarcadero hin?
-
Nimm doch einfach TTrayIcon.
-
Das würde ich ja gerne, aber ich arbeite mit Embarcadero RAD Studio XE2 Architect und Firemonkey, da gibt es das TTrayIcon leider nicht mehr.
Edit:
Ich habe hier jetzt tatsächlich etwas gefunden, allerdings in Delphi:
http://delphi.about.com/library/code/ncaa121801a.htmMit der Portierung des Codes in C++ habe ich eigentlich keine Probleme, aber soetwas kenne ich nicht und weis auch nicht, wie man es in C++ umsetzen kann:
procedure TrayMessage(var Msg: TMessage); message WM_ICONTRAY;
Bei
void TrayMessage(TMessage m); message WM_TRAYMESSAGE;
kennt er offensichtlich das "message" nicht.
-
SchweineKoenig schrieb:
Das würde ich ja gerne, aber ich arbeite mit Embarcadero RAD Studio XE2 Architect und Firemonkey, da gibt es das TTrayIcon leider nicht mehr.
Du hast ja trotzdem den Quelltext von TTrayIcon, so daß du dir daraus ein TFMTrayIcon herstellen könntest. Natürlich wäre es an dir, für eine Mac-Version zu sorgen, wenn du das überhaupt brauchst.
-
Nein, den Quelltext habe ich nicht und es liegt ja möglicherweise nur noch an dieser einen Zeile.
-
SchweineKoenig schrieb:
Nein, den Quelltext habe ich nicht
Freilich hast du; schau mal nach TTrayIcon in $(BDS)\source\vcl\Vcl.ExtCtrls.pas .
Ich will dich natürlich nicht hindern, den Weg des geringsten Widerstandes zu gehen. Wenn du das Tray-Icon lieber von Hand in deine Form-Datei einbauen willst, dann bitte: hier steht, wie die explizite Nachrichtenbehandlung in C++Builder funktioniert. Ich will dich darauf hingewiesen haben, daß es im Interesse der gegenwärtigen und zukünftigen Entwickler eures Projektes wäre, wenn du den Tray-Icon-Code in eine Komponente auslagerst, so daß er wiederverwendbar bleibt und nicht auch noch zur allgemeinen Unübersichtlichkeit der Formular-Quelldateien beiträgt. Dummerweise mußt du dich dann einen Moment lang mit der Portierung von TTrayIcon auseinandersetzen - das wäre zwar lehrreich und nützlich, aber eben nicht der direkteste Weg zum Ziel.
Edit: Grammatik
-
Wow, danke. Da ist tatsächlich der Source
Ich habe leider keinerlei Ahnung, wie ich das in ein extra Komponente auslagern kann, aber ich werde mich mal schlau machen.Das mit dem MessageHanlder habe ich auch schon versucht, was unter VCL auch 1a funktioniert, nur bei der Verwendung der FMX erhält mein Fenster keine Message vom Icon.
Hier mal mein Code, vielleicht mach ich ja irgendetwas falsch:
Unit1.h//--------------------------------------------------------------------------- #ifndef Unit1H #define Unit1H //--------------------------------------------------------------------------- #include <System.Classes.hpp> #include <FMX.Controls.hpp> #include <Vcl.Controls.hpp> #include <FMX.Forms.hpp> #include <FMX.Types.hpp> #include <FMX.Menus.hpp> //--------------------------------------------------------------------------- #define WM_TRAYNOTIFY (WM_USER + 1001) class TForm1 : public TForm { __published: // Von der IDE verwaltete Komponenten Fmx::Menus::TPopupMenu *PopupMenu1; Fmx::Menus::TMenuItem *MenuItem1; void __fastcall FormClose(TObject *Sender, TCloseAction &Action); void __fastcall FormCreate(TObject *Sender); private: // Benutzer-Deklarationen void __fastcall WMTrayNotify(TMessage &Msg); public: // Benutzer-Deklarationen __fastcall TForm1(TComponent* Owner); BEGIN_MESSAGE_MAP MESSAGE_HANDLER(WM_TRAYNOTIFY, TMessage, WMTrayNotify) END_MESSAGE_MAP(TForm1) }; //--------------------------------------------------------------------------- extern PACKAGE TForm1 *Form1; //--------------------------------------------------------------------------- #endif
Unit1.cpp
//--------------------------------------------------------------------------- #include <fmx.h> #include <shellapi.h> #pragma hdrstop const int IDC_TRAY1 = 1005; const char *HINT_MESSAGE = "Tray Hint Message"; NOTIFYICONDATA nid; HWND hwnd; #include <shellapi.h> #include "Unit1.h" //--------------------------------------------------------------------------- #pragma package(smart_init) #pragma resource "*.fmx" TForm1 *Form1; //--------------------------------------------------------------------------- void __fastcall TForm1::WMTrayNotify(TMessage &Msg) { switch(Msg.LParam) { case WM_RBUTTONUP: POINT WinPoint; GetCursorPos(&WinPoint); SetForegroundWindow(hwnd); PopupMenu1->Popup(WinPoint.x, WinPoint.y); PostMessage(hwnd, WM_NULL, 0,0); break; case WM_LBUTTONDBLCLK: Visible = true; ShowWindow(hwnd, SW_SHOW); break; } } //--------------------------------------------------------------------------- __fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) { } //--------------------------------------------------------------------------- void __fastcall TForm1::FormCreate(TObject *Sender) { hwnd = FindWindow(0, L"Form1"); nid.cbSize = sizeof(NOTIFYICONDATA); nid.hWnd = hwnd; nid.uID = 100; nid.uVersion = NOTIFYICON_VERSION; nid.uCallbackMessage = WM_TRAYNOTIFY; nid.hIcon = LoadIcon(NULL, IDI_QUESTION); wcscpy_s(nid.szTip, 256, L"Tray Icon"); nid.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP; Shell_NotifyIconW(NIM_ADD, &nid); } //--------------------------------------------------------------------------- void __fastcall TForm1::FormClose(TObject *Sender, TCloseAction &Action) { Shell_NotifyIconW(NIM_DELETE, &nid); } //---------------------------------------------------------------------------
-
SchweineKoenig schrieb:
Das mit dem MessageHanlder habe ich auch schon versucht, was unter VCL auch 1a funktioniert, nur bei der Verwendung der FMX erhält mein Fenster keine Message vom Icon.
Richtig, bei FMX werden aus Gründen der Plattformunabhängigkeit keine Window-Messages herumgeschickt - da hatte ich nicht dran gedacht. Du müßtest das FMX-Fensterhandle subclassen, um Windows-Nachrichten empfangen zu können.
SchweineKoenig schrieb:
Wow, danke. Da ist tatsächlich der Source
Ich habe leider keinerlei Ahnung, wie ich das in ein extra Komponente auslagern kann- Als Warnung vorab: fange nicht an, TTrayIcon nach C++ zu übersetzen. Das funktioniert auch in Delphi ganz prima, alles andere wäre schiere Zeitverschwendung.
- Zunächst nimmst du aus dem VCL-Quelltext die Definitionen von WM_SYSTEM_TRAY_MESSAGE, TBalloonFlags, TCustomTrayIcon und TTrayIcon und fügst sie in eine Delphi-Quelldatei in einem neuen Package ein. Dort fügst du die nötigen Unit-Verweise ein (Winapi.Windows, Winapi.Messages, Winapi.ShellAPI, System.Classes, Fmx.Menus und Fmx.Types in "interface", System.SysUtils, System.UITypes und Fmx.Forms in "implementation") und schaust mal, ob man es kompilieren kann.
- Natürlich klappt das Kompilieren nicht auf Anhieb. Du wirst feststellen, daß es in FMX kein TIcon und keine Imagelists gibt. Als ersten Ansatz kommentierst du alles aus, was mit "Icon", "ImageList", "Timer" oder "Animate" zu tun hat.
- Möglicherweise mußt du einige Enum-Werte aus TMouseButton explizit qualifizieren (TMouseButton.mbLeft anstatt mbLeft).
- Statt der Codezeile
SetForegroundWindow(Application.Handle);
schreibst du
SetForegroundWindow (FmxHandleToHWND (Application.MainForm.Handle));
- TPopupMenu hat in FireMonkey kein AutoPopup-Property; die entsprechende Zeile kommentierst du auch aus.
- Dann kommt der kreative Teil: irgendwie mußt du ja Icons in die Komponente hineinbekommen. (Das Animieren lassen wir mal beiseite.) Du findest also heraus, wie man in FireMonkey aus einem TBitmap ein HICON macht. Außerdem erstellst du eine neue Eigenschaft "FWinIcon: HICON" und liest in der MSDN nach, wie du das Handle im Destruktor freigeben mußt.
- Dann fügst du ein neues "Bitmap: TBitmap"-Property und ein "FBitmap: TBitmap"-Feld (inklusive Allokation und Freigabe in Konstruktor und Destruktor) in TCustomTrayIcon hinzu, veröffentlichst es genau wie die anderen Properties in TTrayIcon - und dann suchst du dir alle auskommentierten Stellen, an denen FData.hIcon zugewiesen wurde, und an deren Stelle schließt du das aktuelle FWinIcon-Handle, erstellst dann ein neues Icon aus FBitmap und weist es schließlich zu mit der Anweisung
FData.hIcon := FWinIcon;
.
-
Erstmal vielen Dank für diese Anleitung, aber ich glaube nicht, dass das was bringt.
Fast in jeder Zeile bzw. Abfrage sind rote Zeilen drin, wenn ich die alle auskommentiere ist kaum noch etwas da.Ich glaube auch, dass wenn es so einfach gehen würde, Embarcadero es selbst gemacht hätte.
Mittlerweile ist das ganze ziemlich deprimierend, wenn ich das mit dem TrayIcon nicht hinbekomme, hat es auch keinen Zweck mit dem Programm weiter zu machen.
-
SchweineKoenig schrieb:
Erstmal vielen Dank für diese Anleitung, aber ich glaube nicht, dass das was bringt.
Es ist ja nicht so, daß ich eine derart detaillierte Anleitung ins Blaue hinein geben würde. Ich habe natürlich getestet, was ich da vorschlug. Die letzten beiden Punkte habe ich ausgelassen, da ich keine Lust habe, mich jetzt in das TBitmap->HICON-Problem einzulesen, aber den Rest habe ich genauso gemacht, und ich bekomme keine Compilerfehler.
SchweineKoenig schrieb:
Fast in jeder Zeile bzw. Abfrage sind rote Zeilen drin, wenn ich die alle auskommentiere ist kaum noch etwas da.
"Rote Zeilen" sind nett, aber manchmal irreführend. Es zählen nur Compilerfehler. Außerdem sollst du nicht alle "roten Zeilen" auskommentieren, sondern nur die, auf die meine in diesem Punkt sehr genaue Beschreibung zutrifft.
SchweineKoenig schrieb:
Ich glaube auch, dass wenn es so einfach gehen würde, Embarcadero es selbst gemacht hätte.
Ich glaube nicht, sondern weiß, daß FireMonkey unter erheblichem Zeitdruck entstanden ist und es andere, weit dringendere Dinge gab, die man noch nicht implementiert hat, z.B. Action-Unterstützung oder Anchors.
SchweineKoenig schrieb:
Mittlerweile ist das ganze ziemlich deprimierend, wenn ich das mit dem TrayIcon nicht hinbekomme, hat es auch keinen Zweck mit dem Programm weiter zu machen.
Du könntest ja mal meine Anleitung genau befolgen und dann die Fehlermeldungen posten, die du so bekommst.