Solved

How to get TrayIcon bounds exactly ???

Posted on 1997-02-27
16
735 Views
Last Modified: 2013-12-04
Hello;

I've got an application that puts an Icon in the System Tray using Delphi 2.0.  I'd like to be able to find out what the bounds of my TrayIcon are in screen coordinates. (Bounds being defined as the hittable area of my TrayIcon
with a mouse click/move/doubleclick event).  

I've tried HitTesting the TrayIcon window with Mouse Messages, and this seems to work, but also ends up sending these mouse messages to the adjacent TrayIcon's and causing them to respond to the Mouse Messages.  This isn't good, because I need to ensure that other TrayIcon's aren't affected by the HitTest.

Does anyone know of a way for me to exactly find out the bounds of a TrayIcon, without adversely affecting other TrayIcon's  ???  
0
Comment
Question by:bradp
  • 8
  • 4
  • 2
  • +2
16 Comments
 

Author Comment

by:bradp
ID: 1298911
Edited text of question
0
 

Author Comment

by:bradp
ID: 1298912
Edited text of question
0
 
LVL 2

Expert Comment

by:sapek
ID: 1298913
Could you write more about what you tried (ie what "mouse messages" you used) and more about what was wrong with it. I think it will be faster if you contact me dircetly via e-mail at sapek@homer.iinf.polsl.gliwice.pl

Adam
0
 

Author Comment

by:bradp
ID: 1298914
Adjusted points to 350
0
 
LVL 3

Expert Comment

by:sperling
ID: 1298915
The SysTray does not allocate anything especial for the icons placed there. Meaning, there are no handles to query.

I'd suggest you do as follows:

- Create and show the icon.
- Save cursor pos
- Use FindWindow and so on to locate the systray window
- Loop through all pixels in the windows rectangle, and set the cursor pos. This loop should interact with the mousemove event handler, to limit the number of loop iterations.
- Finally, after detecting the bounds, restore cursor position


As mouse moves very rarely effects the icons, this should be a rather safe method to get the bounds. It is not 100% failsafe, though, there's an odd chance some app actually does something when the user moves the mouse over it.
You would, by the way, also keep checking that mouse moves sent to the icon are within the bounds you have detected, in case another app is removed from the tray and your icon moves.



If you wan't some example Delphi code, let me know.

Regards,

Erik.
0
 

Author Comment

by:bradp
ID: 1298916
When I said "HitTesting", I meant sending a message to the SysTray window.  The process I used to do this is exactly the process that you described.  But this process isn't "safe" enough for me.  I need 100% safety. This feature is going into a commercial product, a Delphi component.  If it isn't 100% safe, then all of the end users of my component will have to deal with this "unsafe" feature in their applications.  If I can't have safety, then I'd rather not implement the feature at all.  Thanks for the suggestion.  I look forward to a 100% safe solution.
0
 

Expert Comment

by:nkeynes
ID: 1298917
I have no idea if this applies to Delphi, but normally windows sends a 'user' message to inform your application of mouse activity over your tray icon, you don't need to hit-test it yourself.
0
 

Author Comment

by:bradp
ID: 1298918
I know that Windows sends a notification message to my TrayIcon whenever an event occurs (ie someone moves over the TrayIcon, clicks the TrayIcon, etc).  I've got tons of expericence with Event driven programming and TrayIcon's, and know exactly what you mean.  The only reason I was doing my own hit testing is because I'm trying to find out the bounds of my TrayIcon in screen coordinaes.  

If I send a mouse move message to the TrayWindow and my TrayIcon receives the message, I can assume that my TrayIcon was at the coordinates of the Mouse move message.  If my TrayIcon doesn't receive the message, then I can assume that it isn't at the coordinates of the mouse message that I sent.  Using this technique, I should be able to find out the bounds of my TrayIcon in screen coordinates IF I can find a message that is passed on to TrayIcon's but ISN'T documented as a message that TrayIcons' are supposed to be able to receive.  This way I can "safely" assume that I won't be adversely affecting other TrayIcon's when I send the messages to the TrayWindow.  By the way, If anyone can figure out a better way to get the bounds of a TrayIcon, I'd be glad to use it.  Remember that it''s got to be "professional", in the respect that I can't have the TrayIcon hiding and showing every once in a while or anything like that.  

