Skip to content

Instantly share code, notes, and snippets.

@nathan-osman
Last active September 8, 2023 08:05
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nathan-osman/57a560a85783ebaa0b12b86fd4b04b61 to your computer and use it in GitHub Desktop.
Save nathan-osman/57a560a85783ebaa0b12b86fd4b04b61 to your computer and use it in GitHub Desktop.
Create a very basic system tray icon
project(wintray)
add_executable(wintray WIN32
main.cpp
resource.rc
wintray.exe.manifest
)
set_target_properties(wintray PROPERTIES
CXX_STANDARD 11
CXX_STANDARD_REQUIRED ON
)
target_link_libraries(wintray Comctl32)
/*
* The MIT License (MIT)
*
* Copyright (c) 2023 Nathan Osman
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#ifndef CONFIG_H
#define CONFIG_H
#define IDI_ICON 101
#endif // CONFIG_H
/*
* The MIT License (MIT)
*
* Copyright (c) 2023 Nathan Osman
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#ifndef UNICODE
#define UNICODE
#endif
#include <windows.h>
#include <windowsx.h>
#include <commctrl.h>
#include <shellapi.h>
#include <strsafe.h>
#include "config.h"
#define ID_QUIT 101
const UINT WMAPP_NOTIFYCALLBACK = WM_APP + 1;
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow)
{
// Create and register the window class
const wchar_t CLASS_NAME[] = L"WndClass";
WNDCLASS wc = { };
wc.lpfnWndProc = WindowProc;
wc.hInstance = hInstance;
wc.lpszClassName = CLASS_NAME;
RegisterClass(&wc);
// Create a window only for notifications
CreateWindowEx(
0,
CLASS_NAME,
L"System Tray Window",
0,
0,
0,
0,
0,
HWND_MESSAGE,
NULL,
hInstance,
NULL
);
// Event loop
MSG msg = { };
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
void createPopup(HWND hwnd, POINT pt)
{
// Create the popup
HMENU hmenu = CreatePopupMenu();
// Create the menu item
MENUITEMINFO mii = { };
mii.cbSize = sizeof(mii);
mii.fMask = MIIM_STRING | MIIM_ID;
mii.wID = ID_QUIT;
mii.dwTypeData = L"&Quit";
mii.cch = wcslen(mii.dwTypeData);
// Insert it
InsertMenuItem(hmenu, 0, TRUE, &mii);
// Show the menu
int id = TrackPopupMenu(
hmenu,
TPM_RETURNCMD,
pt.x,
pt.y,
0,
hwnd,
NULL
);
switch (id) {
case ID_QUIT:
PostQuitMessage(0);
break;
}
DestroyMenu(hmenu);
}
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg) {
case WM_CREATE:
{
// Prepare the struct
NOTIFYICONDATA nid = { };
nid.hWnd = hwnd;
nid.uFlags = NIF_ICON | NIF_TIP | NIF_MESSAGE;
nid.uCallbackMessage = WMAPP_NOTIFYCALLBACK;
// Load the icon
LoadIconMetric(
GetModuleHandle(nullptr),
MAKEINTRESOURCE(IDI_ICON),
LIM_SMALL,
&(nid.hIcon)
);
// Load the tooltip
StringCchCopy(nid.szTip, ARRAYSIZE(nid.szTip), L"Tray Icon");
Shell_NotifyIcon(NIM_ADD, &nid);
// Set the version
nid.uVersion = NOTIFYICON_VERSION_4;
Shell_NotifyIcon(NIM_SETVERSION, &nid);
return 0;
}
case WMAPP_NOTIFYCALLBACK:
{
if (LOWORD(lParam) == WM_CONTEXTMENU) {
// Determine the offset
POINT pt = {
GET_X_LPARAM(wParam),
GET_Y_LPARAM(wParam)
};
createPopup(hwnd, pt);
}
return 0;
}
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
#include <windows.h>
#include "config.h"
IDI_ICON ICON DISCARDABLE "wintray.ico"
<?xml version='1.0' encoding='UTF-8' standalone='yes'?>
<assembly xmlns='urn:schemas-microsoft-com:asm.v1' manifestVersion='1.0' xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
<dependency>
<dependentAssembly>
<assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" language="*" processorArchitecture="*" version="6.0.0.0" publicKeyToken="6595b64144ccf1df"/>
</dependentAssembly>
</dependency>
<asmv3:application>
<asmv3:windowsSettings>
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware>
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness>
</asmv3:windowsSettings>
</asmv3:application>
</assembly>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment