Lazarus

Programming => General => Topic started by: pcurtis on October 06, 2021, 06:35:58 pm

Title: Shell_NotifyIconW
Post by: pcurtis on October 06, 2021, 06:35:58 pm
How do I use Shell_NotifyIconW to put a message in the notification box on Win 10
Title: Re: Shell_NotifyIconW
Post by: Remy Lebeau on October 06, 2021, 08:28:31 pm
Is there something in the documentation that is unclear to you?

Notifications and the Notification Area (https://docs.microsoft.com/en-us/windows/win32/shell/notification-area)

Shell_NotifyIconW function (https://docs.microsoft.com/en-us/windows/win32/api/shellapi/nf-shellapi-shell_notifyiconw)

NotificationIcon Sample (https://docs.microsoft.com/en-us/previous-versions/windows/desktop/legacy/dd940367(v=vs.85))

Can you please clarify your issue?
Title: Re: Shell_NotifyIconW
Post by: pcurtis on October 06, 2021, 08:56:29 pm
Im not a 'C' guy and I would need help to implement / translate it to FP

On Win 10 I have a notification app icon (see attached pic). I just want to put messages there
Title: Re: Shell_NotifyIconW
Post by: Remy Lebeau on October 06, 2021, 09:36:48 pm
Im not a 'C' guy and I would need help to implement / translate it to FP

Have you searched this forum (https://forum.lazarus.freepascal.org/index.php?action=search2;params=eJwtkEFOw0AMRe_Chs1bZDwee3IENmw4QNQmg1oUGpQGUKUcHlN1Y31_P32P5zD9HC5jm_bnPe1P-3G9q4p1pB5J5EQyilMquacU1MgVEcSREIYEqUihBN8hmSxkRzMl4IwWspKjGiookdKhESeUgJSeFBuFFOsyljDBQihWMMMcqySCKCQnkxRNqBMTKiXy4tk9nvAOFzzjihfc8MAq3lO7uPJ6Wn6Hcfn8mtvW4t5_6_v40cZtWC7z7eEs6zZM5zW6qV3HhxPd2uZ2_7R_qx3W8RTm26nN8_C6bOf328u4XP4AJtxboA..) yet? There are several discussions showing how to use Shell_NotifyIcon/W() in FreePascal.  For example:

Can Lazarus make a system tray app (https://forum.lazarus.freepascal.org/index.php/topic,9467.0.html)
Title: Re: Shell_NotifyIconW
Post by: pcurtis on October 07, 2021, 08:54:50 am
None of the search results from this site work  :( (or at least not for me)
Title: Re: Shell_NotifyIconW
Post by: Remy Lebeau on October 07, 2021, 07:39:37 pm
None of the search results from this site work  :( (or at least not for me)

In what way exactly? Please show your actual code that you are having trouble with, and show any compiler errors you are getting.
Title: Re: Shell_NotifyIconW
Post by: pcurtis on October 07, 2021, 08:01:29 pm
So I used this sample https://forum.lazarus.freepascal.org/index.php/topic,9467.msg46442.html#msg46442 (https://forum.lazarus.freepascal.org/index.php/topic,9467.msg46442.html#msg46442)
It compiles and runs but does nothing (yes I included Application.ShowMainForm := false;)
Title: Re: Shell_NotifyIconW
Post by: Remy Lebeau on October 08, 2021, 05:56:27 pm
So I used this sample https://forum.lazarus.freepascal.org/index.php/topic,9467.msg46442.html#msg46442 (https://forum.lazarus.freepascal.org/index.php/topic,9467.msg46442.html#msg46442)
It compiles and runs but does nothing (yes I included Application.ShowMainForm := false;)

Offhand, the code looks OK.  Are you checking the return value of Shell_NotifyIcon() for failure?

Also, which version of Windows are you testing with?  Are you taking into account that modern Windows versions do not display notification icons near the clock unless the user chooses to display them there?  They may be in an overflow area instead, or even hidden completely.  Did you check the Taskbar config to make sure your icon is not being hidden?

Also, are you taking into account that in Windows 10+ (or was in 7+?), Shell_NotifyIcon() notifications are displayed in the Action Center instead of on the Taskbar?
Title: Re: Shell_NotifyIconW
Post by: pcurtis on October 11, 2021, 01:24:15 pm
OK this shows the message + balloon. How do I stop the balloon from showing?

Code: Pascal  [Select][+][-]
  1. type
  2.  
  3.   TNotifyIconDataW2 = record
  4.     cbSize: DWORD;
  5.     hWnd: HWND;
  6.     uID: UINT;
  7.     uFlags: UINT;
  8.     uCallbackMessage: UINT;
  9.     hIcon: HICON;
  10.     szTip: array [0..127] of WideChar;
  11.     dwState: DWORD;
  12.     dwStateMask: DWORD;
  13.     szInfo: array [0..255] of WideChar;
  14.     u: record
  15.          case longint of
  16.            0 : ( uTimeout : UINT );
  17.            1 : ( uVersion : UINT );
  18.           end;
  19.     szInfoTitle: array[0..63] of WideChar;
  20.     dwInfoFlags: DWORD;
  21.   end;    
  22.  
  23. const
  24.   uIDTrayIcon = 25;
  25.  
  26. function WideStrLCopy(dest, source: PWideChar; maxlen: SizeInt): PWideChar;
  27. var
  28.   counter: SizeInt;
  29. begin
  30.   counter := 0;
  31.  
  32.   while (Source[counter] <> #0)  and (counter < MaxLen) do
  33.   begin
  34.     Dest[counter] := Source[counter];
  35.     Inc(counter);
  36.   end;
  37.  
  38.   { terminate the string }
  39.   Dest[counter] := #0;
  40.   Result := Dest;
  41. end;  
  42.  
  43. function ShowBalloonHint(const ATrayIcon: TCustomTrayIcon): Boolean;
  44. const
  45.   FlagsMap: array[TBalloonFlags] of dword = (NIIF_NONE, NIIF_INFO, NIIF_WARNING, NIIF_ERROR);
  46. var
  47.   NotifyData: TNotifyIconDataW2;
  48.   w: WideString;
  49. begin
  50.   NotifyData.cbSize:=SizeOf(NotifyData);
  51.   NotifyData.hWnd := ATrayIcon.Handle;
  52.   NotifyData.uID := uIDTrayIcon;
  53.   NotifyData.uFlags:=NIF_INFO;
  54.   NotifyData.u.uTimeout:=ATrayIcon.BalloonTimeout;
  55.   UTF8ToUTF16(ATrayIcon.BalloonHint);
  56.   WideStrLCopy(@NotifyData.szInfo, PWideChar(w), High(NotifyData.szInfo));
  57.   w:=UTF8ToUTF16(ATrayIcon.BalloonTitle);
  58.   WideStrLCopy(@NotifyData.szInfoTitle, PWideChar(w), High(NotifyData.szInfoTitle));
  59.   NotifyData.dwInfoFlags:=FlagsMap[ATrayIcon.BalloonFlags];
  60.  
  61.   Result:= Shell_NotifyIconW(NIM_MODIFY, @NotifyData);
  62. end;
  63.  
Title: Re: Shell_NotifyIconW
Post by: Remy Lebeau on October 11, 2021, 07:34:57 pm
OK this shows the message + balloon. How do I stop the balloon from showing?

https://stackoverflow.com/a/902804/65863

Quote
To immediately hide a balloon, set the szInfo member of the NOTIFYICONDATA to an empty string, like nid.szInfo[0] = 0; and call Shell_NotifyIcon( NIM_MODIFY, &nid ).
Title: Re: Shell_NotifyIconW
Post by: pcurtis on October 11, 2021, 08:02:52 pm
I tried this

Code: Pascal  [Select][+][-]
  1.   w:='';
  2.   WideStrLCopy(@NotifyData.szInfo, PWideChar(w), High(NotifyData.szInfo));  
  3.  

and it doesn't work
Title: Re: Shell_NotifyIconW
Post by: sstvmaster on October 11, 2021, 09:08:54 pm
maybe?
Code: Pascal  [Select][+][-]
  1. ...
  2. NotifyData.szInfo[0] := WideChar(0);
  3. ...
  4. Shell_NotifyIconW(NIM_MODIFY, @NotifyData);
  5.  
Title: Re: Shell_NotifyIconW
Post by: pcurtis on October 11, 2021, 10:18:37 pm
Still doesn't work
Title: Re: Shell_NotifyIconW
Post by: pcurtis on October 12, 2021, 07:30:09 am
Here's the full source = it shows the tray icon. When you click it it shows the app. When you click the button
it shows the notification, but still shows the balloon. If I uncomment NotifyData.szInfo[0] := #0; (line 130)  then nothing shows.

Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, Forms, Controls, Graphics, Dialogs, ExtCtrls, StdCtrls,
  9.   Windows, ShellAPI, LazUTF8;
  10.  
  11. const
  12.   uIDTrayIcon = 25;
  13.   WM_ICONTRAY = WM_USER + uIDTrayIcon;
  14.  
  15. type
  16.  
  17.   TNotifyIconDataW2 = record
  18.     cbSize: DWORD;
  19.     hWnd: HWND;
  20.     uID: UINT;
  21.     uFlags: UINT;
  22.     uCallbackMessage: UINT;
  23.     hIcon: HICON;
  24.     szTip: array [0..127] of WideChar;
  25.     dwState: DWORD;
  26.     dwStateMask: DWORD;
  27.     szInfo: array [0..255] of WideChar;
  28.     u: record
  29.          case longint of
  30.            0 : ( uTimeout : UINT );
  31.            1 : ( uVersion : UINT );
  32.           end;
  33.     szInfoTitle: array[0..63] of WideChar;
  34.     dwInfoFlags: DWORD;
  35.   end;
  36.  
  37.   { TForm1 }
  38.  
  39.   TForm1 = class(TForm)
  40.     Button1: TButton;
  41.     procedure Button1Click(Sender: TObject);
  42.     procedure FormDestroy(Sender: TObject);
  43.     procedure FormShow(Sender: TObject);
  44.   private
  45.     function AddIcon : Boolean;
  46.     function ShowMessageN : Boolean;
  47.     procedure TrayMessage(var Msg: TMessage); message WM_ICONTRAY;
  48.   public
  49.  
  50.   end;
  51.  
  52. var
  53.   Form1: TForm1;
  54.   bFirst  : Boolean = True;
  55.   NotifyIcon: TNotifyIconDataW2;
  56.  
  57. implementation
  58.  
  59. {$R *.lfm}
  60.  
  61. { TForm1 }
  62.  
  63. function WideStrLCopy(dest, source: PWideChar; maxlen: SizeInt): PWideChar;
  64. var
  65.   counter: SizeInt;
  66. begin
  67.   counter := 0;
  68.  
  69.   while (Source[counter] <> #0)  and (counter < MaxLen) do
  70.   begin
  71.     Dest[counter] := Source[counter];
  72.     Inc(counter);
  73.   end;
  74.  
  75.   Dest[counter] := #0;
  76.   Result := Dest;
  77. end;
  78.  
  79. procedure TForm1.Button1Click(Sender: TObject);
  80. begin
  81.   ShowMessageN;
  82. end;
  83.  
  84. procedure TForm1.FormDestroy(Sender: TObject);
  85. begin
  86.   Windows.Shell_NotifyIcon(NIM_DELETE, @NotifyIcon);
  87. end;
  88.  
  89. procedure TForm1.FormShow(Sender: TObject);
  90. begin
  91.   if bFirst then
  92.   begin
  93.     Hide;
  94.     bFirst := False;
  95.   end;
  96.  
  97.   AddIcon;
  98. end;
  99.  
  100. procedure TForm1.TrayMessage(var Msg: TMessage);
  101. begin
  102.   case Msg.lParam of
  103.       WM_LBUTTONDOWN : begin
  104.                          Show;
  105.                        end;
  106.       WM_RBUTTONDOWN : begin
  107.                          Hide;
  108.                        end;
  109.     end;
  110. end;
  111.  
  112. function TForm1.ShowMessageN : Boolean;
  113. const
  114.   FlagsMap: array[TBalloonFlags] of dword = (NIIF_NONE, NIIF_INFO, NIIF_WARNING, NIIF_ERROR);
  115. var
  116.   NotifyData: TNotifyIconDataW2;
  117.   w: WideString;
  118. begin
  119.   NotifyData.cbSize:=SizeOf(NotifyData);
  120.   NotifyData.hWnd := Self.Handle;
  121.   NotifyData.uID := uIDTrayIcon;
  122.   NotifyData.uFlags:=NIF_INFO;
  123.  
  124.   w:=UTF8ToUTF16('BalloonHint');
  125.   WideStrLCopy(@NotifyData.szInfo, PWideChar(w), High(NotifyData.szInfo));
  126.  
  127.   w:=UTF8ToUTF16('BalloonTitle');
  128.   WideStrLCopy(@NotifyData.szInfoTitle, PWideChar(w), High(NotifyData.szInfoTitle));
  129.   NotifyData.dwInfoFlags:=FlagsMap[bfInfo];
  130.  // NotifyData.szInfo[0] := #0; // < This doesn't work
  131.   Result := Shell_NotifyIconW(NIM_MODIFY, @NotifyData);
  132. end;
  133.  
  134. function TForm1.AddIcon : Boolean;
  135. var
  136.   WideBuffer: widestring;
  137. begin
  138.   FillChar(NotifyIcon, SizeOf(NotifyIcon), 0);
  139.   NotifyIcon.cbSize := SizeOf(NotifyIcon);
  140.   NotifyIcon.hWnd := Self.Handle;
  141.   NotifyIcon.uID := uIDTrayIcon;
  142.   NotifyIcon.uFlags := NIF_MESSAGE or NIF_ICON;
  143.   NotifyIcon.uFlags := NotifyIcon.uFlags or NIF_TIP;
  144.   NotifyIcon.uCallbackMessage := WM_USER + uIDTrayIcon;
  145.   NotifyIcon.hIcon := Application.Icon.Handle;
  146.  
  147.   WideBuffer := UTF8ToUTF16('Hint');
  148.   WideStrLCopy(@NotifyIcon.szTip, PWideChar(WideBuffer), 127);
  149.  
  150.   Result := Shell_NotifyIconW(NIM_ADD, @NotifyIcon);
  151. end;
  152.  
  153. end.
  154.  
  155.  
Title: Re: Shell_NotifyIconW
Post by: dbannon on October 12, 2021, 12:47:23 pm

pcurtis, this may, for what ever reason be a silly suggestion and if so, I apologize. But the standard Lazarus TrayIcon appears to my (windows uneducated) eyes do what you want to do, "out of the box".  Have you done one of -

a. Considered taking the easy way out and using it, "Additional", roughly the middle component.

b. Having a look through the Lazarus component's code to see how its done there ?

Davo
Title: Re: Shell_NotifyIconW
Post by: pcurtis on October 12, 2021, 01:00:15 pm
Show me the code how to get the notification without the balloon using trayicon
Title: Re: Shell_NotifyIconW
Post by: dbannon on October 12, 2021, 01:39:06 pm
maybe -

Code: Pascal  [Select][+][-]
  1. procedure TForm1.Button1Click(Sender: TObject);
  2. begin
  3.     TrayIcon1.ShowBalloonHint := 'Hello, I am a balloon hint');
  4.     TrayIcon1.BalloonTimeout := 50;                    // thats mS
  5.     TrayIcon1.ShowBalloonHint;
  6. end;    

Tested on Linux were it produces a very ugly and obtrusive oversized yellow box (and I use libnotifier instead), 50mS is just noticable, 10mS is not but I assume the event is still fired.  I allow myself only one boot into windows each day and I did that earlier to see if it works through the windows notifier system (yes, it does). 

I suggest you try it yourself.

Davo
Title: Re: Shell_NotifyIconW
Post by: pcurtis on October 12, 2021, 02:34:46 pm
I have, and it doesn't work
Title: Re: Shell_NotifyIconW
Post by: balazsszekely on October 12, 2021, 09:25:14 pm
@pcurtis
On Win10 you have to enable balloon hints firts. You can do it with the Group Policy Editor.
Title: Re: Shell_NotifyIconW
Post by: pcurtis on October 12, 2021, 09:34:10 pm
they are enabled. i want to stop them programmatically

attached is a screenshot of a balloon
Title: Re: Shell_NotifyIconW
Post by: dbannon on October 13, 2021, 02:31:37 am
I have, and it doesn't work

Well, its not clear at all to me just what 'it' is. 
As I understand it, you are talking about W10's ability to alert the user of some minor event. First we see a slide out box from the right side of screen, it goes away in a few seconds, then the user can go looking for that notification and see it in a larger, ad filled slide out ?

I understand you want the notification available if the user goes looking but do not want the slide out to appear ?

So, when you say "it doesn't work", just what does not work ?  Do you see the initial slideout for the full 3 seconds and cannot reduce that ?  Is the notification not recorded at all ? Does smoke come out of your computer ?

Your average word count is about ten words per post in this thread, can you be  little more expansive ?

Davo


Title: Re: Shell_NotifyIconW
Post by: pcurtis on October 13, 2021, 05:00:25 am
Yes I want to stop the slide out from appearing (hide the balloon).
Title: Re: Shell_NotifyIconW
Post by: balazsszekely on October 13, 2021, 08:44:54 am
@pcurtis

You seem to confuse Balloon notifications with Toast notifications.

1. Balloon notifications
If you wish to enable Balloon notifications on Win10, press "Windows Key + R", then type "gpedit.msc" + enter. After the "Group Policy Editor" appears, follow the instructions from the first screenshot. Please note that in order for the changes to take effect a restart is needed. Balloon notification is a well documented and tested feature. You can find a detailed Delphi implementation here:
   https://github.com/coolshou/CoolTrayIcon/blob/master/Source/CoolTrayIcon.pas
Search for ShowBalloonHint and HideBalloonHint functions. It shouldn't take to much time to convert it to Lazarus. Actually I tested the code on Win10 and it works fine:
   https://youtu.be/MRdoWBYSDcI

2. Toast notification
This is the default notification in Win10, but you must also enable it in "Notifications & actions", by switching "Get notifications from apps and other senders" to "On". The same code(see above) will show a toast notification in the right panel, however it does not hide it(see second screenshot). You should do a google search: "hide toast notification" or "hide toast notification with delphi", maybe the API has changed.
Title: Re: Shell_NotifyIconW
Post by: pcurtis on October 13, 2021, 11:18:24 am
OK I wan't to stop the slide out dialog.

Quote
You can find a detailed Delphi implementation here:
   https://github.com/coolshou/CoolTrayIcon/blob/master/Source/CoolTrayIcon.pas
Search for ShowBalloonHint and HideBalloonHint functions. It shouldn't take to much time to convert it to Lazarus. Actually I tested the code on Win10 and it works fine:
   https://youtu.be/MRdoWBYSDcI
 
Thats above my level. Thats why I asked here for help
Title: Re: Shell_NotifyIconW
Post by: balazsszekely on October 13, 2021, 01:37:03 pm
@pcurtis

I ran a few tests with Delphi 11 Alexandria, which supports the Win10 notification center via TNotificationCenter/TNotification classes. Showing a toast message is quite simple:
Code: Pascal  [Select][+][-]
  1. var
  2.   Notification: TNotification;
  3. begin
  4.   Notification := NotificationCenter.CreateNotification;
  5.   try
  6.     Notification.Name := 'myNotification';
  7.     Notification.Title := 'Notification';
  8.     Notification.AlertBody := 'Test test test';
  9.     NotificationCenter.PresentNotification(Notification);
  10.   finally
  11.     MyNotification.Free;
  12.   end;
  13. end;
The above code works fine.

According to de documentation, the following code should remove the notification:
Code: Pascal  [Select][+][-]
  1. NotificationCenter.CancelAll;
  2.  
  3. {Use CancelAll to cancels all notifications:
  4. - The notifications that are scheduled will not be fired.
  5. - The notifications, that belong to this application, and are available in the notification center or notification drawer are also cancelled and removed.}
Unfortunately it does not work, something fishy is happening here.

Quote
Thats above my level. Thats why I asked here for help
I don't have time to translate that code to Lazarus, but as you can see it won't work with toast notification. Even the dedicated components from Delphi does not work and I have no idea why.
Title: Re: Shell_NotifyIconW
Post by: dbannon on October 13, 2021, 02:06:51 pm
> Unfortunately it does not work, something fishy is happening here.

Well, I had a poke around the Lazarus TrayIcon code, its identical to the block of code circulating on this thread ending with Shell_NotifyIconW(NIM_MODIFY, @NotifyData);

pcurtis's "does not work" could be translated to "if you set the NotifyData.szInfo[0] := 0 it does not compile (obviously) and if you set it to WideChar(0); it compiles and runs fine but no notification is recorded by Windows.".

Clearly, some time in the past, setting NotifyData.szInfo must have "worked" and setting the timeout (which is ignored) must have done what we'd expect. That says to me that MS have redefined the API, probably in a effort to make the notifier experience more uniform. They don't want different applications doing different things, if you send a notification expect a "banner" (thats the small slider) to be visible for 3-4 seconds (as long as the user has not turned the banner off).

Incidentally, on my Windows install I have changed almost none of the settings, by default, that banner or Toaster as Getmem calls it, is enabled for all apps.

pcurtis, what I suggest you do is accept the banner is going to appear. You can however determine what it says, so, how about settings it's title to the name of some Windows Service ?  The users who find it really annoying will blame that service and not your app !

Davo
Title: Re: Shell_NotifyIconW
Post by: balazsszekely on October 13, 2021, 02:22:22 pm
@dbannon
Quote
that banner or Toaster as Getmem calls it, is enabled for all apps.
I'm afraid this is the official name :):
https://docs.microsoft.com/en-us/windows/apps/design/shell/tiles-and-notifications/adaptive-interactive-toasts?tabs=builder-syntax
https://docs.microsoft.com/en-us/windows/apps/design/shell/tiles-and-notifications/toast-desktop-apps
https://docs.trendmicro.com/all/ent/officescan/v11.0/en-us/osce_11.0_sp1_cp_agent_olh/Enabling-Toast-Notif1.html

Perhaps because it looks like a toast or you're toasted and cannot hide the message.
Title: Re: Shell_NotifyIconW
Post by: pcurtis on October 13, 2021, 02:48:49 pm
https://docs.microsoft.com/en-us/windows/win32/api/shellapi/ns-shellapi-notifyicondataa

NIF_INFO (0x00000010)

0x00000010. Display a balloon notification. The szInfo, szInfoTitle, dwInfoFlags, and uTimeout members are valid. Note that uTimeout is valid only in Windows 2000 and Windows XP.

    To display the balloon notification, specify NIF_INFO and provide text in szInfo.
    To remove a balloon notification, specify NIF_INFO and provide an empty string through szInfo.
    To add a notification area icon without displaying a notification, do not set the NIF_INFO flag.

Does this mean in W10 no can do?
Title: Re: Shell_NotifyIconW
Post by: balazsszekely on October 13, 2021, 03:05:51 pm
Quote
To display the balloon notification, specify NIF_INFO and provide text in szInfo.
To add a notification area icon without displaying a notification, do not set the NIF_INFO flag.
Works with xp, win7(balloon) and win8, win10(balloon + toast).

Quote
To remove a balloon notification, specify NIF_INFO and provide an empty string through szInfo.
That's exactly what cooltrayicon does:
Code: Pascal  [Select][+][-]
  1. function TCoolTrayIcon.HideBalloonHint: Boolean;
  2. // Hide balloon hint. Return false if error.
  3. begin
  4.   with IconData do
  5.   begin
  6.     uFlags := uFlags or NIF_INFO;
  7.     StrPCopy(szInfo, '');
  8.   end;
  9.   Result := ModifyIcon;
  10. end;
Works with xp, win7, win8, win10(balloon),  does not work with win10 toast notifications. I have nothing more to say, Picard out.
Title: Re: Shell_NotifyIconW
Post by: dbannon on October 14, 2021, 12:39:11 am
Quote
that banner or Toaster as Getmem calls it, is enabled for all apps.
I'm afraid this is the official name :):

I respectfully suggest that Windows users (as opposed to developers) are more likely to know it as the Notification Banner. But I do like your reasoning for the name "Toast"   :D

TinyPortal © 2005-2018