Thanks for the feedback !
0
IT, Stop Being Called Into Every Meeting

Highfive is so simple that setting up every meeting room takes just minutes and every employee will be able to start or join a call from any room with ease. Never be called into a meeting just to get it started again. This is how video conferencing should work!

 
LVL 3

Expert Comment

by:sperling
ID: 1298919
I've got a little bit of info, and an idea which might get you started...

The SysTray window obviously creates a region for each icon. The handle for this region is passed as wParam when the SysTray window sends a WM_NCPAINT message, shortly after a modification of any visible icon has occurred.

It *might* be possible to use a hook to catch this message, and then use the standard region functions to query for the position of the region, bounding rect and so on. I haven't tried this out, as it would take more time than I can spare at the moment, but you could investigate it a bit further.

The possible pitfalls I can see:
1) How to catch a SendMessage. (Is there a hook for this?)
2) Does Windows check ownership of a region handle?
3) Is the region created using screen coordinates, or created using local coordinates and then drawn at the wanted position.

Another approach:
  Find the processes main thread and suspend it.
  Inject a DLL into the process.
  Patch calls to SendMessage/Region functions, have them call your DLL
  In your injected DLL, handle whatever, and pass calls on to windows.

This is a proven and working technique used by debuggers and API Spys.(Yes, I know from experience) It is a helluva lot of work though, to get that damned rectangle ;)

Sorry I didn't have the time to investigate this further. But, you sound as if you know what you're talking about, and this might get you started.

Regards,

Erik.


0
 

Author Comment

by:bradp
ID: 1298920
Hmmm.. This Windows Hook idea might have some merit.  I've got some expericence with Windows Hooks and WM_NCXX messages from some work I did recently, and should be able to give this a try failrly soon.  Thanks for the suggestion, I'll let you know if it works !
0
 
LVL 2

Accepted Solution

by:
gysbert1 earned 350 total points
ID: 1298921
You don't have to know where your icon is located. In fact,
it is bad to know, because the position can change at any time.
When you set up your tray icon, there is a uID that you can set,
and you can check for this uID when a tray message is intercepted. The trick therefore is only in choosing a uID that
no other tray app uses. The chances are pretty small though that
your app uses the same uID as one of your other 10 tray apps (nobody seriously wants to run more than about 10 tray apps at once...) I have done this successfully in a project, but I don't have the source code with me at the moment. If you need an example, just email me and I can send it to you from home.
0
 

Author Comment

by:bradp
ID: 1298922
Could I get that source ?  That's be great.
Thanks.
0
 
LVL 3

Expert Comment

by:sperling
ID: 1298923
*Why* do you need the tray icon bounds? I should've asked that question long ago.... ;)

Regards,

Erik.

0
 

Author Comment

by:bradp
ID: 1298924
Actually, thats a great question.  The main reason is that I'd like to implement OnMouseEnter and OnMouseLeave events for my TrayIcon.  

It's easy to implement OnMouseEnter and OnMouseMove, since Windows sends the WM_MOUSEMOVE message to each TrayIcon.  The problem is in implementing the OnMouseLeave event.  I've got it implemented by using a Timer, and differencing the position of the last mouse move message and the current mouse position.  If the difference gets to big, then I know that the TrayIcon hasn't received a mouse move message in a while.  The problem is that this doesn't work in the case that something captures the mouse input while I'm over the TrayIcon.  Initially it would seem that this wouldn't happen all that much, but TrackPopupMenu seems to use it in tracking mouse movements.

So to summarize, I want the TrayIcon's bounds so I can know for sure where my TrayIcon is on the screen.  This will allow me to find out for sure if the cursor is over my TrayIcon or not, and will allow me to fire an OnMouseLeave event only when it should be fired.

Thanks for the help.
Brad.
0
 
LVL 3

Expert Comment

by:sperling
ID: 1298925
;) Should have asked long ago, as I said.

I've got a solution where you don't need to get the icon bounds. It's really rather simple...

I'll submit it if/when you open the question for answers again.


Regards,

Erik.
0
 
LVL 2

Expert Comment

by:gysbert1
ID: 1298926
Here is a the code for a form for a little test program I wrote
a while back:

unit Mnform;

interface

uses
      SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
  Forms, Dialogs, ExtCtrls, Menus;

