Describe the bug
Before I start: I don't think this is the right place for this perceived DesktopBridge bug. I wanted to give Microsoft Q&A a try but I cannot attach a minimal repro app to a post over there (no .zip, .rar, etc... file types allowed, only xml, gif, png, jpg, jpeg, pdf, txt.). I'm afraid that leaves me without any other place I have confidence in getting people at MS to look at this issue. I hereby apologize to the WinUI team for choosing this repo and humbly ask for their understanding. Thank you.
Now the actual issue:
I'm working on a MSIX-packaged WPF app and I want to use native Windows 10 notifications. Clicking on a notification should activate the app (or if no app instance is running: create and start a new app instance).
The documentation states that there two ways for DesktopBridge apps to activate the app on notification click: COM Activation and "No COM / Stub CLSID". In my case, I don't need COM activation so I went with the second option. Here, the documentation states:
For Desktop Bridge apps, just send toast notifications like a UWP app would. When the user clicks on your toast, your app will be command line launched with the launch args that you specified in the toast.
This doesn't appear to be the case. I created a minimal repro app attached below which fires a notification when clicking on an in-app button. The notification contains an additional action button:
When using the app: Observe that no matter if the app is running or not, neither clicking the toast nor the action button activates the app. It also makes no difference if you interact with the notification in the Action Center or outside of it.
All examples I found so far about toast notifications activating Desktop Bridge apps use the COM Activator (option 1). I have not found any example for the second option listed in the docs.
Expected behavior
When clicking the notification (or the action button) I expect a new app instance to be launched with the specified arguments in the toast content schema as stated in the documentation.
| Windows 10 version | Saw the problem? |
| :--------------------------------- | :-------------------- |
| Insider Build (xxxxx) | |
| November 2019 Update (18363) | Yes |
| May 2019 Update (18362) | |
| October 2018 Update (17763) | |
| April 2018 Update (17134) | |
| Fall Creators Update (16299) | |
| Creators Update (15063) | |
Attached files
DesktopBridgeToastActivation.zip
Hey @Felix-Dev,
There was a regression in Toast activation of a Desktop Bridge app in the November 2019 Update (18363). I confirmed that your app is working on my insider build (19041).
Thanks for your repro case - super helpful!
-jw
@jsidewhite Thanks for the fast reply! I confirmed it in my VM running 19041 as well now. Both activation cases work (clicking on the notification and clicking on the action button) 馃檪
Any chance this will be fixed for the November 2019 Update (I asume it does work on 1903 and below)? I currently target 1809 and above for my MSIX packaged app and it would be nice to use native toast notifications for it without having to use the COM activation method. The out-of-the-box Desktop Bridge support is much simpler!
It was fixed in build 18967, so that won't make it to the November 2019 Update (which is build 18363), sadly.
There's no plan, currently, to service the fix to builds predating 18967, because we had no customer data suggesting people were impacted at the time the fix was made - however - I've forwarded your request to our servicing lead. Will update if anything changes. Fwiw, I'm adding a test to prevent toast-activation regressions in the future.
@jsidewhite Thank you for your reply and for passing on my request :)
In the meantime, I will likely use COM Activation then as the working alternative.
@jsidewhite Ok, this has the potential to be quite frustrating....
I went with option 1 as said previously (using the COM Activator) because the app activation behavior by clicking on a toast notification is vital for the app experience I want to create. The helper implementation provided on the web uses the System.Runtime.InteropServices.RegistrationServices API which turns out is NOT available on .NET Core 3 apps! I also have not found any workaround so far to address this issue.
What this means is: I currently cannot use native toast notifications for my NET Core 3 MSIX app because I need the toast notification app activation feature. In light of this finding, I would like to ask you, @jsidewhite, to please report this back to your servicing lead and check if there's any possibility the fix can be backported to affected OS versions (I would be fine with OS versions 1903 and later). Alternatively, if that just isn't an option for Microsoft, please ask around if there is a workaround for the missing System.Runtime.InteropServices.RegistrationServices
API on .NET Core 3.
Right now I'm stumped and somewhat frustrated.
PS: It should be pointed out in the docs - if confirmed - that toast notification app activation does not work for .NET Core 3 desktop apps on at least Windows 10 1909.
PPS: A workaround available in theory would be to switch back to the .NET Framework where the RegistrationServices
API is available. That would require significant time investments though as my app in question has been designed from the ground up with .NET Core 3.
Thanks for the feedback @Felix-Dev, and apologies for the frustration. I'm escalating the servicing priority.
@jsidewhite No worries and thank you for your help. Much appreciate it (no matter the result) 馃檪
I just want to add my experience in the development of a business app using UWP notifications so far. Maybe this is a help for someone.
I also came to the point where I realized activating a desktop app (.NET Framework, no MSIX, just running an exe) using the COM activation callback when clicking on a UWP notification worked completely unreliable. Sometimes it worked without any problem and then there were times it did not work at all without any change in code. So I switched completely away from using COM activation.
The alternative I came up with was registering a custom protocol handler in the registry that will invoke the executable passing URIs defined in the notification XML as argument. To enable this it is just necessary to set activationType="protocol"
in the notification XML.
Finally the recently started executable checks whether there is already a running instance of the executable and passes the notification information to the running instance using IPC and then terminates.
This works completely reliable for me and also does not need RegistrationServices
, so it should be no problem using this approach from .NET Core.
If wished I can add code snippets for more details.
@FrecherxDachs Thanks for the input! It sounds like an approach I could try out (I'm familiar with the second app instance being launched and having to send some data to the primary app instance then exiting because of Win32 Jumplists). If I happen to need to look at some code snipptes for help I will gladly ask you :)
Edit: It works :) Since I'm using an MSIX I don't even have to write to the registry manually but can simply register a protocol URI using Visual Studio. Very simple and yes, it seems to be very reliable so far.
Thanks a lot again @FrecherxDachs and @jsidewhite for all your help! I think we can mention the protocol activation approach as a working solution in the docs. Given that this approach works reliably I think you guys can take it easier now with the servicing of the fix to lower Windows versions 馃榿. It would still be nice to have it but I can definitely live with the protocal activation solution!
@FrecherxDachs I seem to be in a pinch again. Using protocol activation I can launch a second app instance just fine, the issue is I currently don't know how to bring my primary ap instance to the foreground with focus. I am calling AllowSetForegroundWindow from my second app instance and passing it the process ID of my primary app instance. The function returns success but attempting to then set the foreground window via SetForegroundWindow still fails (function returns failure).
I then proceeded to obtain the ID of the actual foreground process right before calling SetForegroundWindow
and got told that it's apparently the ShellExperienceHost
process. Any attempts to attach my primary app instance's window thread to the foreground thread also fail (to be expected in light of the ShellExperienceHost finding).
Did you manage to bring your primary app instance to the foreground and give it focus? If yes, how?
PS: I briefly got the idea to have my secondary app instance create an (invisible) window in the hope that it will be the foreground app then so that AllowSetForegroundWindow
would then work. It is starting to get "too complicated" for my taste though.
PPS: Apparently Interaction.AppActivate is a method which always works but it's not available to .NET Core 3 apps unfortunately.
@Felix-Dev Sorry for the delay, I was busy the last days.
If I get it right, the main problem seems to be the restrictions by the OS regarding to bringing some window in the foreground.
In our solution we use Remoting / IpcChannel to call into the primary app and then we use WPF APIs to bring the window into the foreground. I'm not quite sure why this solution works, but your solution does not. But since SetForegroundWindow mentions several remarks, I expect you hit one of those. For some reason calling the window APIs from the primary instance seem to work here.
Remoting does not exist in .NET Core, but if you want to try this approach for your app, you could use SendMessage to notify the primary app window by sending a custom message (anything beyond WM_USER) and then try to bring this window into foreground from the primary process itself.
@FrecherxDachs I'm already using SendMessage
to call back into the primary process itself. Here's an overview of my current process. Note that when a notification arrives, my app might be in the background (so calls to SetForegroundWindow
won't work). In light of this, here's the approach:
User clicks on a notification -> a second app instance is launched
AllowSetForegroundWindow
in case it has foreground right and delegates this right to the primary app instance (if it does not have foreground right, this call won't prevent the primary app instance from launching a foreground window).SendMessage
to send a custom message (i.e. WM_SHOWACTIVATEINSTANCE) to the main window of the primary app instance.WndProc
function and listens for WM_SHOWACTIVATEINSTANCE. It handles this message by then calling the Win32 function SetForegroundWindow
or the WPF variant Window.Activate. Sadly neither call in (3) works because the primary app instance is lacking rights to activate its main window (i.e. Application.MainWindow.Activate() returns false).
One of the conditions listed in the docs for SetForegroundWindow
(which also apply to WPF's Windw.Activate
) is the following:
The process is the foreground process.
Since I'm nowhere in this approach bringing my primary app's process from the background to the foreground I assume that's the root of the issue. I will have to hit the docs to see if there's an API for that.
Which WPF APIs are you calling as soon as you have called into the primary app? I checked the documentation you linked and I'm under the impression that at the time when you called back into the primary app instance, that instance should also still be running in the background potentially.
Edit: I'm using the same code to bring my app to the foreground when the user launches the app again via the Start menu. It works fine there....which would suggest there is a difference in the system when I click on a toast notification to activate the app instead of the clicking the app tile/entry in the Start menu. After all, I click on the app entry in the Start menu (so the app will default to foreground) whereas the notification click is a programmatic protocol activation.
Ok, so after some more testing protocol launching a desktop bridge app via a toast notification just doesn't get focus (focus is in the shell). If I launch a UWP app like Settings via the toast, the app gets focus just fine. Anyway, I'm using Window.TopMost to bring the window of my app to the foreground again (and then unset Window.TopMost again). While not in focus this will work for me.
However, the problem of COM activation not available on .NET Core 3 and in-built toast activation for desktop bridge apps not working on 1909 (due to a regression) still exists. I am thinking about adding selection input to my toast. Missing COM Activator is okay, because in 20H1, in-built activation works and I can get the ToastNotificationActivatedArgs containing the toast arguments and user input via AppInstance.GetActivatedEventArgs just fine. I cannot use selection input in my app though if I want to target Windows 10 1909.
@jsidewhite
TL;DR: Currently, on Windows 10 1909, there is no way for .NET Core 3 desktop bridge apps to create toasts where the user can provide input (quick reply textbox, selection input). Backporting the fix shipped in 20H1 would enable this scenario as API support is available (using AppInstance.GetActivatedEventArgs()). It will allow desktop bridge apps targeting 1909 to create fully featured toast notification experiences just as can be done currently on 20H1.
cc @FrecherxDachs
@Felix-Dev I have created a sample that uses COM activation in an unpackaged .NET Core 3 application and simply prints out the notification arguments and brings the main window into foreground.
I hope this is a help somehow: GitHub sample
_The source code might be a bit quick and dirty, though_ 馃檮
@FrecherxDachs Thanks for the sample code! I will carefully look though it the following days. I'm already seeing that for it to work in unpackaged .NET Core 3 apps quite a bit of code has to be added. Perhaps some of it can be removed for packaged apps to get a leaner workaround solution (i.e. the shortcut part)馃槄
Appreciate your help!
Most helpful comment
Hey @Felix-Dev,
There was a regression in Toast activation of a Desktop Bridge app in the November 2019 Update (18363). I confirmed that your app is working on my insider build (19041).
Thanks for your repro case - super helpful!
-jw