David S.
David S.

Reputation: 11200

How to create a notification balloon on Windows?

I want to create a simple command line tool to post quick notifications like this.

enter image description here

I want the tool to be as simple and small as possible. So I choose to code in CPP, and use Win32 API directly.

I found this guide very useful. But it seems this Shell_NotifyIcon API requires a valid hWnd handler, which means I will have to create a hidden/invisible window in my command line tool, which I'd rather not.

Any better idea on how to create a notification on Windows?

Upvotes: 6

Views: 2946

Answers (2)

Michael Haephrati
Michael Haephrati

Reputation: 4245

You can do that easily with no need to use ATL or MFC, just pure Win32 API, which can be a Console application as well.

First you need the icon image. Store it in your resource.h file

#define IDI_BATTERY_IMAGE             101

add an .rc file to your project with the following line

IDI_BATTERY_IMAGE             ICON                    "Battery.ico"

Bonus tip: you can find free images to download, like this one. Just make sure to call it "Battery.ico" and place it in the path of your other source files.

Best if you create a separate pair of .cpp and .h files for the notification balloon functionality. In your .cpp file place the following code:

// tray icon data
NOTIFYICONDATA m_NID;

BOOL CreateTrayIcon()
{
    memset(&m_NID, 0, sizeof(m_NID));
    m_NID.cbSize = sizeof(m_NID);

    m_NID.uID = IDI_BATTERY_IMAGE;

    // set handle to the window that receives tray icon notifications
    m_NID.hWnd = GetForegroundWindow();

    // fields that are being set when adding tray icon 
    m_NID.uFlags = NIF_MESSAGE | NIF_ICON;

    // set image
    m_NID.hIcon = LoadIcon(NULL,MAKEINTRESOURCE(IDI_BATTERY_IMAGE));

    if (!m_NID.hIcon)
        return FALSE;

    m_NID.uVersion = NOTIFYICON_VERSION_4;

    if (!Shell_NotifyIcon(NIM_ADD, &m_NID))
        return FALSE;

    return Shell_NotifyIcon(NIM_SETVERSION, &m_NID);
}

BOOL ShowTrayIconBalloon(LPCTSTR pszTitle, LPCTSTR pszText, UINT unTimeout, DWORD dwInfoFlags)
{
    m_NID.uFlags |= NIF_INFO;
    m_NID.uTimeout = unTimeout;
    m_NID.dwInfoFlags = dwInfoFlags;

    if (StringCchCopy(m_NID.szInfoTitle, sizeof(m_NID.szInfoTitle), pszTitle) != S_OK)
        return FALSE;

    if (StringCchCopy(m_NID.szInfo, sizeof(m_NID.szInfo), pszText) != S_OK)
        return FALSE;

    return Shell_NotifyIcon(NIM_MODIFY, &m_NID);
}

In your header file place the following:

BOOL CreateTrayIcon();
BOOL ShowTrayIconBalloon(LPCTSTR pszTitle, LPCTSTR pszText, UINT unTimeout, DWORD dwInfoFlags);

In your main function add the following code:

CreateTrayIcon();
ShowTrayIconBalloon(L"27 percent remaining", L"Your battary has such and such percentage...", 1000, NULL);

The following code was tested with a simple Console app.

Upvotes: 4

David Heffernan
David Heffernan

Reputation: 613572

The shell notification API requires that you supply a window handle. So create a message only window and use that as the owner of the notification icon and balloons.

That you would prefer not to create a window in your console app is understandable but the API is what it is. You don't get to re-write system APIs for your convenience. You just have to go along with them.

Upvotes: 4

Related Questions