Reputation: 129
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
Upvotes: 1
Views: 8438
Reputation: 7948
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.
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, ¬ifyIconData)
defer shellDll.ShellNotifyIcon(w32.NIM_DELETE, ¬ifyIconData)
// 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
.
Upvotes: 0
Reputation: 595369
What you are asking for is covered in MSDN's documentation for Shell_NotifyIcon()
:
NIF_MESSAGE (0x00000001)
TheuCallbackMessage
member is valid
uCallbackMessage
Type: UINTAn 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, thewParam
parameter of the message contains the identifier of the taskbar icon in which the event occurred. This identifier can be 32 bits in length. ThelParam
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 theuCallbackMessage
member, but the interpretation of thelParam
andwParam
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:
Specify an hWnd
that will receive notifications from the icon.
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.
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);
}
Upvotes: 9