简体   繁体   中英

NOTIFY ICON Click Win32 Api in c++

I've this code that set an icon in the taskbar, but I can't when someone do Right/Left click on it. Is there a way to do that?

//Notification
    NOTIFYICONDATA nid = {};
    nid.hWnd = hwnd;
    nid.cbSize = sizeof(nid);
    nid.uFlags = NIF_ICON | NIF_TIP | NIF_GUID;

    // Note: This is an example GUID only and should not be used.
    // Normally, you should use a GUID-generating tool to provide the value to
    // assign to guidItem.
    HICON hIcon = static_cast<HICON>(LoadImage(NULL,
        TEXT("gui\\sample.ico"),
        IMAGE_ICON,
        0, 0,
        LR_DEFAULTCOLOR | LR_SHARED | LR_DEFAULTSIZE | LR_LOADFROMFILE));
    static const GUID myGUID =
    { 0x23977b55, 0x10e0, 0x4041,{ 0xb8, 0x62, 0xb1, 0x95, 0x41, 0x96, 0x36, 0x68 } };
    nid.guidItem = myGUID;
    nid.hIcon = hIcon;
    // This text will be shown as the icon's tooltip.
    StringCchCopy(nid.szTip, ARRAYSIZE(nid.szTip), title);
    SendMessage(nid.hWnd, WM_SETICON, ICON_BIG, (LPARAM)hIcon);
    SendMessage(nid.hWnd, WM_SETICON, ICON_SMALL, (LPARAM)hIcon);
//TaskBar
        // Show the notification.
        Shell_NotifyIcon(NIM_ADD, &nid) ? S_OK : E_FAIL;

Could somebody help me please? Thanks

What you are asking for is covered in MSDN's documentation for Shell_NotifyIcon() :

NOTIFYICONDATA structure

NIF_MESSAGE (0x00000001)
The uCallbackMessage member is valid

uCallbackMessage
Type: UINT

An application-defined message identifier. The system uses this identifier to send notification messages to the window identified in hWnd . These notification messages are sent when a mouse event or hover occurs in the bounding rectangle of the icon , when the icon is selected or activated with the keyboard, or when those actions occur in the balloon notification.

When the uVersion member is either 0 or NOTIFYICON_VERSION, the wParam parameter of the message contains the identifier of the taskbar icon in which the event occurred. This identifier can be 32 bits in length. The lParam parameter holds the mouse or keyboard message associated with the event. For example, when the pointer moves over a taskbar icon, lParam is set to WM_MOUSEMOVE.

When the uVersion member is NOTIFYICON_VERSION_4, applications continue to receive notification events in the form of application-defined messages through the uCallbackMessage member, but the interpretation of the lParam and wParam parameters of that message is changed as follows:

  • LOWORD(lParam) contains notification events, such as NIN_BALLOONSHOW, NIN_POPUPOPEN, or WM_CONTEXTMENU.

  • HIWORD(lParam) contains the icon ID. Icon IDs are restricted to a length of 16 bits.

  • GET_X_LPARAM(wParam) returns the X anchor coordinate for notification events NIN_POPUPOPEN, NIN_SELECT, NIN_KEYSELECT, and all mouse messages between WM_MOUSEFIRST and WM_MOUSELAST. If any of those messages are generated by the keyboard, wParam is set to the upper-left corner of the target icon. For all other messages, wParam is undefined.

  • GET_Y_LPARAM(wParam) returns the Y anchor coordinate for notification events and messages as defined for the X anchor.

When you add your notification icon, you need to:

  1. Specify an hWnd that will receive notifications from the icon.

  2. Specify an nID or guidItem to identify the icon. If you use guidItem and display multiple icons, notifications will not be able to tell you which icon is notifying you, so you would have to use a separate HWND for each icon. Also, guidItem is actually more restrictive in functionality than nID , and causes more problems than it solves, so I strongly recommend staying away from guidItem altogether and always use nID only.

  3. enable the NIF_MESSAGE flag, and provide a custom uCallbackMessage message ID.

The HWND's window procedure will then receive the message ID whenever the user interacts with the icon. The message's WPARAM and LPARAM values will describe the action.

For example:

#define APPWM_ICONNOTIFY (WM_APP + 1)

...

HICON hIcon = static_cast<HICON>(LoadImage(NULL,
    TEXT("gui\\sample.ico"),
    IMAGE_ICON,
    0, 0,
    LR_DEFAULTCOLOR | LR_SHARED | LR_DEFAULTSIZE | LR_LOADFROMFILE));

SendMessage(hwnd, WM_SETICON, ICON_BIG, (LPARAM)hIcon);
SendMessage(hwnd, WM_SETICON, ICON_SMALL, (LPARAM)hIcon);

//Notification
NOTIFYICONDATA nid = {};
nid.cbSize = sizeof(nid);
nid.hWnd = hwnd;
nid.uID = 1;
nid.uFlags = NIF_ICON | NIF_TIP | NIF_MESSAGE;
nid.uCallbackMessage = APPWM_ICONNOTIFY;
nid.hIcon = hIcon;
// This text will be shown as the icon's tooltip.
StringCchCopy(nid.szTip, ARRAYSIZE(nid.szTip), title);

// Show the notification.
Shell_NotifyIcon(NIM_ADD, &nid);

...

LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch (uMsg)
    {
        case APPWM_ICONNOTIFY:
        {
            switch (lParam)
            {
                case WM_LBUTTONUP:
                    //...
                    break;
                case WM_RBUTTONUP:
                    //...
                    break;
            }

            return 0;
        }

        //...
    }

    return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

ShellNotifyIcon only helps you to create icons, the right click menu needs to be defined by you. When you enable NIF_MESSAGE , the NOTIFYICONDATA. UCallbackMessage will be valid.

such that WndProc.uMsg = NOTIFYICONDATA.UCallbackMessage

and you can create a new PopupMenu on the message event.


Here is an example written using go, but mainly calling winapi.

Example

func main() {
    // prepare test data
    var iInfo w32.ICONINFO
    myHICON := w32.HICON(userDll.MustLoadImage(
        0,                         // hInstance must be NULL when loading from a file
        "testdata/img/golang.ico", // the icon file name
        w32.IMAGE_ICON,            // specifies that the file is an icon
        0, 0,                      // cx, cy
        w32.LR_LOADFROMFILE| // we want to load a file (as opposed to a resource)
            w32.LR_DEFAULTSIZE| // default metrics based on the type (IMAGE_ICON, 32x32)
            w32.LR_SHARED, // let the system release the handle when it's no longer used
    ))
    userDll.GetIconInfo(myHICON, &iInfo)

    defer gdiDll.DeleteObject(w32.HGDIOBJ(iInfo.HbmColor))
    defer gdiDll.DeleteObject(w32.HGDIOBJ(iInfo.HbmMask))

    // variable used for createWindow
    chanWin := make(chan w32.HWND)

    const WMNotifyIconMsg = w32.WM_APP + 123

    go runWindow("classShellNotifyIcon", "windowShellNotifyIcon",
        WMNotifyIconMsg,
        chanWin)

    hwndTarget, isOpen := <-chanWin
    if !isOpen {
        return
    }

    notifyIconData := w32.NOTIFYICONDATA{
        CbSize: 968,
        HWnd:   hwndTarget,
        UID:    888, // wparam
        UFlags: 0 | // NIF: NotifyIcon Flag
            w32.NIF_ICON | // HIcon // main Icon
            w32.NIF_INFO | // SzInfo, SzInfoTitle
            w32.NIF_TIP | // SzTip
            w32.NIF_MESSAGE, // UCallbackMessage
        DwInfoFlags: 0 | // NIF: NotifyIcon Info Flag
            w32.NIIF_USER | w32.NIIF_LARGE_ICON | // HBalloonIcon
            w32.NIIF_NOSOUND,
        HIcon:            myHICON,
        HBalloonIcon:     userDll.MustLoadIcon(0, w32.MakeIntResource(w32.IDI_QUESTION)),
        UCallbackMessage: WMNotifyIconMsg,
    }

    copy(notifyIconData.SzInfo[:], utf16.Encode([]rune("SzInfo Body"+"\x00")))
    copy(notifyIconData.SzInfoTitle[:], utf16.Encode([]rune("SzInfoTitle Title"+"\x00")))
    copy(notifyIconData.SzTip[:], utf16.Encode([]rune("Hover message"+"\x00")))
    _ = shellDll.ShellNotifyIcon(w32.NIM_ADD, &notifyIconData)
    defer shellDll.ShellNotifyIcon(w32.NIM_DELETE, &notifyIconData)

    // test
    _ = userDll.PostMessage(hwndTarget, WMNotifyIconMsg, 123, w32.WM_LBUTTONUP)
    _ = userDll.PostMessage(hwndTarget, WMNotifyIconMsg, 0, w32.WM_RBUTTONUP)

    <-chanWin
}

