Click here to Skip to main content
Click here to Skip to main content

CTrayIconPosition - where is my tray icon?

By , 10 Jul 2012
 

Sample Image - CTrayIconPosition.gif

Introduction

This compact class makes one impossible thing possible - it's able to detect position of tray icon of your application.

The problem

I'm the author of the popular application Tray Helper. This application takes advantage of Shell_NotifyIcon WinAPI function and puts its own icon into system tray. For a long time, I didn't have any problems with this feature until.... I found a great class by Joshua Heyer (Shog9) - CBalloonHelp. Shog9's class is able to display cool balloon messages and I decided that my application will display those balloons coming from my tray icon. After reading MSDN and many WWW sites, I found that there is no way to determine where exactly your tray icon is! There is no API at all for that! 

Two ways to do it (2+)

  1. Direct method: Seems to be perfect if end user do not use a different tray manager than the one that is installed with MS Windows.

    This method was proposed by Neal Andrews and I ported it from VB (source code) to C++. The main idea behind this method is that system tray uses an ordinary toolbar control to display icons (if you don't believe me - check it out with Spy++ application). It's also an easy thing to find a handle of this control and ask it directly for the rectangle of our icon. There are two things that need to be implemented. First, we need to find a handle to toolbar control. It can be done by enumerating all windows in a system and finding the one with Shell_TrayWnd class name (this is a main window for a system tray). Then we enumerate all the child windows of the tray to find a toolbar (ToolbarWindow32 class name).

    Once we have a handle to a toolbar, we can query it for the number of icons it currently posseses:

    //now we check how many buttons is there - should be more than 0
    int iButtonsCount = SendMessage(hWndTray, TB_BUTTONCOUNT, 0, 0);

    If number of icons seems to be fine (is greater than 0), ee can start thinking how to ask this control for our icon. If toolbar would be a part of our application, we could just send it TB_GETBUTTON and TB_GETITEMRECT messages. It could look like:

    for(int iButton=0; iButton<iButtonsCount; iButton++)
    {
        TBBUTTON buttonData;
        //this structure will be filled with data about button
    
        SendMessage(hWndTray, TB_GETBUTTON, iButton, (LPARAM)&buttonData);
    }

    But in our code, such message would fail or even raise a General Protection Fault error! The main reason is that we can't pass pointer to locally allocated TBBUTTON structure to another process (process of Windows tray application). To solve this problem, we need to allocate TBBUTTON structure inside tray application process. Then we can send message to a toolbar with a pointer to that allocated memory, and at the end - we can read this block of memory back to our application.

    Code sample (error checking was skipped for easier reading):

    BOOL FindOutPositionOfIconDirectly(const HWND a_hWndOwner, 
                 const int a_iButtonID, CRect& a_rcIcon)
    {
        HWND hWndTray = GetTrayToolbarControl();
    
        //now we have to get an ID of the parent process for system tray
        DWORD dwTrayProcessID = -1;
        GetWindowThreadProcessId(hWndTray, &dwTrayProcessID);
    
        //here we get a handle to tray application process
        HANDLE hTrayProc = 
          OpenProcess(PROCESS_ALL_ACCESS, 0, dwTrayProcessID);
     
        //now we check how many buttons is there - should be more than 0
        int iButtonsCount = SendMessage(hWndTray, TB_BUTTONCOUNT, 0, 0);
    
        //We want to get data from another process - it's not possible 
        //to just send messages like TB_GETBUTTON with a locally
        //allocated buffer for return data. Pointer to locally allocated 
        //data has no usefull meaning in a context of another
        //process (since Win95) - so we need 
        //to allocate some memory inside Tray process.
        //We allocate sizeof(TBBUTTON) bytes of memory - 
        //because TBBUTTON is the biggest structure we will fetch. 
        //But this buffer will be also used to get smaller 
        //pieces of data like RECT structures.
        LPVOID lpData = VirtualAllocEx(hTrayProc, NULL, 
                        sizeof(TBBUTTON), MEM_COMMIT, PAGE_READWRITE);
    
        BOOL bIconFound = FALSE;
    
        for(int iButton=0; iButton<iButtonsCount; iButton++)
        {
            //first let's read TBUTTON information 
            //about each button in a task bar of tray
    
            DWORD dwBytesRead = -1;
            TBBUTTON buttonData;
            SendMessage(hWndTray, TB_GETBUTTON, iButton, (LPARAM)lpData);
    
            //we filled lpData with details of iButton icon of toolbar 
            //- now let's copy this data from tray application
            //back to our process
            ReadProcessMemory(hTrayProc, lpData, &buttonData, 
                             sizeof(TBBUTTON), &dwBytesRead);
    
            //let's read extra data of each button: 
            //there will be a HWND of the window that 
            //created an icon and icon ID
            DWORD dwExtraData[2] = { 0,0 };
            ReadProcessMemory(hTrayProc, (LPVOID)buttonData.dwData, 
                   dwExtraData, sizeof(dwExtraData), &dwBytesRead);
    
            HWND hWndOfIconOwner = (HWND) dwExtraData[0];
            int  iIconId         = (int)  dwExtraData[1];
    
            if(hWndOfIconOwner != a_hWndOwner || iIconId != a_iButtonID)
            {
                continue;
            }
    
            //we found our icon - in WinXP it could be hidden - let's check it:
            if( buttonData.fsState & TBSTATE_HIDDEN )
            {
                break;
            }
    
            //now just ask a tool bar of rectangle of our icon
            RECT rcPosition = {0,0};
            SendMessage(hWndTray, TB_GETITEMRECT, iButton, (LPARAM)lpData);
            ReadProcessMemory(hTrayProc, lpData, 
                         &rcPosition, sizeof(RECT), &dwBytesRead);
    
            MapWindowPoints(hWndTray, NULL, (LPPOINT)&rcPosition, 2);
            a_rcIcon = rcPosition;
    
            bIconFound = TRUE;
            break;
        }
    
        VirtualFreeEx(hTrayProc, lpData, NULL, MEM_RELEASE);
        CloseHandle(hTrayProc);
    
        return bIconFound;
    }
  2. Visual scan method: There is also a different approach possible. We can find the rectangle of system tray (we did it in previous method) and then scan this area for our icon manually. The idea is easy but implementation was not. As you may guess, Shell_NotifyIcon while adding your icon to the system tray often does a lot of things with your icon. What is being done depends, for example, on the version of Windows and sometimes on your graphic mode (number of colors). In other words, if you ask Shell_NotifyIcon to add your beautiful 32x32 pixels big and colorful icon to the tray - it could land there with reduced size and number of colors, and it's near not possible to predict how it would look like in the system tray. So it would not be wise to try to seek for your colorful icon.

    But there is an easy and reliable solution (it really works on nearly all machines!). What about changing your default icon to plain-black one, seek for black rectangle in system tray, and after that restore the icon of your application? Not convinced? Well, I was skeptic also - but it just works fine :)

  3. Using both methods: Direct scan method seems to be perfect - but what if user changed his/her default tray application to some third party software available in the market? It's rather unlikely but if you write an application that has to work on every PC, you should consider it. Second approach (visual scan) has a chance to succeed when first one will fail. Ultimate solution is simple - use both methods - if one fails, just try the second one. The code posted here gives you an easy ability to take this approach.

