ElektroStudios ElektroStudios - 4 months ago 17x
Vb.net Question

Get the application's NotifyIcon rectangle?

I would like to know the location of my

in the system tray (before performing any click on it).

In this other question @Hans Passant made a comment saying NO, it's not possible, but I think that almost all the things which can be done internally by the OS also can be reproduced by the developer, if I'm not right then why the SO can get the
location to show popups on it?.

In the same question above there is a C# example illustrating how to locate the systemtray rectangle, I wonder whether maybe that's a start.

Can this task can be realized?

If yes, then how?

If not, why the OS can? In what way we are limited to be unable to reproduce the same thing?


You need these declarations:

    public const Int32 WM_MYMESSAGE = 0x8000; //WM_APP
    public const Int32 NOTIFYICON_VERSION_4 = 0x4;

    public const Int32 WM_CONTEXTMENU = 0x7B;
    public const Int32 NIN_BALLOONHIDE = 0x403;
    public const Int32 NIN_BALLOONSHOW = 0x402;
    public const Int32 NIN_BALLOONTIMEOUT = 0x404;
    public const Int32 NIN_BALLOONUSERCLICK = 0x405;
    public const Int32 NIN_KEYSELECT = 0x403;
    public const Int32 NIN_SELECT = 0x400;
    public const Int32 NIN_POPUPOPEN = 0x406;
    public const Int32 NIN_POPUPCLOSE = 0x407;

    public const Int32 NIIF_USER = 0x4;
    public const Int32 NIIF_NONE = 0x0;
    public const Int32 NIIF_INFO = 0x1;
    public const Int32 NIIF_WARNING = 0x2;
    public const Int32 NIIF_ERROR = 0x3;
    public const Int32 NIIF_LARGE_ICON = 0x20;

    public enum NotifyFlags { 
        NIF_MESSAGE = 0x01, 
        NIF_ICON = 0x02,
        NIF_TIP = 0x04,
        NIF_INFO = 0x10,
        NIF_STATE = 0x08, 
        NIF_GUID = 0x20, 
        NIF_SHOWTIP = 0x80 

    public enum NotifyCommand { NIM_ADD = 0x0, NIM_DELETE = 0x2, NIM_MODIFY = 0x1, NIM_SETVERSION = 0x4}
    public struct NOTIFYICONDATA
        public Int32 cbSize;
        public IntPtr hWnd;
        public Int32 uID;
        public NotifyFlags uFlags;
        public Int32 uCallbackMessage;
        public IntPtr hIcon;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
        public String szTip;
        public Int32 dwState;
        public Int32 dwStateMask;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
        public String szInfo;
        public Int32 uVersion;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)]
        public String szInfoTitle;
        public Int32 dwInfoFlags;
        public Guid guidItem; //> IE 6
        public IntPtr hBalloonIcon;

    public static extern System.Int32 Shell_NotifyIcon(NotifyCommand cmd, ref NOTIFYICONDATA data);

    public struct RECT
        public Int32 left;
        public Int32 top;
        public Int32 right;
        public Int32 bottom;

        public Int32 cbSize;
        public IntPtr hWnd;
        public Int32 uID;
        public Guid guidItem;

    //Works with Shell32.dll (version 6.1 or later)
    [DllImport("shell32.dll", SetLastError = true)]
    public static extern int Shell_NotifyIconGetRect([In]ref NOTIFYICONIDENTIFIER identifier, [Out]out RECT iconLocation);

Add the icon:

//you only need this guid to identify your icon
private Guid guid;

//call only once to set the icon and create the guid.
private void AddIcon()
    guid = Guid.NewGuid();


    data.cbSize = Marshal.SizeOf(data);
    data.hWnd = this.Handle;
    data.guidItem = guid;
    data.uCallbackMessage = WM_MYMESSAGE; //This is the message sent to our app
    data.hIcon = Properties.Resources.myIcon;
    data.szTip = "Your text";

    data.uFlags = NotifyFlags.NIF_ICON | NotifyFlags.NIF_GUID | NotifyFlags.NIF_MESSAGE | NotifyFlags.NIF_TIP | 

    Shell_NotifyIcon(NotifyCommand.NIM_ADD, ref data);

    data.uVersion = NOTIFYICON_VERSION_4;
    Shell_NotifyIcon(NotifyCommand.NIM_SETVERSION, ref data);

Get position of icon in screen coordinates:

private void GetRectIcon()
    RECT rect = new RECT();

    notifyIcon.cbSize = Marshal.SizeOf(notifyIcon);
    //only guid is needed
    notifyIcon.guidItem = guid;

    int hresult = Shell_NotifyIconGetRect(ref notifyIcon, out rect);

    //rect now has the position and size of icon

To delete the notification icon:

private void DeleteIcon()
    data.cbSize =   Marshal.SizeOf(data);
    data.uFlags = NotifyFlags.NIF_GUID;
    data.guidItem = guid;

    Shell_NotifyIcon(NotifyCommand.NIM_DELETE, ref data);

To add a baloon

private void AddBalloon()
    data = new NOTIFYICONDATA();

    data.cbSize = Marshal.SizeOf(data);
    data.guidItem = guid;

    //Set custom icon for balloon or NIIF_NONE for no icon. You can use all the other 
    //NIIF_... for system icons
    data.dwInfoFlags = NIIF_USER;
    data.hBalloonIcon = Properties.Resources.myNewIcon;
    //text in balloon
    data.szInfo = "My text in balloon";
    //balloon title
    data.szInfoTitle = "Balloon title";
    //set the flags to be modified
    data.uFlags = NotifyFlags.NIF_INFO | NotifyFlags.NIF_SHOWTIP | NotifyFlags.NIF_GUID;

    Shell_NotifyIcon(NotifyCommand.NIM_MODIFY, ref data);

Catch messages

protected override void WndProc(ref Message m)

    if (m.Msg == WM_MYMESSAGE)
        //(Int32)m.LParam & 0x0000FFFF get the low 2 bytes of LParam, we dont need the high ones. 
        //(Int32)m.WParam & 0x0000FFFF is the X coordinate and 
        //((Int32)m.WParam & 0xFFFF0000) >> 16 the Y
        switch ((Int32)m.LParam & 0x0000FFFF) 
            case NIN_BALLOONHIDE:

            case NIN_BALLOONSHOW:

            case NIN_BALLOONTIMEOUT:

            case NIN_BALLOONUSERCLICK:
                //user clicked on balloon

            case NIN_SELECT:
                //user left click on icon

            case WM_CONTEXTMENU:
                //user right click on icon


            //get what mouse messages you want
            //case WM_LBUTTONDOWN:



    base.WndProc(ref m);

The power of unmanaged code