hkBattousai
hkBattousai

Reputation: 10931

How do I show shell notify icon balloon tool tip on demand?

My aim is to create a shell notification icon, and make it display a balloon tool tip when demanded. I wrote the following code.

bool SystemTrayIcon::Create(const std::wstring &    Tip,
                            HWND                    hWndParent,
                            const GUID &            Guid,
                            UINT                    IdCallback,
                            HICON                   hIcon,
                            TYPE                    Type,
                            bool                    bSound,
                            bool                    bSharedIcon,
                            bool                    bHidden,
                            bool                    bLargeIcon,
                            bool                    bRespectQuiteTime)
{
    NotifyIconData.cbSize           = sizeof(NotifyIconData);
    NotifyIconData.hWnd             = hWndParent;
    NotifyIconData.uID              = 0;    // Use GUID instead. (NIF_GUID)
    NotifyIconData.uFlags           = NIF_MESSAGE | NIF_ICON | NIF_TIP | NIF_STATE | NIF_GUID | NIF_REALTIME | NIF_SHOWTIP;
    NotifyIconData.uCallbackMessage = IdCallback;
    NotifyIconData.hIcon            = hIcon;

    if (Tip.length() > MAX_TIP)
    {
        std::wcsncpy(NotifyIconData.szTip, Tip.c_str(), MAX_TIP);
        NotifyIconData.szTip[MAX_TIP] = wchar_t(0);
    }
    else
    {
        std::wcsncpy(NotifyIconData.szTip, Tip.c_str(), Tip.length() + 1);
    }

    NotifyIconData.dwState          = (bSharedIcon      ? NIS_SHAREDICON    : 0)
                                    | (bHidden          ? NIS_HIDDEN        : 0);
    NotifyIconData.dwStateMask      = NIS_SHAREDICON | NIS_HIDDEN;
    NotifyIconData.uVersion         = NOTIFYICON_VERSION_4;

    DWORD dwType;
    switch (Type)
    {
        case TYPE::STI_INFO:
            dwType = NIIF_INFO;
            break;
        case TYPE::STI_WARNING:
            dwType = NIIF_WARNING;
            break;
        case TYPE::STI_ERROR:
            dwType = NIIF_ERROR;
            break;
    }

    NotifyIconData.dwInfoFlags      = dwType | NIIF_USER
                                    | (bSound               ? 0                         :   NIIF_NOSOUND)
                                    | (bLargeIcon           ? NIIF_LARGE_ICON           :   0)
                                    | (bRespectQuiteTime    ? NIIF_RESPECT_QUIET_TIME   :   0);

    NotifyIconData.guidItem         = Guid;

    return bInitialized = Shell_NotifyIconW(NIM_ADD, &NotifyIconData);
}

void SystemTrayIcon::Balloon(   const std::wstring &    Title,
                                const std::wstring &    Message,
                                HICON                   hBalloonIcon)
{
    if (!bInitialized) return;
    if (Title.length() > MAX_TITLE)
    {
        std::wcsncpy(NotifyIconData.szInfoTitle, Title.c_str(), MAX_TITLE);
        NotifyIconData.szInfoTitle[MAX_TITLE] = wchar_t(0);
    }
    else
    {
        std::wcsncpy(NotifyIconData.szInfoTitle, Title.c_str(), Title.length() + 1);
    }
    if (Message.length() > MAX_MESSAGE)
    {
        std::wcsncpy(NotifyIconData.szInfo, Message.c_str(), MAX_MESSAGE);
        NotifyIconData.szInfo[MAX_MESSAGE] = wchar_t(0);
    }
    else
    {
        std::wcsncpy(NotifyIconData.szInfo, Message.c_str(), Message.length() + 1);
    }
    NotifyIconData.hBalloonIcon = hBalloonIcon;
    NotifyIconData.dwInfoFlags |= NIF_INFO;
    Update();
}

void SystemTrayIcon::Update()
{
    Shell_NotifyIconW(NIM_MODIFY, &NotifyIconData);
}

I call these member functions in code like this:

TrayIcon.Create(/*const std::wstring &  Tip*/               L"Tip",
                /*HWND                  hWndParent*/        Window.hWnd,
                /*const GUID &          Guid*/              GUID {0x7f0f02e3, 0x479b, 0x4a09, {0xbf, 0x99, 0xd3, 0x39, 0xe9, 0x3a, 0x8b, 0x22}},
                /*UINT                  IdCallback*/        ID_TRAYICON,
                /*HICON                 hIcon*/             (HICON) LoadImageW(Status::hInstance, L"icon1.ico", IMAGE_ICON, 0, 0, LR_LOADFROMFILE),
                /*TYPE                  Type*/              SystemTrayIcon::TYPE::STI_INFO,
                /*bool                  bSound*/            true,
                /*bool                  bSharedIcon*/       false,
                /*bool                  bHidden*/           false,
                /*bool                  bLargeIcon*/        true,
                /*bool                  bRespectQuiteTime*/ false);
Sleep(10000);
TrayIcon.Balloon(   L"New Title",
                    L"New Message",
                    (HICON) LoadImageW(Status::hInstance, L"icon2.ico", IMAGE_ICON, 0, 0, LR_LOADFROMFILE));

The icon is displayed correctly after calling the Create() method. But the Balloon() method has no effect. No balloon toop tip is displayed.

I can make it display the balloon at creation by adding the NIF_INFO constant to the uFlags, and filling up the szInfoTitle and szInfo members. But what I'm trying to achieve is to display it at any arbitrary time by calling the Balloon() method.

What am I missing here?