Usage in your projects

I wrote a compact class CTrayIconPosition. If you want to use it in your project - follow these few simple steps:

  • Add TrayIconPosition.h and TrayIconPosition.cpp to your project.
  • Add #include "TrayIconPosition.h" in files where you plan to use this class.
  • Declare a variable of this class (in my opinion, the best is to make it a member variable of your main dialog window or something like that).
  • Copy IDI_BLANK_BLACK icon from sample project, to your application.
  • Use API described below.

CTrayIconPosition API

  • void InitializePositionTracking(HWND hwndOfIconOwner, int iIconID);

    Before calling this function, you should already have your icon in system tray. This function initializes tracking mechanism.

  • int iIconID - it's the ID of the icon you set while adding the icon to tray with Shell_NotifyIcon.
  • BOOL GetTrayIconPosition(TrackType a_eTrackType = UseBothTechniquesDirectPrefered, Precision a_ePrec = Default);

    This function calculates position of tray icon, and returns TRUE if icon was found and FALSE if it was not found. But even if return value is FALSE - you can use point value - since it most likely will contain useful data. For example, under Windows XP, your tray icon can be hidden - then the return value of this function will be FALSE. But point will contain left, middle part of system tray (in WinXP, it's hide/unhide icons button). Remember that call of this function can change your tray icon to black - call RestoreTrayIcon if you want to undo this effect.

    Please note a_eTrackType parameter: it controls how class should do the tracking. Allowed values are:

    • UseBothTechniquesDirectPrefered - class will try to detect your icon using direct method first; in case of failure, it will do the visual scan of system tray.
    • UseBothTechniquesVisualScanPrefered - similar to UseBothTechniquesDirectPrefered but the order of detection is visual first and direct if visual failed.
    • UseDirectOnly - self explaining.
    • UseVisualScanOnly - self explaining.
  • void RestoreTrayIcon(HICON icon);

    Restores black icon set by GetTrayIconPosition. Since icon changes quite often in my Tray Helper application, I implemented restoring icon in a separate function call. If your application has static, always the same icon, it could be convenient to change this class to auto call this restore function.

  • void SetDefaultPrecision(Precision newPrecision);

    Let me explain meaning of this function on example:

    You call GetTrayIconPostion member function many times a second. Since this function sets black icon in tray - such numerous calls in short period of time could look quite bad (flickering). But usually, position of tray icon doesn't change that often. That's why CTrayIconPosition keeps a cache of last calculated position and if you call GetTrayIconPosition - it is able to return cached value instead of checking it over and over again. Cached value is valid only for some time - and using this function, you can set it if you want more accurate results or less accurate with less flickering.

    Acceptable values:

    • CTrayIconPosition::Default
    • CTrayIconPosition::High - cached position will expire in 10 seconds
    • CTrayIconPosition::Medium - cached position will expire in 30 seconds
    • CTrayIconPosition::Low - cached position will expire in 60 seconds

    On default, High precision is assumed.

  • void Invalidate();

    This function forces next call of GetTrayIconPosition not to use cached values.

Example of usage

//let's add icon to system tray first
NOTIFYICONDATA nid; 
nid.cbSize = sizeof(nid);
nid.hWnd = m_hWnd; 
//ID of icon - you have to pass this 
//value to InitializePositionTracking
nid.uID = 1; 
nid.uFlags = NIF_ICON; 
nid.hIcon = AfxGetApp()->LoadIcon(IDI_YOUR_ICON);
Shell_NotifyIcon(NIM_ADD, &nid);

//let's initialize tray icon position tracking
//second argument it's ID of icon (nid.uID)
m_tipPosition.InitializePositionTracking(m_hWnd,1);

//ok now let's find out the position of our tray icon:
//use m_tipPosition.Invalidate(); 
//if you want to avoid few-seconds position cashing
CPoint ptIcon;
BOOL bIconFound = m_tipPosition.GetTrayIconPosition(ptIcon, 
       CTrayIconPosition::UseBothTechniquesDirectPrefered);

//GetTrayIconPosition in order to find out position 
//can (unless UseDirectOnly method is used)
//sets a black icon in tray - let's restore it now
m_tipPosition.RestoreTrayIcon(AfxGetApp()->LoadIcon(IDI_YOUR_ICON));

//use returned CPoint value here :-D

Some remarks

  1. I was testing this class on Win 98, ME, 2000 and XP - on all those systems, it was working fine. If you're concerned about this changing icon to black for a moment (in visual scan method) - I want to say that on most cases, it's not noticeable. Even on slow, overloaded machines, detecting position takes less than a blink of eye. So it should not be a problem.
  2. As you can see, you have to add IDI_BLANK_BLACK icon to your project in order to use this class - I know that it is not the best way it could be done. If you like, you can write your own code that will create plain, black icon in a run time and get rid of this resource. My goal was to present a compact and working class - you're free to improve it!
  3. Some people were asking me why I didn't use balloon tool tips feature of Windows 2000 and XP. If you ask yourself the same question, it means that you don't understand the purpose of this class - it's designed to find out exact position of tray icon - displaying balloon is only an example - you can of course use it in quite a different way.
  4. If you have a proposal to make this class more effective and reliable - I would be grateful for your feedback. Post a comment on this site or mail me at: irekzielinski-DEL_THIS@wp.pl.
  5. Function GetTrayIconPosition returns a CPoint class type data. Some of you could be disappointed why it's not a CRect or RECT structure. Well - it's because I didn't need that - I think that it's rather easy to modify this class to return such a bit more valuable data. In current implementation, GetTrayIconPosition returns not exactly center point of tray icon - it's rather left, top part.
  6. Sorry for my English - it's not my native language :-D

History of updates

22nd of December 2004

  • Added direct scan method as proposed by Neal Andrews here.

28th of May 2004

  • Updated algorithm to detect a black icon (CheckIfColorIsBlackOrNearBlack) as proposed by Harald here.

License

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

About the Author

Irek Zielinski
Web Developer
United Kingdom United Kingdom
Irek works as a C++ senior software developer.
He runs also his own small shareware bussines (He is author of few quite popular applications like: Tray Helper or Time Adjuster).
 
Occasionaly he posts articles to Codeguru or Codeproject. Besides C++ he likes motorcycles and paragliding.
Check out his software at: http://www.ireksoftware.com

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
QuestionVista, W7 not working, VisualScan only + fix. [modified]memberdchris_med7-Mar-12 17:24 
Unfortunately this this class it won't work on w7. More, the visual scan method it will take a minute to scan the tray rect, if Aero is enabled...due to a slow call to GetPixel() on desktop's hwnd.
 
And here it's a fix for the visual scan technique. We drop GetPixel() and will capture the tray icon rect as an CImage and perform the visual scan on that.
 
please include hist 2 files: ScreenImage.h
// Author:  Ovidiu Cucu
// Website: http://www.codexpert.ro/

#pragma once
 
#include <atlimage.h>

class CScreenImage : public CImage
{
public:
    BOOL CaptureRect(const CRect& rect) throw();
    BOOL CaptureScreen() throw();
    BOOL CaptureWindow(HWND hWnd) throw();
};
 
ScreenImage.cpp
// Author:  Ovidiu Cucu
// Website: http://www.codexpert.ro/

#include "StdAfx.h"
#include "ScreenImage.h"

/****************************************************************************************
 Function:   CScreenImage::CaptureRect
 Purpose:    captures a screen rectangle
 Parameters: rect: screen rectangle to be captured
 Return:     non-zero value if successful
****************************************************************************************/
BOOL CScreenImage::CaptureRect(const CRect& rect)
{
   // detach and destroy the old bitmap if any attached
   CImage::Destroy();
   
   // create a screen and a memory device context
   HDC hDCScreen = ::CreateDC(_T("DISPLAY"), NULL, NULL, NULL);
   HDC hDCMem = ::CreateCompatibleDC(hDCScreen);
   // create a compatible bitmap and select it in the memory DC
   HBITMAP hBitmap = 
      ::CreateCompatibleBitmap(hDCScreen, rect.Width(), rect.Height());
   HBITMAP hBmpOld = (HBITMAP)::SelectObject(hDCMem, hBitmap);
 
   // bit-blit from screen to memory device context
   // note: CAPTUREBLT flag is required to capture layered windows
   DWORD dwRop = SRCCOPY | CAPTUREBLT;
   BOOL bRet = ::BitBlt(hDCMem, 0, 0, rect.Width(), rect.Height(), 
                        hDCScreen, 
                        rect.left, rect.top, dwRop);
   // attach bitmap handle to this object
   Attach(hBitmap);
 
   // restore the memory DC and perform cleanup
   ::SelectObject(hDCMem, hBmpOld);
   ::DeleteDC(hDCMem);
   ::DeleteDC(hDCScreen);
 
   return bRet;
}
 
/****************************************************************************************
 Function:   CScreenImage::CaptureScreen
 Purpose:    captures the whole screen
 Parameters: none
 Return:     non-zero value if successful
****************************************************************************************/
BOOL CScreenImage::CaptureScreen()
{
   CRect rect(0, 0, ::GetSystemMetrics(SM_CXSCREEN), ::GetSystemMetrics(SM_CYSCREEN));
   return CaptureRect(rect);
}
 
/****************************************************************************************
 Function:   CScreenImage::CaptureWindow
 Purpose:    captures a window
 Parameters: hWnd - the window handle
 Return:     non-zero value if successful
****************************************************************************************/
BOOL CScreenImage::CaptureWindow(HWND hWnd)
{
   BOOL bRet = FALSE;
   if(::IsWindow(hWnd))
   {
      CRect rect;
      ::GetWindowRect(hWnd, rect);
      bRet = CaptureRect(rect);
   }
   return bRet;
}
 
then in
 
BOOL CTrayIconPosition::FindOutPositionOfIcon(HICON icon)
 
capture the image from tray's rect:
m_image.CaptureRect(m_rtRectangleOfTheTray);
 
and modify your code like this:
	m_image.CaptureRect(m_rtRectangleOfTheTray);
	//HDC hdcScreen = GetDC( GetDesktopWindow());//CreateDC(_T("DISPLAY"),0,0,0);

	//since tray window can be strethed verticaly let's scan this area in few steps starting from the bottom to the top.
	for(int iy = m_rtRectangleOfTheTray.Height()-3; iy > 1; iy--)
	{
		int iNoOfPixelsInLine=0;
		for(int ix=0;ix<m_rtRectangleOfTheTray.Width();ix++)
		{	
			//CLR_INVALID
			//COLORREF crPixel = GetPixel(hdcScreen, ix, iy);
			//COLORREF crPixel2 = GetPixel(hdcScreen, ix, iy-2);
			//COLORREF crPixel3 = GetPixel(hdcScreen, ix, iy+2);
			
			COLORREF crPixel = m_image.GetPixel( ix, iy);
			COLORREF crPixel2 = m_image.GetPixel( ix, iy-2);
			COLORREF crPixel3 = m_image.GetPixel( ix, iy+2);
 
and here:
 
	//well I think we found it!
	m_ptPosition.x = m_rtRectangleOfTheTray.left + ix-1;
	m_ptPosition.y = m_rtRectangleOfTheTray.top +iy-6;
 
where:
 CScreenImage m_image;
 
et voila, the tray rect it's working fine with aero.
 
At the end I found that after
Shell_NotifyIcon(NIM_MODIFY, &nid);
we must add a Sleep(100) to let the shell paint the black icon; otherwise we may not capture the black icon.

modified 9-Mar-12 1:48am.

GeneralCan't compile on VS.NET 2005memberYuval Naveh23-Aug-06 22:11 

Error 1 error C3867: 'CBalloonHelp::KeyboardHookProc': function call missing argument list; use '&CBalloonHelp::KeyboardHookProc' to create a pointer to member c:\downloads\ctrayiconposition_src\balloonhelp.cpp 205
 
Error 2 error C3867: 'CBalloonHelp::MouseHookProc': function call missing argument list; use '&CBalloonHelp::MouseHookProc' to create a pointer to member c:\downloads\ctrayiconposition_src\balloonhelp.cpp 206
 
Error 3 error C3867: 'CBalloonHelp::CallWndRetProc': function call missing argument list; use '&CBalloonHelp::CallWndRetProc' to create a pointer to member c:\downloads\ctrayiconposition_src\balloonhelp.cpp 207
 
Error 4 error C2440: 'static_cast' : cannot convert from 'UINT (__thiscall CBalloonHelp::* )(CPoint)' to 'LRESULT (__thiscall CWnd::* )(CPoint)' c:\downloads\ctrayiconposition_src\balloonhelp.cpp 1085

 
Any ideas?
 
"The true sign of intelligence is not knowledge but imagination." - Albert Einstein

GeneralRe: Can't compile on VS.NET 2005memberCoruscant31-May-08 8:52 
I know this is several years too late, but for future reference and as a general comment, why not do what the error messages tell you to?
 
For errors 1-3 change the signature of the calls
 
In the case of error 4, change the return value from UINT to LRESULT
 
--
http://www.coruscant.co.uk/

GeneralRe: Can't compile on VS.NET 2005memberRocom1-Jan-12 2:16 
Just change the statement
KeyboardHookProc
MouseHookProc
CallWndRetProc
 
to
&CBalloonHelp::KeyboardHookProc
&CBalloonHelp::MouseHookProc
&CBalloonHelp::CallWndRetProc
World is vivid!

Questioncan u post the original vb - I want a COM classmemberjon.gogan.uk.fujitsu.com26-Apr-06 6:44 
as per .
 
jonny
Questionhow to delete the trayicon started by another app.memberzt978821-Mar-06 14:09 
how to delete the trayicon started by another app.
 
I only have the Trayicon App's HWND, and i do not know its uid and callback function ect...
 
can anyone help me for deleting the trayicon by using my own App.? i appreciate for
 
if it is deleted, how to restore the trayicon just being deleted
I know the MSN plug-in can hide the trayicon and the windows of the MSN.
It really convenience that officer can use this to hide the MSN when the manager coming when they're using MSN to contact with friends.(because the manager thought MSN was a waste of working time)
 
I thought [ShellRegisterCallbacks] could do this.
Using this callback function can forward the [Shell_NotifyIcon] function to a custom shell.
But this way can only work when the MSN runs after the app, I don't know how to do this when the MSN has already run.

 
hi
AnswerRe: how to delete the trayicon started by another app.memberStephen Hewitt3-Apr-06 19:26 
You don't.
 
Steve
GeneralRe: how to delete the trayicon started by another app.memberIrek Zielinski26-Apr-06 9:02 
I suspect you can. You need just to pass a valid HWND of window that created an icon and the icon id (so this class could be modified to do the trick).
Cheers!

 
Check out my software at: http://www.ireksoftware.com
AnswerRe: how to delete the trayicon started by another app.membersajithomas19-Jul-07 17:33 
Hi,
I am facing one problem: In my application I want to delete my printer Queue document. I am able to delete successfully. \
 
Now my problem is, the system Tray Icon's tool tip text is not updating the status. it is showing some document is pending in the printer queue.
 
So I am planning to delete/refresh the printer Icon from the system tray, if any one could help in this then it will be great!...
 
My mail ID Sebastin_saji@hotmail.com
 
T.Sebastin.T
QuestionPorting to C#?memberPeter Rilling23-Jul-05 18:36 
Has anyone taken the time to port this code to C#? My BalloonWindow component Adding Balloon Windows to a .NET Application
[^] would benefit from this.
AnswerRe: Porting to C#?memberAndrewVos8-Mar-06 18:58 
yeah, any .net language would be great... Have u tried yet Peter?
 


All your source are belong to us!
GeneralRe: Porting to C#?memberPeter Rilling11-Mar-06 8:16 
Some months ago I had ported about 80% of the code to C# (using both managed and p/invoke calls). Things came up and so I never got around to finishing it. I will see what I can do and post and article on it sometime in the future.
Generalhide/unhide any icons in the systraysussAnonymous14-May-05 1:21 
Hello,
First, excuse my bad english (it's not my native language, I'm French).
Then, I'm a studient and I have to create a programm which could hide/unhide any icons in the systray (even extern processes' icons) in VB or in C/C++. Do you know a way to do it?
QuestionCan you help me ?memberDreamingCat4-Jan-05 15:16 
:(How to enumerate the icons in the Systray???And How to Get the programe who founds the button in systray? for instance,sound Icon and its button,who founds them??? how to get?Sigh | :sigh:
GeneralAnother approachsusssfzhi28-Dec-04 10:14 
I can suggest one more approach (not perfect, but worth considering, I think).
Having found the tray window you can send WM_MOUSEMOVE to it with different coordinates trying to guess the icon location. As soon the message is forwarded to your application (wrapped in the message specified when calling Shell_NotifyIcon) you have found it.
 
Regards,
Sergei.
GeneralRe: Another approachmemberIrek Zielinski28-Dec-04 10:35 
We already discussed it here: http://www.codeproject.com/shell/ctrayiconposition.asp?select=426932&df=100&forumid=14631&mpp=50#xx426932xx[^]
Thanks!
Irek
 
Check out my software at: http://www.ireksoftware.com
GeneralRe: Another approachsusssfzhi28-Dec-04 10:40 
Oops! Sorry, somehow I have missed that part.
GeneralA problem to get the tip of the iconmemberygw26-Dec-04 15:49 
Thanks to the article,I acquired some knowledges from this.but I have a problem:how to get the tip of the icon in the system tray, and how to get the index of the certain icon in the system tray?
Generalproblem with hided taskbarmemberMario M.23-Dec-04 13:18 
if the taskbar is hided the arrow of the balloontip appears outside screen, and it should call the taskbar up, like any normal infotips displayed in the taskbar (eg. see how the XP SP2 security warning does). This ballon instead points to the outside of the screen and it's not painted entirely.
 
otherwise it's a great control.
QuestionHow to force position of Tray IconmemberPit M.23-Dec-04 4:50 
I would like to force my tray icon to be always the first in tray.
How can I achieve that ?
AnswerRe: How to force position of Tray IconmemberIrek Zielinski23-Dec-04 6:28 
you can modify a direct traking metchod to check if your icon is the first in the toolbar and when it's not - just remove it and then add again - It will land at the first position.
 
Check out my software at: http://www.ireksoftware.com
GeneralRe: How to force position of Tray Iconmemberthomasa88_9-Apr-07 12:32 
Sounds like a race condition if 2 apps starts doing this
 
- thomasa88

QuestionKeeping a tray icon active in WinXP?memberdlanders22-Dec-04 7:16 
Does anyone know if there is an official mechanism (or have unofficial ideas;)) to keep a tray icon active so that Windows XP does not "hide" the inactive icon? I assume that by updating the icon periodically, I can keep it "active", but that seems inefficient. Frown | :(
 
Thanks,
Dave
GeneralDoes not work with custom DPI'smemberneal andrews19-Dec-04 6:32 
If you use a custom dpi in your display settings, say 120 then the code will always fail because windows will scale the icon in the tray area.
GeneralRe: Does not work with custom DPI'smemberIrek Zielinski19-Dec-04 7:51 
Hmmm... my display settings are actually "big fonts" (120 dpi) and it seems to work perfect.
Scaling the icon is not a problem for this code since plain black icon scaled to be larger will also be a black icon.
 
Check out my software at: http://www.ireksoftware.com
GeneralRe: Does not work with custom DPI'smemberneal andrews19-Dec-04 12:42 
That was the only reason i could come up with for it not working on my Win XP system. the only other variable is that I have the task bar at the top of the screen. Anyway I've worked out a way to do the same, without any image processing, See below.
 
The codes is in vb6 but you should be able to easily convert it. The call to GethWndTray (which gets the tray's toolbar control has been ommited for compactness.
 
Private Type TrayData
hWnd As Long
ID As Long
End Type
 
Public Function GetSystemTrayItemRect(ByVal hWnd As Long, ByVal ID As Long) As RECT
Dim hWndTray As Long
Dim hProc As Long
Dim pid As Long
Dim vaPtr As Long
Dim ret As Long
Dim tbut As TBBUTTON
Dim cButtons As Long
Dim td As TrayData
Dim i As Long
Dim rc As RECT
 
hWndTray = GethWndTray
If hWndTray = 0 Then Exit Function
 
Call GetWindowThreadProcessId(hWndTray, pid)
If pid = 0 Then Exit Function

hProc = OpenProcess(PROCESS_VM, 0, pid)
If hProc = 0 Then Exit Function
 
vaPtr = VirtualAllocEx(hProc, ByVal 0&, Len(tbut), MEM_COMMIT, PAGE_READWRITE)
If vaPtr = 0 Then GoTo cleanup
 
cButtons = SendMessage(hWndTray, TB_BUTTONCOUNT, ByVal 0&, ByVal 0&)

On Error GoTo cleanup

For i = 0 To cButtons - 1
Call SendMessage(hWndTray, TB_GETBUTTON, i, ByVal vaPtr)
Call ReadProcessMemory(hProc, ByVal vaPtr, tbut, Len(tbut), ret)
 
If Not tbut.dwData = 0 Then
Call ReadProcessMemory(hProc, ByVal tbut.dwData, td, Len(td), ret)
If hWnd = td.hWnd Then
If ID = td.ID Then
Call SendMessage(hWndTray, TB_GETITEMRECT, i, ByVal vaPtr)
Call ReadProcessMemory(hProc, ByVal vaPtr, rc, Len(rc), ret)
Exit For
End If
End If
End If
Next
Call MapWindowPoints(hWndTray, 0&, rc, 2)
GetSystemTrayItemRect = rc

cleanup:
If hProc Then
If vaPtr Then
Call VirtualFreeEx(hProc, ByVal vaPtr, 0&, MEM_RELEASE)
End If
End If
If hProc Then CloseHandle (hProc)
End Function
GeneralRe: Does not work with custom DPI'smemberIrek Zielinski20-Dec-04 0:39 
Hi Smile | :)
I have ported VB example you provided to C++ and it looks promising. The problem is that all you can retrive with this example it's rectangle of each button in a tray (cool!) and TBBUTTON structure. But TBBUTTON seems to be not enought to determinate witch one of the buttons belongs to our application. Especialy there is no HWND member and button ID member seems to have different values that this ID we specify while adding icon to the tray.
 
in VB code there is a piece of code:
If hWnd = td.hWnd Then
If ID = td.ID Then

but in C++ definition of TBBUTTON of this structure there are no such members.
Anyone hve any idea how to solve it?
Best regars,
Irek
 
Check out my software at: http://www.ireksoftware.com
GeneralRe: Does not work with custom DPI'smemberneal andrews20-Dec-04 1:00 
Copy the first 8 bytes of the dwData member of the TBBUTTON, using the ReadProcessMemory API as you did to get the TBBUTTON struct. The first 4 bytes is the hWnd the button belongs to. The second 4 bytes is the NOTIFYICONDATA.iUD.
 
Then using the index of the button you can then get the it's rect by calling TB_GETITEMRECT, again ReadProcessMemory is required. As seen in my earlier post.
 
There appear's to be alot more info in dwData but I just use the first 8.
 
Hope that helps.
Regards
Neal
GeneralRe: Does not work with custom DPI'smemberIrek Zielinski22-Dec-04 22:05 
Article is updated with a method you proposed. Thanks!
 
Check out my software at: http://www.ireksoftware.com
Generalbad results with VirtuaWinmemberlode_leroy19-Apr-04 5:24 
VirtuaWin uses an icon with a lot of black,
this is sometimes seen as the icon we're looking for!
GeneralRe: bad results with VirtuaWinmemberIrek Zielinski28-May-04 8:39 
I recommend to change a bit code that detects an icon. Now code serches only few lines and test if it's black or near black. Adding more lines to search should help in this case.
 
Check out my software at: http://www.ireksoftware.com
Generalimprove CheckIfColorIsBlackOrNearBlackmemberm7878787-Apr-04 11:48 
CheckIfColorIsBlackOrNearBlack could be implemented like this:
 
BOOL CTrayIconPosition::CheckIfColorIsBlackOrNearBlack(COLORREF crColor)
{
return !(((DWORD)crColor)&0xFCFCFC);
}
 
and make it inline. code will become mutch faster (well not really a problem here)
 
or fastest would be to use it as define
 
#define CheckIfColorIsBlackOrNearBlack(crColor) (!(((DWORD)crColor)&0xFCFCFC))
 
Or a bit slower
 
BOOL CTrayIconPosition::CheckIfColorIsBlackOrNearBlack(COLORREF crColor)
{
return (GetRValue(crColor)<4) && (GetGValue(crColor)<4) && (GetBValue(crColor)<4);
}
 
BTW: nice idea to solve the problem!
 
What do you think of using an icon with a white border (2pix for 32*32 icon) and calculate the exact rectangle of the icon?
 
Harald
GeneralRe: improve CheckIfColorIsBlackOrNearBlackmemberIrek Zielinski28-May-04 9:14 
Hi Smile | :)
Thanks for the feed-back.
I have just submited an update to souce code with:
BOOL CTrayIconPosition::CheckIfColorIsBlackOrNearBlack(COLORREF crColor)
{
return !(((DWORD)crColor)&0xFCFCFC);
}
 
>What do you think of using an icon with a white border (2pix for 32*32 icon) and calculate the exact rectangle of the icon?
 
I think that this could fail since there is no proof that border will not be disrupted by Windows. But I was thinking on a different approach. First you put a plain black icon into the tray and detect all black points, then put a bit lighter icon (dark gray) and check previously black pixels if now those are dark gray - is should stil take a blink of eye and be able to detect an rectangle of icon with a great precision.... I don't plan to implement this feature but (since simple point detection is enought in my applicatins) but I would gladly update souce code if someone would do this Smile | :)
 
Best regards!
Irek
 
Check out my software at: http://www.ireksoftware.com
GeneralEnhancememberdchris_med15-Sep-03 12:27 
Add this:
TrayIconPosition.h
CRect m_rcLastTrayPos
 
TrayIconPosition.cpp
GetTrayIconPosition(CPoint &point, Precision prPrec)
{
...
if(m_rcLastTrayPos!=m_rtRectangleOfTheTray)
bUpdateRequired=1;
 

if(bUpdateRequired)
{
... m_rcLastTrayPos = m_rtRectangleOfTheTray;
 

GeneralRe: Enhancememberdchris_med7-Mar-12 18:51 
This isn't good because the icon rect could move inside the tray rect Poke tongue | ;-P
 
Instead we could track the mouse position from where the function gets called and see if fits the old icon rect. If not..scan for the new rect icon; at least this will lower the scan operations...especially on vista or later which requires visual scan only...that black icon. Laugh | :laugh:
GeneralAttach a menumemberonthecurrant20-Jun-03 0:15 
re hello,
 
I have some mistake with my application. In the OnInitDialog function i do that( i have include CBalloonHelp):
 
NOTIFYICONDATA nid;
nid.cbSize = sizeof(nid);
nid.hWnd = m_hWnd;
nid.uID = 1;
nid.uFlags = NIF_ICON|NIF_TIP;
// strncpy(nid.szTip, "Our icon in tray :-D", 64);
// nid.hIcon = AfxGetApp()->LoadIcon(IDR_ICON2);
Shell_NotifyIcon(NIM_ADD, &nid);
 
//let's initialize tray icon position tracking
//second argument it's ID of icon (nid.uID)
m_tipPosition.InitializePositionTracking(m_hWnd,1);
 
// Add "About..." menu item to system menu.
TraySetIcon(IDR_ICON2);
sprintf(status,"Applicom = %d .Twincat = %d",Appli.GetStatusUnitel(),Appli.GetStatusTwin());
TraySetToolTip(status);
 
TraySetMenu(IDR_MENU1);
TrayShow(); // Show the tray icon
ShowWindow(SW_HIDE); // Hide the window

 
But when i call GetTrayIconPosition in CTrayIconPosition, it load a black icon. After what i refresh my icone in the OnTimer() like that:
sprintf(status,"Applicom = %s Twincat = %s",mot,mot2);
TraySetToolTip(status);
//TraySetMenu(IDR_MENU1);
TrayUpdate();

 
Finaly, my problem is the menu is not attach to my icon and i have to restart my PC because Windows can't killed the thread.
 
Have you some ideas.
 
Thanks by advance.
GeneralRe: Attach a menumemberonthecurrant20-Jun-03 0:19 
re
it is like that:
sprintf(status,"Applicom = %s Twincat = %s",mot,mot2);
TraySetToolTip(status);
TraySetMenu(IDR_MENU1);
TrayUpdate();


GeneralBalloon behind the traymemberonthecurrant10-Jun-03 22:49 
hello,
 
In the first time i would like ti said this code is great. It's the le final part of my project.Big Grin | :-D
 
I have just a problem. The Tail of the balloon appears behind the tray.Confused | :confused:
I don t understand why beacause i creat the balloon after the tray.
 
If you have some ideas, i thank you by advance.
 

GeneralRe: Balloon behind the traymemberIrek Zielinski11-Jun-03 0:08 
As far I know - balloon can be constructed with TOPMOST attribute - it should help Poke tongue | ;-P
 
Check out my software at: http://www.ireksoftware.com
GeneralRe: Balloon behind the traymemberonthecurrant11-Jun-03 1:42 
ok, thanks that's work with TOPMOST.
One more time great job
GeneralThat's what I was looking for!membernewmodel9-Jun-03 22:30 
Thanks Irek! I was looking for such code for a long time... Well done Smile | :)
 
Przemek
GeneralBugmemberChopper27-Feb-03 23:11 
This "Tray Icon" doesn't position itself correctly when the taskbar is on the left or on the right of the desktop. And the pointer in such situation looks real ugly.
 
Regards,
Vitaly Tomilov
 

 
Ultimate all-in-one XP-Style UI multiplatform solution: Tooltips, XP Menus, Hyperlinks, Drawing Graphics and formatted documents, plus powerful binary resource reuse. All Free 100% on www.tooltips.net. It can be used in VC++, C#, Java+, VB, Delphi, C++ Builder, as well as in any COM-compatible platform.
GeneralRe: BugmemberIrek Zielinski27-Feb-03 23:34 
That's not true. Position is beign calculated correctly.
 
This is rater bug in CBalloonHelp class that is not able to display balloon with such anchor points in a nice way.
Please relly on coordinates that application returns rather than the outlook of third-party-created balloon.
 
Check out my software at: http://www.ireksoftware.com
GeneralRe: BugmemberChopper3-Mar-03 2:45 
  1. The position is not calculated correctly, as the balloon won't overlap the taskbar ever!
  2. I relied on your demo project, in which, if I use my taskbar on the left or right side of the desktop the balloon's pointer looks horrible!
  3. Did you ever try your example on a multimonitor system?
Regards,
Vitaly
 

Professional tooltips for all development platforms Free on www.Tooltips.NET

GeneralRe: BugmemberIrek Zielinski3-Mar-03 21:29 
I think I have to make some things clear.
 
First of all this article describes CTrayIconPosition class and have nothing to do with CBalloonHelp class by Shog.
 
The most important data that is being calculated by demo application are X/Y coordinates of tray icon. While creating demo application I had to decide how to display those coordinates - at first I was thinking about simple MessageBox with such kind of message: "Icon found! Coordinates X[xxx] Y[yyy]."
But since I use balloon class in my applications I thought that it would be a good idea to not only show the raw coordinates but also display example of usage of this class.
 
So while testing this class - please compare COORDINATES given in result with position of your tray icon. Balloon is only example of use of this coordinates.
As you suggest balloon looks ugly when task bar is in left or right part of the screen - that's true. But it has nothing to do with CTrayIconPosition class described here.

I think that far better place to point this bug is CBalloonHelp discussion forum (link on the beginning of article).
 
About multi-monitors:
CTrayIconPosition was not checked under multi-monitor environment. But I don't predict any problems - if I'm wrong - let me know about that.
 
I hope this will clarify some things and there will be no more misunderstandings.
Best regards,
Irek
 
Check out my software at: http://www.ireksoftware.com
GeneralUm..memberFuran24-Feb-03 5:33 
What about sending WM_MOUSEMOVE directly to that window with client coordinates that jump by 16 pixels across the area? Then you have no bitmap scanning or anything like that, and you just catch your WM_MOUSEMOVE being sent in your code. Probably a little faster.
-Ian
GeneralRe: Um..memberIrek Zielinski24-Feb-03 7:31 
Well - that's a good idea. I will try if it works Smile | :)
 
Check out my software at: http://www.ireksoftware.com
GeneralRe: Um..memberIrek Zielinski24-Feb-03 21:53 
Well Smile | :)
I spent some time investigting this possibility.
Since it seems to work - it has one serious disadventage.
I have few applications in my tray and one of them started to dispaly a tool tip while recieving WM_MOUSEMOVE mesage. I'm afraid that is not acceptable.
I'm going to spend more time on this matter but seems to me sending fake messages could lead some tray applications to behave strangely.
 
Check out my software at: http://www.ireksoftware.com
GeneralRe: Um..memberFuran25-Feb-03 5:08 
Irek Zielinski wrote:
I have few applications in my tray and one of them started to dispaly a tool tip while recieving WM_MOUSEMOVE mesage. I'm afraid that is not acceptable.
 
Yea, that's no good. Do you send more than one mousemove to the same icon area?
GeneralRe: Um..memberIrek Zielinski25-Feb-03 8:43 
Actualy I was trying few ways to send this WM_MOUSEMOVE message. I'm afraid if I was able to find one application that actualy cares about WM_MOUSEMOVE - there could be more such applications installed on PCs of my users. The problem is that there is no way to guess if fake messages you send will be ignored by tray applications or not.
But thanks for poiting this idea - it could prove usefull someday!
 
Check out my software at: http://www.ireksoftware.com

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

Permalink | Advertise | Privacy | Mobile
Web04 | 2.6.130617.1 | Last Updated 10 Jul 2012
Article Copyright 2003 by Irek Zielinski
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid