繁体   English   中英

NOTIFY ICON 点击c++中的Win32 Api

[英]NOTIFY ICON Click Win32 Api in c++

我有这段代码可以在任务栏中设置一个图标,但是当有人右/左单击它时我不能。 有没有办法做到这一点?

//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;

有人可以帮我吗? 谢谢

您要求的内容包含在 MSDN 的Shell_NotifyIcon()文档中:

NOTIFYICONDATA结构

NIF_MESSAGE (0x00000001)
uCallbackMessage成员有效

u回调消息
类型:UINT

应用程序定义的消息标识符。 系统使用此标识符向hWnd中标识的 window 发送通知消息。 图标的边界矩形中发生鼠标事件或 hover 时,当使用键盘选择或激活图标时,或者当气球通知中发生这些操作时,将发送这些通知消息。

uVersion成员为 0 或 NOTIFYICON_VERSION 时,消息的wParam参数包含事件发生的任务栏图标的标识符。 这个标识符的长度可以是 32 位。 lParam参数保存与事件关联的鼠标或键盘消息。 例如,当指针移到任务栏图标上时, lParam设置为 WM_MOUSEMOVE。

uVersion成员为 NOTIFYICON_VERSION_4 时,应用程序继续通过uCallbackMessage成员以应用程序定义消息的形式接收通知事件,但对该消息的lParamwParam参数的解释更改如下:

  • LOWORD(lParam)包含通知事件,例如 NIN_BALLOONSHOW、NIN_POPUPOPEN 或 WM_CONTEXTMENU。

  • HIWORD(lParam)包含图标 ID。 图标 ID 的长度限制为 16 位。

  • GET_X_LPARAM(wParam)返回通知事件 NIN_POPUPOPEN、NIN_SELECT、NIN_KEYSELECT 以及 WM_MOUSEFIRST 和 WM_MOUSELAST 之间的所有鼠标消息的 X 锚点坐标。 如果这些消息中的任何一个是由键盘生成的,则wParam将设置为目标图标的左上角。 对于所有其他消息, wParam未定义。

  • GET_Y_LPARAM(wParam)返回为 X 锚点定义的通知事件和消息的 Y 锚点坐标。

添加通知图标时,您需要:

  1. 指定将从图标接收通知的hWnd

  2. 指定一个nIDguidItem来标识图标。 如果您使用guidItem并显示多个图标,通知将无法告诉您哪个图标在通知您,因此您必须为每个图标使用单独的 HWND。 此外, guidItem实际上在功能上比nID更受限制,并且导致的问题多于它解决的问题,因此我强烈建议完全远离guidItem并始终仅使用nID

  3. 启用NIF_MESSAGE标志,并提供自定义uCallbackMessage消息 ID。

每当用户与图标交互时,HWND 的 window 过程就会收到消息 ID。 消息的WPARAMLPARAM值将描述操作。

例如:

#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只是帮你制作图标,右键菜单需要你自己定义。 当您启用NIF_MESSAGE时,NOTIFYICONDATA。 UCallbackMessage将有效。

这样WndProc.uMsg = NOTIFYICONDATA.UCallbackMessage

您可以在消息事件上创建一个新的 PopupMenu。


这里是使用go写的例子,但主要调用winapi。

例子

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)
    }
}

注意:一个HWND可以创建多个ShellNotifyIcon,以UID区分,可以创建多个,只需要输入不同的NOTIFYICONDATA即可。 UIDs

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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