1

I'm making a library for Delphi to implement Windows 11 toast notifications easier using the interfaces from Winapi.UI.Notifications.

I want to to post custom notifications to the action center using a IToastNotifier to show the notifications and to be able to to receive events for when when the notification is dismissed or activated (clicked on). Thankfully, the IToastNotification has the add_Activated(), add_Dismissed() and add_Failed() methods to register a callback for when those events are triggered.

In C#, it's rather easy to add them, like so:

public void ShowToastNotification()
{
    // Construct the toast content with data bound fields
    var content = new ToastContentBuilder()
        .AddText("Notification title")
        .GetToastContent();

    // Generate the toast notification
    var toast = new ToastNotification(content.GetXml());
    toast.Activated += Toast_Activated;

    // Show the toast notification to the user
    ToastNotificationManager.CreateToastNotifier().Show(toast);
}

private void Toast_Activated(ToastNotification sender, object args)
{
    ShowMessageDialog($"Toast activated!");
}

To do the same thing in Delphi, I wrote the following code snippet:

unit Unit1;
interface

uses
  Winapi.Windows, Vcl.Forms, Winapi.ui.Notifications,
  Winapi.CommonTypes, Winapi.Winrt, Vcl.Dialogs, Winapi.DataRT;

type
  TToastActivatedHandler = class(TInterfacedObject, TypedEventHandler_2__IToastNotification__IInspectable)
    procedure Invoke(sender: IToastNotification; args: IInspectable); safecall;
  end;
  TToastDismissHandler = class(TInterfacedObject, TypedEventHandler_2__IToastNotification__IToastDismissedEventArgs)
    procedure Invoke(sender: IToastNotification; args: IToastDismissedEventArgs); safecall;
  end;

  TForm1 = class(TForm)
    procedure FormCreate(Sender: TObject);
  end;

var
  Form1: TForm1;
  ActivateHandle: TToastActivatedHandler;
  DismissHandle: TToastDismissHandler;

implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
var
  HStr: HSTRING;
  S:  string;
  Instance: IInspectable;
  Notifier: IToastNotifier;
begin
  // Notification maanger
  const xml = '<toast activationType="protocol">'+
  ' <visual>'+
  '     <binding template="ToastGeneric">'+
  '         <text>Hello world!</text>'+
  '         <text>This is a test notification.</text>'+
  '         <image src="C:\Windows\System32\@facial-recognition-windows-hello.gif" placement="hero" alt=""/>'+
  '     </binding>'+
  ' </visual>'+
  '</toast>';

  // Create XML
  S := 'Windows.Data.Xml.Dom.XmlDocument';
  WindowsCreateString(PChar(S), Length(S), HStr);
  try
    RoActivateInstance(HStr, Instance);
  finally
    WindowsDeleteString(HStr);
  end;

  // Load XML
  WindowsCreateString(PChar(xml), Length(xml), HStr);
  try
    (Instance as Xml_Dom_IXmlDocumentIO).LoadXml( HStr );
  finally
    WindowsDeleteString(HStr);
  end;

  // Create interfaces
  S := 'App.Test';
  WindowsCreateString(PChar(S), Length(S), HStr);
  try
    Notifier := TToastNotificationManager.CreateToastNotifier(HStr);
  finally
    WindowsDeleteString(HStr);
  end;
  const Notification = TToastNotification.CreateToastNotification( Instance as Xml_Dom_IXmlDocument );

  // Create activator
  ActivateHandle := TToastActivatedHandler.Create;
  DismissHandle := TToastDismissHandler.Create;

  // Prepare
  Notification.add_Activated( ActivateHandle );
  Notification.add_Dismissed( DismissHandle );

  // Show
  Notifier.Show( Notification );
end;

{ TToastActivatedHandler2 }
procedure TToastDismissHandler.Invoke(sender: IToastNotification;
  args: IToastDismissedEventArgs);
begin
  ShowMessage('Toast was dismissed!');
end;

{ TToastActivatedHandler }
procedure TToastActivatedHandler.Invoke(sender: IToastNotification;
  args: IInspectable);
begin
  ShowMessage('Toast was activated');
end;

end.

The following notification appears:

Image of shown notification

But the Invoke() method never gets called when I click on the notification, and I'm not sure why. I also tried the add_Dismissed() one, but the same problem occurs for It as well.

I'm completely out of ideas on what to try. There must be a way to have the invoke method called, but I'm unsure on how this may be accomplished.

Note: The unit file is not needed to run the code above, I only took the snippets from It that are needed to show this example. But the full library can be found on GitHub here.

1 Answer 1

1

I've figured out the issue!

It seems that interfaces rely very heavily on their serialized GUIDs, and without the proper IDs added to a class, the registration will fail.

Since TypedEventHandler_2__IToastNotification__IToastDismissedEventArgs is a child interface of TypedEventHandler_2__IToastNotification__IToastDismissedEventArgs_Delegate_Base, I assumed It would inherit It's GUID as well, but It seems that's not the case.

The fix is really easy to apply, just add the TypedEventHandler_2__IToastNotification__IInspectable_Delegate_Base and TypedEventHandler_2__IToastNotification__IToastDismissedEventArgs_Delegate_Base interface classes to the TToastActivatedHandler and TToastDismissedHandler object respectively.

Here is the correct declaration:

TToastActivatedHandler = class(TInterfacedObject, TypedEventHandler_2__IToastNotification__IInspectable,
    TypedEventHandler_2__IToastNotification__IInspectable_Delegate_Base)
  public
    procedure Invoke(sender: IToastNotification; args: IInspectable); safecall;
  end;
  TToastDismissedHandler = class(TInterfacedObject, TypedEventHandler_2__IToastNotification__IToastDismissedEventArgs,
    TypedEventHandler_2__IToastNotification__IToastDismissedEventArgs_Delegate_Base)
  public
    procedure Invoke(sender: IToastNotification; args: IToastDismissedEventArgs); safecall;
  end;
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.