Upvotes: 0

Views: 2439

Answers (1)

hkBattousai
hkBattousai

Reputation: 10931

I have solved a lot of problems in my code. Now it is working perfectly fine and as I desired. The below is my new code. Note the changes.

bool SystemTrayIcon::Create(const std::wstring &    Tip,
                            HWND                    hWndParent,
                            const GUID &            Guid,
                            UINT                    IdCallback,
                            HICON                   hIcon,
                            bool                    bSharedIcon,
                            bool                    bHidden)
{
    if (bInitialized) return false;

    NotifyIconData.cbSize           = sizeof(NotifyIconData);
    NotifyIconData.hWnd             = hWndParent;
    NotifyIconData.uID              = 0;    // Use GUID instead. (NIF_GUID)
    NotifyIconData.uFlags           = NIF_MESSAGE | NIF_ICON | NIF_TIP | NIF_STATE | NIF_GUID | NIF_SHOWTIP;
    NotifyIconData.uCallbackMessage = IdCallback;
    NotifyIconData.hIcon            = hIcon;

    if (Tip.length() > MAX_TIP)
    {
        std::wcsncpy(NotifyIconData.szTip, Tip.c_str(), MAX_TIP);
        NotifyIconData.szTip[MAX_TIP] = wchar_t(0);
    }
    else
    {
        std::wcsncpy(NotifyIconData.szTip, Tip.c_str(), Tip.length() + 1);
    }

    NotifyIconData.dwState          = (bSharedIcon      ? NIS_SHAREDICON    : 0)
                                    | (bHidden          ? NIS_HIDDEN        : 0);
    NotifyIconData.dwStateMask      = NIS_SHAREDICON | NIS_HIDDEN;

    NotifyIconData.guidItem         = Guid;

    bInitialized = Shell_NotifyIconW(NIM_ADD, &NotifyIconData);

    NotifyIconData.uVersion         = NOTIFYICON_VERSION_4;
    bLastResult = Shell_NotifyIconW(NIM_SETVERSION, &NotifyIconData);

    return bInitialized;
}

void SystemTrayIcon::Balloon(   const std::wstring &    Title,
                                const std::wstring &    Message,
                                HICON                   hBalloonIcon,
                                BALLOON_ICON_TYPE       IconType,
                                UINT                    Timeout,    // In milliseconds.
                                bool                    bSound,
                                bool                    bLargeIcon,
                                bool                    bRespectQuiteTime)
{
    if (!bInitialized) return;

    if (Title.length() > MAX_TITLE)
    {
        std::wcsncpy(NotifyIconData.szInfoTitle, Title.c_str(), MAX_TITLE);
        NotifyIconData.szInfoTitle[MAX_TITLE] = wchar_t(0);
    }
    else
    {
        std::wcsncpy(NotifyIconData.szInfoTitle, Title.c_str(), Title.length() + 1);
    }

    if (Message.length() > MAX_MESSAGE)
    {
        std::wcsncpy(NotifyIconData.szInfo, Message.c_str(), MAX_MESSAGE);
        NotifyIconData.szInfo[MAX_MESSAGE] = wchar_t(0);
    }
    else
    {
        std::wcsncpy(NotifyIconData.szInfo, Message.c_str(), Message.length() + 1);
    }

    DWORD dwType;
    switch (IconType)
    {
        case BALLOON_ICON_TYPE::BIT_NONE:
            dwType = 0;
            break;
        case BALLOON_ICON_TYPE::BIT_INFO:
            dwType = NIIF_INFO;
            break;
        case BALLOON_ICON_TYPE::BIT_WARNING:
            dwType = NIIF_WARNING;
            break;
        case BALLOON_ICON_TYPE::BIT_ERROR:
            dwType = NIIF_ERROR;
            break;
        case BALLOON_ICON_TYPE::BIT_USER_DEFINED:
            dwType = NIIF_USER; // Use the "hBalloonIcon" parameter.
            break;
    }

    NotifyIconData.dwInfoFlags      = dwType
                                    | (bSound               ? 0                         :   NIIF_NOSOUND)
                                    | (bLargeIcon           ? NIIF_LARGE_ICON           :   0)
                                    | (bRespectQuiteTime    ? NIIF_RESPECT_QUIET_TIME   :   0);

    NotifyIconData.uTimeout     = Timeout;
    NotifyIconData.hBalloonIcon = hBalloonIcon;
    NotifyIconData.uFlags       = NIF_INFO | NIF_GUID;

    Update();
}

void SystemTrayIcon::Update()
{
    bLastResult = Shell_NotifyIconW(NIM_MODIFY, &NotifyIconData);
}

Usage in code:

TrayIcon.Create(/*const std::wstring &  Tip*/               L"Tip",
                /*HWND                  hWndParent*/        Window.hWnd,
                /*const GUID &          Guid*/              GUID {0x7f0f02e4, 0x479b, 0x4a09, {0xbf, 0x99, 0xd3, 0x39, 0xe9, 0x3a, 0x8b, 0x22}},
                /*UINT                  IdCallback*/        ID_TRAYICON,
                /*HICON                 hIcon*/             (HICON) hImage,
                /*bool                  bSharedIcon*/       false,
                /*bool                  bHidden*/           false);

// ...

TrayIcon.Balloon(   L"Button 2",
                    L"You have just clicked the Button #2.",
                    (HICON) hImage,
                    SystemTrayIcon::BALLOON_ICON_TYPE::BIT_USER_DEFINED,
                    20000U,
                    true,
                    true,
                    false);

Upvotes: 1

Related Questions