func runWindow(wndClassName, wndWindowName string,
    WMNotifyIconMsg w32.UINT,
    ch chan<- w32.HWND,
) {
    hInstance := w32.HINSTANCE(kernelDll.GetModuleHandle(""))
    wndProcFuncPtr := syscall.NewCallback(w32.WndProc(func(hwnd w32.HWND, uMsg w32.UINT, wParam w32.WPARAM, lParam w32.LPARAM) w32.LRESULT {
        switch uMsg {
// check:
// 1. uCallbackMessage: https://learn.microsoft.com/en-us/windows/win32/api/shellapi/ns-shellapi-notifyicondataa#nif_showtip-0x00000080
// 2. NIN_.{...} https://learn.microsoft.com/en-us/windows/win32/api/shellapi/nf-shellapi-shell_notifyicona#remarks
        case w32.WM_CLOSE:
            if wParam != 123 {
                log.Println("do not close the window.")
                userDll.ShowWindow(hwnd, w32.SW_HIDE)
                return 0
            }
        case w32.WM_DESTROY:
            log.Println("WM_DESTROY")
            userDll.PostQuitMessage(0)
            return 0
        case w32.WM_CREATE:
            ch <- hwnd
        case WMNotifyIconMsg:
            log.Println("NOTIFYICONDATA.UID", wParam)
            switch lParam {
            case w32.WM_MOUSEMOVE:
                log.Println("WMNotifyIconMsg WM_MOUSEMOVE")
            case w32.NIN_BALLOONUSERCLICK:
                log.Println("NIN_BALLOONUSERCLICK")
                userDll.ShowWindow(hwnd, w32.SW_SHOW)
            case w32.NIN_BALLOONSHOW:
                log.Println("NIN_BALLOONSHOW")
            case w32.NIN_BALLOONTIMEOUT:
                log.Println("NIN_BALLOONTIMEOUT")
            case w32.WM_LBUTTONUP:
                log.Println("WMNotifyIconMsg->WM_LBUTTONUP")
            case w32.WM_LBUTTONDBLCLK:
                log.Println("WMNotifyIconMsg->WM_LBUTTONDBLCLK")
                userDll.ShowWindow(hwnd, w32.SW_SHOWNORMAL) // SW_MAXIMIZE
            case w32.WM_RBUTTONDBLCLK:
                // exit program
                if errno := userDll.DestroyWindow(hwnd); errno != 0 {
                    log.Printf("%s", errno)
                    return 0
                }
            case w32.WM_RBUTTONUP:
                // show popup menu
                log.Println("WMNotifyIconMsg->WM_RBUTTONUP")
                hMenu := userDll.CreatePopupMenu()
                _ = userDll.AppendMenu(hMenu, w32.MF_STRING, 1023, "Display Dialog")
                _ = userDll.SetMenuDefaultItem(hMenu, 0, true)
                _ = userDll.AppendMenu(hMenu, w32.MF_STRING, 1024, "Say Hello")

                _ = userDll.AppendMenu(hMenu, w32.MF_STRING, 1025, "Exit program")

                // Optional add icon for 1025
                {
                    var menuItemInfo w32.MENUITEMINFO
                    menuItemInfo.CbSize = uint32(unsafe.Sizeof(menuItemInfo))
                    menuItemInfo.FMask = w32.MIIM_BITMAP
                    hIconError := userDll.MustLoadIcon(0, w32.MakeIntResource(w32.IDI_ERROR))
                    var iInfo w32.ICONINFO
                    userDll.GetIconInfo(hIconError, &iInfo)
                    menuItemInfo.HbmpItem = iInfo.HbmColor
                    _ = userDll.SetMenuItemInfo(hMenu, 1025, false, &menuItemInfo)
                    defer func() {
                        gdiDll.DeleteObject(w32.HGDIOBJ(iInfo.HbmColor))
                        gdiDll.DeleteObject(w32.HGDIOBJ(iInfo.HbmMask))
                    }()
                }

                defer userDll.DestroyMenu(hMenu)

                var pos w32.POINT
                _ = userDll.GetCursorPos(&pos)
                userDll.SetForegroundWindow(hwnd)
                // "Displays" a shortcut menu at the specified location and "tracks the selection of items on the menu".
                _, _ = userDll.TrackPopupMenu(hMenu, w32.TPM_LEFTALIGN, pos.X, pos.Y, 0, hwnd, nil)
            default:
                log.Printf("WMNotifyIconMsg unknown lParam: %d\n", lParam)
            }
            return 1
        case w32.WM_COMMAND:
            id := w32.LOWORD(wParam)
            switch id {
            case 1023:
                _ = userDll.PostMessage(hwnd, uint32(WMNotifyIconMsg), 0, w32.WM_LBUTTONDBLCLK)
            case 1024:
                log.Println("hello")
            case 1025:
                log.Println("1025")
                _ = userDll.PostMessage(hwnd, w32.WM_DESTROY, 0, 0)
            }
        }
        return userDll.DefWindowProc(hwnd, uMsg, wParam, lParam)
    }))

    userDll.RegisterClass(&w32.WNDCLASS{
        Style:         w32.CS_HREDRAW | w32.CS_HREDRAW,
        HbrBackground: w32.COLOR_WINDOW,
        WndProc:       wndProcFuncPtr,
        HInstance:     hInstance,
        // HIcon:
        ClassName: &(utf16.Encode([]rune(wndClassName + "\x00")))[0],
    })

    defer func() {
        userDll.UnregisterClass(wndClassName, hInstance)
        close(ch)
    }()

    hwnd, _ := userDll.CreateWindowEx(0,
        wndClassName,
        wndWindowName,
        w32.WS_OVERLAPPEDWINDOW,

        // Size and position
        w32.CW_USEDEFAULT, w32.CW_USEDEFAULT, w32.CW_USEDEFAULT, w32.CW_USEDEFAULT,

        0, // Parent window
        0, // Menu
        hInstance,
        0, // Additional application data
    )

    if hwnd == 0 {
        return
    }

    var msg w32.MSG
    for {
        if status, _ := userDll.GetMessage(&msg, 0, 0, 0); status <= 0 {
            break
        }
        userDll.TranslateMessage(&msg)
        userDll.DispatchMessage(&msg)
    }
}

Note: One HWND can create more than one ShellNotifyIcon, which is distinguished by UID, you can create more than one, just feed different NOTIFYICONDATA. UIDs .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM