Help with Shell_NotifyIcon function to update the tray icon's tooltip

Get help with using AutoHotkey (v1.1 and older) and its commands and hotkeys
iPhilip
Posts: 823
Joined: 02 Oct 2013, 12:21

Help with Shell_NotifyIcon function to update the tray icon's tooltip

20 Nov 2018, 21:37

Hi Folks,

I am trying to use the Shell_NotifyIcon function to change the tray icon's tooltip, much like the Menu, Tray, Tip [, Text] command works. I am running into some difficulties when the text is over 64 characters. The code below illustrates the problem. I suspect the issue is with how I am using the StrPut function below.

Code: Select all

Text := ""
Loop, 100
   Text .= Mod(A_Index, 10)
Menu, Tray, Tip, %Text%
MsgBox The tray tooltip should now be 100 characters long
SetTrayTip(A_ScriptHwnd, , Text)
MsgBox The tray tooltip is only 64 characters long. Why?
ExitApp

SetTrayTip(hWnd, uID := 0x404, Text := "") {
   NumPut(VarSetCapacity(NID,(A_IsUnicode ? 2 : 1) * 384 + A_PtrSize * 5 + 40, 0), NID, 0, "UInt")
   NumPut(hWnd , NID,  (A_PtrSize = 8 ?  8 :  4), A_PtrSize ? "Ptr" : "UInt")
   NumPut(uID  , NID,  (A_PtrSize = 8 ? 16 :  8), "UInt")
   NumPut(0x4  , NID,  (A_PtrSize = 8 ? 20 : 12), "UInt")  ; NIF_TIP = 0x4
   StrPut(Text, &NID + (A_PtrSize = 8 ? 40 : 24))  ; <<<<<
   Return DllCall("shell32\Shell_NotifyIcon" (A_IsUnicode ? "W" : "A"), "UInt", 0x1, "UInt", &NID, "Int")  ; NIM_MODIFY = 0x1
}
According to Microsoft, the NOTIFYICONDATA structure supports a tooltip with maximum of 128 characters, including the terminating NULL, so there should be no problem displaying a tooltip of a 100 characters.

If you have any insights or advice, I would appreciate it.

Cheers!

-iPhilip
Windows 10 Pro (64 bit) - AutoHotkey v2.0+ (Unicode 64-bit)
iPhilip
Posts: 823
Joined: 02 Oct 2013, 12:21

Re: Help with Shell_NotifyIcon function to update the tray icon's tooltip

21 Nov 2018, 21:36

+1
Windows 10 Pro (64 bit) - AutoHotkey v2.0+ (Unicode 64-bit)
Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

Re: Help with Shell_NotifyIcon function to update the tray icon's tooltip

23 Nov 2018, 07:09

Hello iPhilip :wave:
You have the wrong size of the struct. Try cbSize := a_ptrsize == 8 ? 968 : (a_isunicode ? 952 : 504).
I have attempted to write down the offsets of each member,

Code: Select all

typedef struct _NOTIFYICONDATAW {
  DWORD cbSize;									0				:	0				:	0			
  HWND  hWnd;									8				:	4				:	4
  UINT  uID;									16				:	8				:	8
  UINT  uFlags;									20 				:	12				:	12
  UINT  uCallbackMessage;						24				:	16				:	16
  HICON hIcon;									32				:	20				:	20
#if ...				
  WCHAR szTip[64];								
#else				
  WCHAR szTip[128];								40				: 	24				:	24
#endif				
  DWORD dwState;								40+128*2 = 296	:	24+128*2 = 280 	:	24+128*1 = 152
  DWORD dwStateMask;							300 			: 	284				:	156
  WCHAR szInfo[256];							304 			: 	288				:	160
  union {				
    UINT uTimeout;				
    UINT uVersion;				
  } DUMMYUNIONNAME;								304+256*2 = 816	: 	288+256*2 = 800	:	160+256 = 416
  WCHAR szInfoTitle[64];						820				:	804				:	420
  DWORD dwInfoFlags;							820+64*2 = 948	:	804+64*2 = 932	:	420+64 = 484
  GUID  guidItem;								952				:	936				:	488
#if 0
  HICON hBalloonIcon;							
#endif
} NOTIFYICONDATAW, *PNOTIFYICONDATAW;	size:	968				:	952				:	504
It is 64 bit U: 32 bit U : 32 bit ANSI. Note that hBalloonIcon is not considered in the final size.

As for the strput, you should ensure that you only write a string of maximum 127 characters.

Cheers

Edit: typo
Last edited by Helgef on 23 Nov 2018, 14:53, edited 1 time in total.
iPhilip
Posts: 823
Joined: 02 Oct 2013, 12:21

Re: Help with Shell_NotifyIcon function to update the tray icon's tooltip

23 Nov 2018, 13:38

Wow! Thank you so much for that detailed analysis Helgef. I really appreciate it. I was almost ready to give up on it. You know, yesterday was Thanksgiving here in the US. The generous gift of your time and insights is helping me to extend that spirit of gratitude. Thank you! :)

I will be studying the breakdown of the struct that you provided as I want to use this as an opportunity to learn. May I ask you some follow-up questions? One immediate one is:
  • Where did you find the NOTIFYICONDATA structure description that you provided? It is different than the one that I found on Microsoft's website. For example, the #if 0 conditional for HICON hBalloonIcon is missing on that website.
I noticed a typo in your post. The offset for the 32 bit U version of GUID guidItem should be 936 not 836. :)

Finally, I verified that the cbSizes you provided were the correct ones. In other words the function below now works. :)

Code: Select all

SetTrayTip(hWnd, uID := 0x404, Text := "") {  ; AHK_NOTIFYICON = 0x404
   NumPut(VarSetCapacity(NID, (A_PtrSize = 8 ?  968 : A_IsUnicode ? 952 : 504), 0), NID, 0, "UInt")
   NumPut(hWnd , NID,  (A_PtrSize = 8 ?  8 :  4), A_PtrSize ? "Ptr" : "UInt")
   NumPut(uID  , NID,  (A_PtrSize = 8 ? 16 :  8), "UInt")
   NumPut(0x4  , NID,  (A_PtrSize = 8 ? 20 : 12), "UInt")  ; NIF_TIP (0x00000004)
   StrPut(Text, &NID + (A_PtrSize = 8 ? 40 : 24), 127)
   Return DllCall("shell32\Shell_NotifyIcon" (A_IsUnicode ? "W" : "A"), "UInt", 0x1, "UInt", &NID, "Int")  ; NIM_MODIFY (0x00000001)
}
-iPhilip
Windows 10 Pro (64 bit) - AutoHotkey v2.0+ (Unicode 64-bit)
Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

Re: Help with Shell_NotifyIcon function to update the tray icon's tooltip

23 Nov 2018, 14:52

Hello, the struct is defined in shellapi.h, like,

Code: Select all

typedef struct _NOTIFYICONDATAW {
    DWORD cbSize;
    HWND hWnd;
    UINT uID;
    UINT uFlags;
    UINT uCallbackMessage;
    HICON hIcon;
#if (NTDDI_VERSION < NTDDI_WIN2K)
    WCHAR  szTip[64];
#endif
#if (NTDDI_VERSION >= NTDDI_WIN2K)
    WCHAR  szTip[128];
    DWORD dwState;
    DWORD dwStateMask;
    WCHAR  szInfo[256];
    union {
        UINT  uTimeout;
        UINT  uVersion;  // used with NIM_SETVERSION, values 0, 3 and 4
    } DUMMYUNIONNAME;
    WCHAR  szInfoTitle[64];
    DWORD dwInfoFlags;
#endif
#if (NTDDI_VERSION >= NTDDI_WINXP)
    GUID guidItem;
#endif
#if (NTDDI_VERSION >= NTDDI_VISTA)
    HICON hBalloonIcon;
#endif
} NOTIFYICONDATAW, *PNOTIFYICONDATAW;
#if (NTDDI_VERSION >= NTDDI_VISTA) is false when I open AHK source in VS, so I put #if 0 to indicate it is not considered.

Note, regarding StrPut(Text, &NID + (A_PtrSize = 8 ? 40 : 24), 127),
strput wrote: If Length is less than the length of the converted string, the function fails and returns 0
So if you pass text with length 128 or more, you fail. Also,
If Length is exactly the length of the converted string, the string is not null-terminated; otherwise the returned count includes the null-terminator.
Hence, if you pass a string of length 127 and there happens to be something else than '\0' at character 128, your string is not properly null terminated. This doesn't happen in your code since you pass 0 to varsetcapacity's third parameter. To be safe, you can use strput like this, strput(substr(Text,1,127), &NID + (A_PtrSize = 8 ? 40 : 24), 128).

Thank you for the typo correction.

Cheers and happy thanksgiving :)

Return to “Ask for Help (v1)”

Who is online

Users browsing this forum: Bing [Bot], lechat and 139 guests