Click here to Skip to main content
15,881,455 members
Articles / Desktop Programming / MFC
Article

A Task Tray Applet Framework

Rate me:
Please Sign up or sign in to vote.
4.78/5 (3 votes)
24 Feb 2000CPOL 160.2K   1.1K   53   28
A framework for system tray applets
  • Download source files - 31 Kb
  • The problem

    Once I had written a control panel applet to control my NT Service I though that it might be handy to be able to control the service from an icon in the task tray. Since the result of clicking an icon in the task tray is often similar to running up a control panel applet I hoped to be able to reuse much of my code. The end result was a framework for task bar applets and a derived class that managed control panel applets. I then just let the task bar applet load my control panel applet and provided access to it from the task tray with no code changes required. The resulting classes can load any control panel applet into the task tray, just supply the file name and applet index...

    What's so special about a task tray applet anyway?

    To put an icon in the task tray all an application has to do is call Shell_NotifyIcon() to supply the icon, tool tip, callback message and a window to send it to. Making an application that's just a task tray applet is just a case of creating a hidden window, calling Shell_NotifyIcon() and processing the messages. The messages are your standard mouse click type messages. It's not that difficult but it takes a bit of time.

    What does CJBTaskTrayApplet give you?

    Creating a hidden window with a WndProc that responds to Task Tray applet messages correctly and does all the right things can be cumbersome if all you want to do is pop up a dialog box app from the task tray. The framework lets you derive from an base class that handles all of that stuff for you. All you need to do is override a few virtual functions and you have a dialog popping up! CJBTaskTrayApplet also gives you multiple applets in one exe (with multiple separate icons in the task tray) and automatic right button menu handling.

    Your derived class

    To implement your task tray applet you need to do the following:

    • include "TaskBarApplet.hpp" and "TaskBarAppletMain.hpp" the later gives you a WinMain() implementation.
    • publicly derive a class from CJBTaskTrayApplet.
    • implement the pure virtual functions GetTrayTip() and GetIcon() to supply the basic require by the framework.
    • handle OnLeftDoubleClick() to do something when a user double clicks on you. This is where you would generally show a dialog or something.
    • create an instance of your applet at global scope.
    • link with TaskBarApplet.cpp.

    If you wish you can also:

    • handle GetMenu(), HandleMenuCommand() and ReleaseMenu() to customise the menu you get - By default you get a menu with Close on it in response to OnRightButtonUp().
    • handle InitInstance() to allocate resources at start up.
    • handle any or all of OnLeftButtonDown(), OnLeftButtonUp(), OnRightButtonDown(), OnRightButtonUp(), OnLeftDoubleClick(), OnRightDoubleClick() and OnMouseMove() to customise your applet's user interface.
    <h2multiple applets="" in="" a="" single="" exe,="" but="" differently...<="" h2="">

    When I implemented CJBControlPanelApplet I placed multiple applets in a single DLL. After writing much of the same "base class has a static list" code again for the task tray applet I decided to try and create a common base class that incorporated and hid all of the "I'm a class whos instances are in a static list" functionality. It seemed simple enough until I realised that to be type-safe and remove heaps of casts in the derived class the base class needed to know about the derived class. Without a second thought I templatised the base class and had the derived class inherit from it thus:

    class CTaskBarApplet : public CLinkedClass<CTaskBarApplet>

    Then I thought about it and it seemed like a really odd thing to do. It worked great and gave no warnings but was it nice? Since then I've spoken to several people and read Stroustrup 3e (and he thinks it's a "Good Thing") and now I like the idea. It did seem very odd at first though...

    Once I had a standard way to link instances of a class together I needed a way to navigate this list at run-time. After several aborted attempts I ended up with an STL-style iterator. This lets you do things like:

    for (Iterator addIt = Begin(); addIt != End(); addIt++)
    {
       addIt->AddIcon();   // call any derived class method here
    }

    Of course, having moved the common functionality out into a base class I never got around to using the base class in CJBControlPanelApplet, but I've used it in plenty of other places.

    The CPlTrayApplet and CPlAppletClient classes

    By this stage I had a task tray framework but I still needed to somehow reuse the code in my control panel applet. Since a .cpl is just a DLL, and since I'd worked out how CPlApplets worked when I wrote CJBControlPanelApplet I decided to write a wrapper class that could load a control panel applet, send it all the right messages to initialise it and then run the applet. Once I had CJBCPlAppletClient I derived a CJBCPlTrayApplet from CJBTaskTrayApplet and wired the two together. The end result was a collaboration something like this:

    CJBTaskBarApplet.gif (4158 bytes)

    CJBCPlAppletClient isn't linked in any way to the task tray classes. It's just used by CJBCPlTrayApplet . It can be used anywhere you want to access a control panel applet programmatically.

    Using MFC with the framework

    When I started looking at task tray applets I tried to write one with MFC. I've seen it done but you have to do a fair bit of work to get it to work right. Message map entries for the callback message, preventing the CWinApp's main window from popping up briefly, etc, etc. I didn't need MFC so I wrote the straight Win32 solution. Then I wanted a single complex dialog in a task tray applet and I wanted to use MFC for the dialog and my framework for the applet... It all went well until I ran it and the dialog died with assertion failures, I didn't have a CWinApp derived class and it was a serious requirement... Since I didn't have time to convert my applet framework to a CWinApp derived framework I cheated. Since all that is required is that a CWinApp object exists so that the dialog has a message pump to use I stuck a vanilla CWinApp object inside WinMain and called AfxWinInit() directly and everything worked fine... An #ifdef around this version of WinMain() means that at the flick of a #define you can have either MFC enabled or lean and mean versions of your task tray applets.

    I really keep meaning to sit down and make CJBTaskTrayApplet a real CWinApp derivative, but, this works...

    See article on Len's homepage for the latest updates.

    License

    This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


    Written By
    Software Developer (Senior) JetByte Limited
    United Kingdom United Kingdom
    Len has been programming for over 30 years, having first started with a Sinclair ZX-80. Now he runs his own consulting company, JetByte Limited and has a technical blog here.

    JetByte provides contract programming and consultancy services. We can provide experience in COM, Corba, C++, Windows NT and UNIX. Our speciality is the design and implementation of systems but we are happy to work with you throughout the entire project life-cycle. We are happy to quote for fixed price work, or, if required, can work for an hourly rate.

    We are based in London, England, but, thanks to the Internet, we can work 'virtually' anywhere...

    Please note that many of the articles here may have updated code available on Len's blog and that the IOCP socket server framework is also available in a licensed, much improved and fully supported version, see here for details.

    Comments and Discussions

     
    GeneralKilling explorer.exe Pin
    dandy729-Dec-04 5:05
    dandy729-Dec-04 5:05 
    GeneralRe: Killing explorer.exe Pin
    KarstenK16-May-06 22:16
    mveKarstenK16-May-06 22:16 
    GeneralProblems in VC 7.1 Pin
    ThomT29-Jun-03 11:34
    ThomT29-Jun-03 11:34 
    GeneralRe: Problems in VC 7.1 Pin
    Len Holgate29-Jun-03 12:50
    Len Holgate29-Jun-03 12:50 
    GeneralRe: Problems in VC 7.1 Pin
    ThomT29-Jun-03 13:41
    ThomT29-Jun-03 13:41 
    GeneralRe: Problems in VC 7.1 Pin
    Len Holgate29-Jun-03 20:39
    Len Holgate29-Jun-03 20:39 
    GeneralRe: Problems in VC 7.1 Pin
    ThomT30-Jul-03 14:02
    ThomT30-Jul-03 14:02 
    GeneralRe: Problems in VC 7.1 Pin
    Len Holgate30-Jul-03 21:34
    Len Holgate30-Jul-03 21:34 
    GeneralRe: Problems in VC 7.1 Pin
    Len Holgate30-Jul-03 21:43
    Len Holgate30-Jul-03 21:43 
    GeneralRe: Problems in VC 7.1 Pin
    ThomT31-Jul-03 4:17
    ThomT31-Jul-03 4:17 
    GeneralRe: Problems in VC 7.1 Pin
    Len Holgate31-Jul-03 6:38
    Len Holgate31-Jul-03 6:38 
    GeneralRe: Problems in VC 7.1 Pin
    Len Holgate31-Jul-03 8:20
    Len Holgate31-Jul-03 8:20 
    GeneralRe: Problems in VC 7.1 Pin
    ThomT31-Jul-03 11:24
    ThomT31-Jul-03 11:24 
    GeneralDiscriminate between Click and Double-Click Pin
    12-May-02 11:06
    suss12-May-02 11:06 
    GeneralMenu Pin
    2-May-02 6:22
    suss2-May-02 6:22 
    GeneralTask Tray and Service Pin
    5-Mar-02 4:49
    suss5-Mar-02 4:49 
    GeneralRe: Task Tray and Service Pin
    Paul A. Howes5-Mar-02 5:14
    Paul A. Howes5-Mar-02 5:14 
    GeneralRe: Task Tray and Service Pin
    david ewan3-Jun-03 9:18
    david ewan3-Jun-03 9:18 
    "By definition a service cannot have an interface" - I think a more accurate statement would be that only a. processes running in the interactive login session and b. proceses running in the SYSTEM logon session (and this could include services) can access to WinSta0. For services this can be either set programmatically or on the service's property page there's a checkbox that says "allow service to interact with the desktop". Processes running in other login sessions (e.g. authority\principal) cannot see WinSta0.

    But the question remains, how do you get that pesky icon in the system tray (by this I mean how do you detect the start of an interactive logon session, or when the desktop is created?). If you are going to write a separate program, and this sounds reasonable, how can you - as Administrator - ensure it runs when an interactive session starts, or is this something only the interactive user can set? Don't know the answer to that one - guess I gotta get me some more learnin'.

    DEM.
    GeneralRe: Task Tray and Service Pin
    Len Holgate3-Jun-03 9:44
    Len Holgate3-Jun-03 9:44 
    GeneralRe: Task Tray and Service Pin
    david ewan3-Jun-03 9:55
    david ewan3-Jun-03 9:55 
    GeneralRe: Task Tray and Service Pin
    Len Holgate3-Jun-03 10:01
    Len Holgate3-Jun-03 10:01 
    GeneralRe: Task Tray and Service Pin
    vive_la_squeeze14-Sep-03 10:55
    vive_la_squeeze14-Sep-03 10:55 
    GeneralThe Pop Dialog is not activate Pin
    9-Jan-02 16:43
    suss9-Jan-02 16:43 
    GeneralRe: The Pop Dialog is not activate Pin
    Len Holgate9-Jan-02 20:57
    Len Holgate9-Jan-02 20:57 
    GeneralMFC DLL + ATL + Shell Pin
    23-Mar-01 9:32
    suss23-Mar-01 9:32 

    General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

    Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.