type
  TMainForm = class(TForm)
    MainPopupMenu: TPopupMenu;
    CloseMenuItem: TMenuItem;
    N1: TMenuItem;
    About1: TMenuItem;
    Timer1: TTimer;
            procedure CloseMenuItemClick(Sender: TObject);
            procedure FormCreate(Sender: TObject);
            procedure FormDestroy(Sender: TObject);
    procedure FormShow(Sender: TObject);
    procedure About1Click(Sender: TObject);
    procedure AppIdle(Sender: TObject; var Done: Boolean);
      private
            { Private declarations }
    SSave : boolean;
            procedure TrayCallBack(var M : TMessage); message WM_USER+102;

      public
            { Public declarations }
      end;

var
      MainForm: TMainForm;

implementation

{$R *.DFM}

uses
      ShellAPI,MMSystem,about;

procedure TMainForm.CloseMenuItemClick(Sender: TObject);
begin
 Close;
end;

var
  Sleep : word;

procedure TMainForm.FormCreate(Sender: TObject);
var
      P : TNotifyIconData;
begin
      {Display tray icon}
      P.cbSize:=sizeof(P);
      P.Wnd:=Handle;
      P.uID:=15000;
      P.uFlags:=NIF_MESSAGE+NIF_ICON+NIF_TIP;
      P.uCallbackMessage:=WM_USER+102;
      P.hIcon:=Application.Icon.Handle;
      P.szTip:='Start Screen Saver';

      Shell_NotifyIcon(NIM_ADD, Addr(P));

  Sleep:=0;
  Application.OnIdle:=AppIdle;
end;

procedure TMainForm.TrayCallBack(var M : TMessage);
var
      uId : UINT;
      uMouseMsg : UINT;
      P : TPoint;
begin
      uId:=UINT(M.wParam);
      uMouseMsg:=UINT(M.lParam);
      if uId=15000 then
    begin
      if (uMouseMsg=WM_RBUTTONUP) then begin
        GetCursorPos(P);
                        MainPopupMenu.Popup(P.X, P.Y);
      end;
      if (uMouseMsg=WM_LBUTTONDBLCLK) then
      begin
        SSave:=true;
      end;
    end;
end;

procedure TMainForm.FormDestroy(Sender: TObject);
var
      P : TNotifyIconData;
begin
      {Remove tray icon}
      P.cbSize:=sizeof(P);
      P.Wnd:=Handle;
      P.uID:=15000;

      Shell_NotifyIcon(NIM_DELETE, Addr(P));
end;

procedure TMainForm.FormShow(Sender: TObject);
begin
      {Go away}
      Width:=0;
      Height:=0;
end;


procedure TMainForm.About1Click(Sender: TObject);
begin
  AboutBox.ShowModal;
end;

procedure TMainForm.AppIdle(Sender: TObject; var Done: Boolean);
begin
  if (SSave=true) then
    begin
      MessageBeep(1);
      SSave:=false;
      SendMessage(Handle,WM_SYSCOMMAND,SC_SCREENSAVE,0);
    end;
  Done:=false;
end;

end.



Sorry the formatting is so bad, but these little netscape memo type things leave much to be desired....
0

Featured Post

6 Surprising Benefits of Threat Intelligence

All sorts of threat intelligence is available on the web. Intelligence you can learn from, and use to anticipate and prepare for future attacks.

Join & Write a Comment

This article shows how to make a Windows 7 gadget that extends its U/I with a flyout panel -- a window that pops out next to the gadget.  The example gadget shows several additional techniques:  How to automatically resize a gadget or flyout panel t…
Whether you've completed a degree in computer sciences or you're a self-taught programmer, writing your first lines of code in the real world is always a challenge. Here are some of the most common pitfalls for new programmers.
This is Part 3 in a 3-part series on Experts Exchange to discuss error handling in VBA code written for Excel. Part 1 of this series discussed basic error handling code using VBA. http://www.experts-exchange.com/videos/1478/Excel-Error-Handlin…
When you create an app prototype with Adobe XD, you can insert system screens -- sharing or Control Center, for example -- with just a few clicks. This video shows you how. You can take the full course on Experts Exchange at http://bit.ly/XDcourse.

743 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

13 Experts available now in Live!

Get 1:1 Help Now