UWP app with Systray extension

A common piece of feedback from folks exploring the move from Win32/NET to UWP is about the lack of Systray (aka notification area) support in UWP. It is true that UWP code can’t run in the Systray, however a UWP package can contain a Win32/NET component that can put itself into the Systray. The UWP package will still be universal (run on Surface Hub, Windows Phone, etc.), and light up the PC-specific Systray functionality on the desktop edition only. In this post I will explain how this can be accomplished on Windows 10.

Capture

TL;DR
See source code on GitHub and install the demo app from the Windows Store:
Get it on Windows 10

Details
First step is adding a WinForms project to your UWP solution. It can be done in C++/Win32 as well, but personally I find WinForms to be the most convenient framework for legacy Systray component development. Next add a class file (in my case called ‘SystrayApplicationContext.cs’) to implement all your systray functionality.

SolutionExplorer

To connect the UWP app and the WinForms systray component, we will be using the following two UWP platform features on Windows 10: FullTrustProcessLauncher and AppService. For the former we need to add a reference to the Windows 10 Desktop Extensions SDK:

AddReference

Now we can declare the “windows.fullTrustProcess” and “windows.AppService” extensions in the manifest for our UWP app package. Note also the two capability declarations. The first one is needed for launching the WinForms code from the UWP, the second one we will need in order to intercept the CloseRequested event (when the user hits the “X” on the window). The latter is somewhat unrelated to the topic of this post, but it’s an implementation choice I made for the sample app – and might be useful for your similar scenarios. Note that this API is available in the Creators Update in preview state.

manifest

With the project structure and manifest declarations in place, we can start implementing the desired functionality. First, we want to intercept the user’s close gesture by subscribing to the CloseRequested event:

SystemNavigationManagerPreview mgr =
 SystemNavigationManagerPreview.GetForCurrentView();
mgr.CloseRequested += SystemNavigationManager_CloseRequested;

When the event got triggered we let the user decide if/how they want to close the app. If they chose “Close to Systray” we will launch our WinForms component that will continue to run our code in the notification area. Note that I am only making this call on systems that support the FullTrustAppContract. This way my app will still be universal and work correctly on all editions of Windows 10, such as Surface Hub or Windows Phone:

case CloseAction.Systray:
 if (ApiInformation.IsApiContractPresent(
  "Windows.ApplicationModel.FullTrustAppContract", 1, 0))
 {
 await FullTrustProcessLauncher.LaunchFullTrustProcessForCurrentAppAsync();
 }

Now switching over to the WinForms component. All the relevant code lives in SystrayApplicationContext.cs. I am implementing basic functionality here in the context menu to demonstrate a couple of common concepts:

  1. Open UWP” – restores the UWP app back to the foreground
  2. Send message to UWP” – sends a message via AppService, the UWP will display it as toast (if in background) or dialog (if in foreground)
  3. Open legacy companion” – displays a WinForms UI that is implemented in the same component, included in our UWP package
  4. Exit” – exits both the WinForms and UWP processes

Here is how we hook up the menu handlers. Explore the full source code on GitHub for details on each of the menu actions.

public SystrayApplicationContext()
{
 MenuItem openMenuItem =
 new MenuItem("Open UWP", new EventHandler(OpenApp));
 MenuItem sendMenuItem =
 new MenuItem("Send message to UWP", new EventHandler(SendToUWP));
 MenuItem legacyMenuItem =
 new MenuItem("Open legacy companion", new EventHandler(OpenLegacy));
 MenuItem exitMenuItem = new MenuItem("Exit", new EventHandler(Exit));
 openMenuItem.DefaultItem = true;

 notifyIcon = new NotifyIcon();
 notifyIcon.DoubleClick += new EventHandler(OpenApp);
 notifyIcon.Icon = SystrayComponent.Properties.Resources.Icon1;
 notifyIcon.ContextMenu = new ContextMenu(new MenuItem[]
   { openMenuItem, sendMenuItem, legacyMenuItem, exitMenuItem });
 notifyIcon.Visible = true;
}

Two of the actions I want to highlight real quick, as they are using UWP features, called from the WinForms component, which may be a bit of a new concept for some readers. To get access to those APIs (AppListEntry and AppServiceConnection) I just need to add a reference to the Windows.winmd in my WinForms project. Then I can make these calls:

private async void OpenApp(object sender, EventArgs e)
{
 IEnumerable<AppListEntry> appListEntries = await Package.Current.GetAppListEntriesAsync();
 await appListEntries.First().LaunchAsync();
}

private async Task SendToUWP(ValueSet message)
{ 
 if (connection == null)
 {
  connection = new AppServiceConnection();
  connection.PackageFamilyName = Package.Current.Id.FamilyName;
  connection.AppServiceName = "SystrayExtensionService";
  connection.ServiceClosed += Connection_ServiceClosed;
  AppServiceConnectionStatus connectionStatus = await connection.OpenAsync();
 }
 await connection.SendMessageAsync(message);
}

Hope this information was useful for some of you and will help you when you make the jump from Win32/NET to UWP on Windows 10. Please leave your questions and comments here and I will respond as soon as I can.

103 thoughts on “UWP app with Systray extension

  1. The Systray can’t support background media player of the UWP project.
    When I closed the app, the background task still be received the cancel event.

    Because I want to implement the feature:
    When app be close, the background media player can keep play music in the Systray.

    Is it possible? thank you.

    Like

  2. Thank you for this great article.
    I would like to ask a question.
    If I update my existing UWP app that is published in store to use capabilities mentioned here, should I expect anything unusual? I mean, I’ve seen that projects that are converted through the bridge are publishing with a special onboarding process, will it also be the case for my app?

    Like

    1. Yes, when you submit your update that adds the ‘runFullTrust’ capability the submission will go through a special onboarding review. Let me know if you run into any problems with that.

      Like

  3. Hi and thank you for a great article.
    I would like to ask a question.
    I have a UWP app published in store, and I would like to know if there would be any obstacles while publishing an update if I update my app to use the capability mentioned here.
    Thanks.

    Like

  4. did something break with fall creators update? I get the error c:\program files\windowsapps\….\Systraycomponent.exe -> The data area passed to a system call is too small. This was installed from the store. I haven’t tried from the repository yet.

    Like

      1. Well timing is everything. We’ll have to wait for the CU for SQL to come out to fix this as the article says, we’re using filestreams in the database. Good to know though for future.

        Like

  5. Dear Stefanwick,

    As per our current analysis, systray app is just acting like a Dock in the system tray. It is stopping its activity immediately after minimized to systray.

    We are having the following requirement:

    Once the app minimized to systray, It should be running in background and keep on sending SendIOControlAsync() [Async IOCTL from UWP] request from background to a custom kernel mode driver [for a virtual device].

    Once we got response from driver it should show notification to user.

    Note: Background task worker thread can be invoked only by the response from driver. We don’t have any system event to invoke it. Also the driver will respond back to the Async IOCTL only after 1 hr/2 hr.(It means we could not define a specific response time period)

    Will that be supported in UWP systray app?

    Your suggestions are greatly appreciated.

    Regards,
    Vicky

    Like

    1. I think your question is really unrelated to the systray. You can keep the UWP running when minimized – no need for systray to do this. Take a look at this: https://docs.microsoft.com/en-us/windows/uwp/launch-resume/run-in-the-background-indefinetly

      You may or may not have a systray presence – that’s really your design choice.

      Regarding this sample: yes, the UWP process by default stops executing when minimized, but the systray component keeps running, so you could also just make those periodic calls from that component. This has the added advantage that you have access to the full Windows API from that component, whereas the UWP process is limited to the UWP API surface.

      Like

  6. I have a requirement to host a WCF service to receive responses from a service. As UWP app doesn’t have wcf hosting capabilities, I chose this system tray component where I will host my service so that I get responses and communicate to UWP app via app service and show toasts.

    But I couldn’t figure out what’s happening, the systray component terminates immediately after the launch. To be clear on my systray integration with uwp I already tested it and is working and then extended it with WCF hosting.

    Is there any issue in my concept or this scenario isn’t acceptable with UWP?
    Kindly help.

    Like

    1. Hi Siddhardha,

      I can’t think of any reason why this wouldn’t work. We would need more details to analyze why it’s not working. Have you run this under a debugger yet? You might want to put this out on Stackoverflow and we’ll get you an answer there.

      Thanks,
      Stefan

      Like

  7. Hi Stefanwick

    Wonderful work! But I ran into an issue. My UWP app is in JavaScript and I used your C# code for Systray Extention. I ran into this weird error of “The type or namespace name ‘Windows’ does not exist in the namespace ‘System’ (are you missing an assembly reference?)”. I added the System.Windows.Forms reference too and I tried changing the .NET framework version to 4.5.2 and then the error changed to “The type or namespace name ‘ApplicationModel’ does not exist in the namespace ‘Windows’ (are you missing an assembly reference?)”. Can you please help me with this.

    Thanks,
    Madhav

    Like

      1. Thanks a ton! It works great except that when I open from the system tray, the app restarts to its home page, even when the app is just minimized and not closed. Is there a way to open the app at its current page if it is in suspended state?

        Thanks,
        Madhav

        Like

  8. Hey Stefanwick,

    Fantastic tutorial, thanks so much!

    Quick question – I’m trying to initialize Firebase (step-up-labs version) in the SystrayComponent, but it seems to crash. Is there any way to see the error messages being thrown? I’m also using JS for the UWP app and logs from that appear fine.

    Is there anything I’m missing to get the Firebase initialization working? (e.g. internet permissions etc.?) https://github.com/step-up-labs/firebase-authentication-dotnet

    Thanks,
    Kieran

    Like

  9. Hi Stefanwick

    Did you find a way to give admin rights to the win32 app? I’m asking because I’d like to log to EventViewer and I need admin rights in order to create a source. Also, I’ve tried to use MetroLog for both apps, but I can not find the log files for win32 app, only for UWP one. What would you recommend for logging here?

    Thanks

    Like

    1. Hi Adrian:

      there are two answers to your question:

      1. being able to run the win32 process with admin privileges is a new feature that is coming in the 2018 fall update of Windows 10. It should already be working in current insider builds (need to declare the new restricted capability “allowElevation”). Hope I can share a sample for this soon.

      2. Just for the purpose of logging you should not be required to elevate. Not very familiar with Metrolog, but have you raised an issue with them? Note that declaring the new capability for elevation may prevent you from shipping in the Store, so this may not be the preferred route for this problem anyway.

      Thanks,
      Stefan

      Like

  10. I have tried converting an existing application using Desktop App Convertor by providing input as the .msi file from the installer package folder path of my existing desktop application. I tried installing it and using the application post conversion and it works.. But I have few questions below.

    1. The converted application couldn’t be launched on the OS version which is less than the Base image version took for conversion. Is this expected? So if we want to support old versions should we take the 10.0.14393 as base image and convert?

    2. Post installing the converted app, launched WACK to test the installed app and it fails for “Package sanity test” saying ProcessorArchitecture is x86 in manifest and these 2 dll’s (comctl32.dll & GdiPlus.dll) were of x64 config.
    I didn’t mentioned anywhere as x86 during conversion, no idea how it is taken in manifest. How to resolve this?

    3. The converted application has only .appx created, no .appxupload file generated. For these kind of converted applications case how to reach the Store?

    Like

    1. Hi SiD,

      1. just set MinVersion=”10.0.14393.0″ in your appxmanifest file to support that version, assuming your app doesn’t depend on OS features that have been added only in later versions

      2. use -packageArch parameter to specify x64 if you intended to create an x64 package
      https://docs.microsoft.com/en-us/windows/uwp/porting/desktop-to-uwp-run-desktop-app-converter#architecture-params

      3. you can upload .appx files to the store as well. But if you want to go with .appxupload, the best way to create an .appxupload file for your Win32 application would be to use the Visual Studio packaging project for creating the packages.

      Btw, for general questions like these that aren’t directly related to the blog post, I recommend using stackoverflow with tag “DesktopBridge” in the future, to get the best visibility and traction on your requests.

      Thanks,
      Stefan

      Like

      1. 1. As I used the Convertor I had not prepared any manifest manually. Post Conversion it is generated automatically and it already has min version tag with 14393 version. Still it is not launching on RS1 & RS2.

        2. So do you mean we have to generate multiple packages with same desktop application input to generate separate appx?

        3. As this is converted app using the converted tool, I can’t package it through Visual Studio as I’m not using the source but making use of the builded product i.e. msi file.

        Like

  11. 1. Need more details about the app to answer that. I suggest you post a question on stackoverflow with repro and/or logs.

    2. It’s your choice. If you want to distribute multiple architecture then you will need to create multiple appx package – one for each architecture. You can then zip them up and rename to .appxbundle to combine all of them and submit them in one shot to the Store.

    3. It’s possible to use the packaging project also with pre-built binaries, but you don’t have to use VS to create an .appxupload file. You can create it manually, it’s just a zip file that contains the .appxbundle and the .appxsym file. Then again, you need to have an .appxupload as you could also submit either the .appx or the .appxbundle file directly to the Store.

    Liked by 1 person

  12. Hi Stefanwick,

    Thanks for the excellent sample for enabling Uwp UI to be rendered in response to systray [ / notification area ] process context menu selections. A couple of related questions . . .

    q1. When I f5 on the SystrayExtension and try to debug through SystrayComponent breakpoints that doesn’t work as it appears vs17 15.8.2 debugger launch process doesn’t successfully hookup debugger to both processes. Is there a trick to making that work? All I’ve been able to make work thus far is launch a debug version of SystrayExtension from outside of vs17 that I then close to leave SystrayComponent process running at which point I attach vs17 debugger to in order to get support for stepping through all but startup code for SystrayComponent.

    q2. If I want startup to immediately got to minimized SystrayComponent process presence, vs uwp UI view, would I just have SystrayExtension | MainPage.xaml.cs | SystrayExtension OnNavigatedTo() event issue a call to version of SystemNavigationManager_CloseRequested() event handler that executes the case CloseAction.Systray: steps?

    q3. Currently the SystrayComponent display what appears to be a SystrayApplicationContext constructor runtime defined Windows.Forms based context menu. Is there a way to have it instead pop a SystrayExtension defined uwp UI above the systray icon when context / right mouse button is clicked similar to what it seems the OneDrive, Sounds and Windows Ink Workspace do?

    Like

    1. For Q1: this is currently a gap in the Visual Studio debugging support. It does not automatically attach to both processes as you would expect. The VS team is looking into possible solutions.

      For Q2: You would just call FullTrustProcessLauncher.LaunchFullTrustProcessForCurrentAppAsync() during start up of the UWP app, e.g. from your MainPage’s OnNavigatedTo event.

      For Q3: A UWP UI is currently not supported there. However, with the new RS5 support for hosting UWP-XAML in Win32 processes you should be able to accomplish such scenarios.

      Liked by 1 person

      1. Thanks for the responses, that helps.

        Any reason why with this uwp desktop bridge scenario a person shouldn’t be using a “windows application packaging project” to produce the store publishing upload similar to what you made use of in GlobalHotkey uwp desktop bridge sample vs the package output of the SystrayExension uwp app project?

        Like

      2. Actually, it’s highly recommended to use the Packaging project also for this type of scenario. The only reason I was not using it here is because I created this sample a long time ago before the Packaging project was able to support the scenario (and then I never found the time to update it later).

        Liked by 1 person

      3. In the derivation of your sample i’m working on i created a SystrayComponent | SystrayApplicationContext.cs user input event handler that opens a registry key for read and write purposes [ using (RegistryKey key = Registry.CurrentUser.OpenSubKey(“Control Panel\\Mouse”, true)) ] that is successfully able to read the SwapMouseButtons value but fails to write it.

        The exact same code running in a .net framework console app is able to successfully write to that key/value without requiring any “run as administrator privileges”.

        Is there some setting i’m overlooking that is necessary to allow the .net framework / winforms based SystrayComponent to be able to write to hkcu paths?

        Like

      4. Thanks for response. If one where to make their systray [ / notification area ] application using only .net framework windows app project, e.g. the systraycomponent project only, would that change this restriction?

        Like

      5. Thanks for clarification that helps. Sounds like one would have to resort to a wix msi installed set of bits if it was critical to app experience that you have a way to get around the registry hkcu write security issue.

        Like

  13. Any insight how in the Uwp Page OnNavigatedTo() handler I could determine if page is being opened as a result of desktop extensions launched systray component’s “Open UWP” menu item?

    I ask because in the Uwp Page OnNavigatedTo() I have code that automatically uses desktop extension to launch systray component and then close, versus having to do that step manually, and then when user selects “Open UWP” and it gets reopened I have to find a way to not carry out those steps in that case.

    Like

    1. . . . I thought I could maybe use the connection.SendMessageAsync(message) approach to reopening it but when I look at MainPage.xaml.cs OnNavigatedTo parameter NavigationEventArgs e I don’t see a way to access sent message and that wouldn’t help anyway because that requires a person to manually click the notification popoup in order to relaunch the UWP Page view.

      Like

      1. . . . using uwp app ApplicationData.Current.LocalSettings.Values[“systrayComponentLaunched”] = true; to track it having launched systray component using desktop extensions and then using ApplicationData.Current.LocalSettings.Values.Remove(“systrayComponentLaunched”); in systray component exit to capture it having been closed.

        Like

    2. In the sample I am using AppListEntry.LaunchAsync() to launch the UWP, which doesn’t provide any context/parameters. What you could do instead is do a protocol launch, which will allow you to pass in arbitrary parameters. This way you can detect that the launch came from the systray component.

      Like

  14. Thanks for suggestion I am exploring package manifest | declarations | add | Protocol coupled with use of Windows.System.Launcher.LaunchUriAsync.

    I’m also looking at your GlobalHotKey sample which makes use of desktop extensions and am finding that its MainPage.xaml.cs | OnNavigatedTo is able to call System.Diagnostics.Process.GetCurrentProcess(); which it stores in a LocalSetting for access later by desktop extensions launched win32 app process.

    I tried introducing this same call into my derivative of your uwp SystrayExtension + win32 SystrayComponent sample provided here and it doesn’t accept it, “The type or namespace name ‘Process’ does not exist in the namespace ‘System.Diagnostics’ (are you missing an assembly reference?)”

    Looking at uwp app references in both samples, this one and GlobalHotKey one, i’m seeing same Analyzers, Microsoft.NETCore.UniversalWIndowsPlatform, “Universal Windows” and “Windows Desktop Extensions for the UWP” entries.

    Any insights as to what i’m overlooking that is necessary to enable use of System.Diagnostics.Process.GetCurrentProcess() call?

    Like

    1. In order to use System.Diagnostics.Process.* make sure both your Min and Target versions are set to 16299 (or higher) in the UWP project – and make sure you reference a current Microsoft.NETCore.UniversalWIndowsPlatform package.

      Like

      1. Thanks for details. The min version in systrayextension was set to creators update 15063 which I changed to fall creators update 16299 and the Microsoft.NETCore.UniversalWindowsPlatform package was set in SystrayExtension.csproj to 5.2.3 which I manually updated to 6.1.9 version used in new | project | c# | windows universal | blank app. I did that manually as it wasn’t clear how to remove and re-add that package reference to pickup the latest version reference.

        Like

    1. . . . tried “Window.Current.Close();” and it generates a “System.Runtime.InteropServices.COMException: ‘A method was called at an unexpected time. Closing main window is not allowed.'”

      Like

  15. Thanks that helped. With that matter addressed I was all excited to have my uwp store app, that made use of the desktop bridge/extensions functionality you covered in this sample and your followup global hotkey one [ https://stefanwick.com/2018/05/15/global-hotkey-registration-in-uwp/ ], published in the store. I went through the submission process and they responded with a no where the certification report just says the issue is “Your account has not been approved for the Centennial program.”. There was no guidance in that report, or at https://developer.microsoft.com/en-us/windows/bridges/desktop that i could readily see, on next steps to get my developer microsoft account approved for the Centennial program, which appears to be marketing name for release of desktop bridge/extensions services to help get more owners of windows desktop apps to slowly migrate and publish them as store apps. Any pointers you can provide as I posted question about this to desktop bridge support forum [ https://social.msdn.microsoft.com/Forums/windowsapps/en-US/88bdb12e-a276-4054-bc89-70e232dd9e23/getting-developer-microsoft-account-msa-added-to-centennial-program-aka-desktop-bridge-?forum=wpdevelop ] and haven’t gotten any leads to this matter from that as of yet?

    Like

  16. Hi Stefan,

    I’ve actually tried to implement the systray component into my existing solution but ran into a problem concerning the async OpenApp method.

    This is what it tells me : http://oi65.tinypic.com/jinhuc.jpg

    Any help on that? I’ve checked the windows.winmd version, the .Net Framework version and the output type of the Systray component project I created, which is all the same as yours.

    Thanks

    Like

      1. Thanks!

        I’ve found it with NuGet Package Manager.

        However, I still need to sort out this error by myself :

        Could not install package ‘System.Runtime.WindowsRuntime 4.3.0’. You are trying to install this package into a project that targets ‘.NETFramework,Version=v4.6.1’, but the package does not contain any assembly references or content files that are compatible with that framework. For more information, contact the package author.

        Will update for anyone else with the same problem if I find the solution

        Like

      2. No need to install a Nuget package. You can just add an assembly reference to %ProgramFiles(x86)%\Reference Assemblies\Microsoft\Framework\.NETCore\v4.5\System.Runtime.WindowsRuntime.dll

        Like

  17. Thanks for the solution. Would i be possible to hide the icon from Task bar and only show it in Task tray when minimized?

    Like

  18. I’m trying to include a reference to Newtonsoft.JSON in the systray component. I can add the reference but when I try to use it in code, I get an exception:

    Type: System.IO.FileNotFoundException
    Message: Could not load file or assembly ‘Newtonsoft.Json, Version=12.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed’ or one of its dependencies. The system cannot find the file specified.

    I’ve tried referencing it through Nuget and by directly referencing the DLL.

    I’ve tried to reference a different .NET Standard library and had the same problem.

    Any suggestions on how to get this reference to work?

    Like

  19. Hello,
    My question may be a little bit “stupid” … But where do we find the manifest ? I am at the step where we do have to declare the “windows.fullTrustProcess” and “windows.AppService” extensions in the manifest for our UWP app package.

    But in my UWP folder there is no file “app.manifest”. Where can I find / create it ?

    Thanks for your support

    Like

    1. In VS the file you want to edit is called Package.appxmanifest. Based on that file the VS build process then creates AppxManifest.xml, which is the manifest that ends app in your app package.

      Like

      1. Thanks for your quick reply.

        I still have an issue with that manifest. The and both are underlined in red and nothing works, VS says that they are not defined … (Btw I’ve added the Windows Desktop for Extension UWP) and I’m under OS 1809.

        Thanks for your support

        Like

  20. I updated my store app, based on use of the desktop bridge functionality outlined in this post, from using the 17134 windows.winmd to the new 17763 windows.winmd installed by current vs19 environment

    $(MSBuildProgramFiles32)\Windows Kits\10\UnionMetadata\10.0.17134.0\Windows.winmd
    $(MSBuildProgramFiles32)\Windows Kits\10\UnionMetadata\10.0.17763.0\Windows.winmd

    Now i’m finding that the “Windows App Certification Kit 10.0.17763.13” is failing on the “Package Sanity Test” “Blocked executables” test saying that “File Win32\SystrayComponent.exe contains a reference to a “Launch Process” related API System.Diagnostics.Process.Start”.

    Does this imply that store app packages are no longer allowed to use this functionality and if so how does one package, publish and share an update to an existing store application that is doing this?

    Like

    1. . . . and below the Error Found: details noted about there is a How to fix: entry that says “How to fix: Identify which of the flagged entries represent a call to launch an executable file that is not part of your app and remove those calls. If the flagged files are part of your application, you may ignore the warning.” suggesting i can ignore this.

      Like

      1. Yes that test is listed in the “Results for optional tests” section of the “Windows App Certification Kit 10.0.17763.13” output that was kicked off by Package | Store | Create App Packages process.

        I’m also noticing that with vs19 17763 sdk in place the “Windows App Certification Kit 10.0.17763.13” output non-optional tests section is now creating an “App manifest resources tests” | “App resources” subtest failure i wasn’t seeing before stating “Error Found: The app resources validation test detected the following errors: The test was blocked from execution. Please try re-running the validation. If the test execution remains blocked then please contact support.”

        Not a lot of insight in the output or reference link it points to for more help [ App Package Resource Validation -> https://docs.microsoft.com/en-us/windows/uwp/debug-test-perf/windows-app-certification-kit-tests#app-manifest-resources-test ] that outlines why this is now surfacing and wasn’t with prior 17134 sdk in place. I guess i can just try and submit the Package | AppPackages | Package_1.1.#.0_x86_bundle.appxupload output file to the store in spite of this non-optional test error and see what happens.

        Like

      2. Sorry, can’t help with that. I no longer work on the Windows team. If you are seeing new issues in newer versions of the SDKs or tools, please log a bug and/or reach via official channels. I am unable to provide support on this.

        Liked by 1 person

      3. Yes i understand and appreciate you taking the time to provide your thoughts on this given you long since have moved on to other activities.

        Like

  21. Is there a way to start a UWP app in the systray? Opposed to having to press close and then send it to the systray, can you just automatically have it start (and live) in the systray?

    Like

    1. Yes and no. You can add the systray entry right away when the app first launches. No need to do it on close. However, after installing the UWP app, the user needs to launch the app at least once in order to get the systray entry, in other words: it can’t be done at install time.

      Like

    1. Yes, you can. Take a look at App.xaml.cs and notice the AppServiceConnection instance that gets created when the systray component starts up. This object enables bi-directional communication, so you can not only receive messages from the systray, but also send messages to it from the UWP.

      Liked by 2 people

      1. I am able to send the message and receive a succeed as the response.

        public async Task SendToTray(ValueSet message)
        {
        if (Connection == null)
        {
        Connection = new AppServiceConnection
        {
        PackageFamilyName = Package.Current.Id.FamilyName,
        AppServiceName = “TheNetWatchmanExtension”
        };
        AppServiceConnectionStatus connectionStatus = await Connection.OpenAsync();
        if (connectionStatus != AppServiceConnectionStatus.Success)
        {
        return;
        }
        }
        AppServiceResponse response = await Connection.SendMessageAsync(message);
        }

        My only problem is how to receive the message on in the apptray code.

        Thanks

        Like

      2. I added the code to send in UWP and to receive in the SystemTray but for some reason, the UWP app is sending to itself 🙂
        I am not sure if i should create a new service or add any custom configuration to the app.config of the winforms. Any documentation or suggestion will be highly appreciated

        private async Task SendToSystemTray(ValueSet message)
        {
        try
        {
        if (SendingConnection == null)
        {
        SendingConnection = new AppServiceConnection();
        SendingConnection.PackageFamilyName = Package.Current.Id.FamilyName;
        SendingConnection.AppServiceName = “SystrayExtensionService”;
        SendingConnection.ServiceClosed += Connection_ServiceClosed;
        AppServiceConnectionStatus connectionStatus = await SendingConnection.OpenAsync();
        if (connectionStatus != AppServiceConnectionStatus.Success)
        {
        //MessageBox.Show(“Status: ” + connectionStatus.ToString());
        Logger.Instance.WriteToConsole(“Statues: ” + connectionStatus.ToString());
        return;
        }
        }

        await SendingConnection.SendMessageAsync(message);
        }
        catch (Exception ex)
        {
        Logger.Instance.WriteToConsole(“failed to send to System Tray”);
        }

        }

        Like

      3. Don’t create a new AppServiceConnection. Use the one that is already established in the code. See App.xaml.cs in OnBackgroundActivated() the active instance of the connection is obtained. Use this instance to send messages to the systray component. To receive the message in the systray component subscribe to the AppServiceConnection.RequestReceived event.

        Like

  22. After creating a store app and publishing it using this posts recipe for a uwp app discovery, installation and launcher of win32 systray extension process i’m wondering how i interpret the “app usage” details in the published store app’s analytics page, https://partner.microsoft.com/en-us/dashboard/analytics/reports/appusage?productId=9MTW4CJ7S276.

    It seems my acquisitions and installs are pretty much staying in lock step, which i assume is to be expected, but my usage numbers are confusing to interpret. Mainly because i’m not sure if launching of the store app uwp process that in turn uses full trust to spawn the win32 systray extension process, results a registered usage or not like a store app that is the primary foreground process would be.

    Like

  23. Hi Stefan, Thanks for this awesome post! It is very helpful! I have a few questions.
    1. Every time user clicks “x” to close the App, if “Close to Systray” is selected, this “FullTrustProcessLauncher.LaunchFullTrustProcessForCurrentAppAsync()” is called. So when this is called the first time, the systray icon is created as “SystrayComponent.exe” process is started. What is the behavior of this “LaunchFullTrustProcessForCurrentAppAsync()” call when it is called subsequently? Is the call a no-op if the process is already running?
    2. With this scenario:
    * User clicks “x” to close the App and chooses “Close to Systray”
    * User clicks systray icon to open the App
    * User clicks “x” to close the App and chooses “Terminate App”
    Now the “Terminate App” wouldn’t actually terminate the App, it behaves as if “Close to Systray” is called.
    If I want “Terminate App” to close the App, I guess I can call “App.Current.Exit()”, if I want SystrayComponent.exe closed as well, do I have to use AppService to send message to SystrayComponent.exe and let it react to the message and close itself? Is there another API the App can call to terminate the launched FullTrustProcess?
    3. If I want “SystrayComponent.exe” started (with icon added to systray) as soon as the main App is started, where is the best place to make that call? In App.OnLaunched()?
    Thanks!

    Like

  24. Any recommendations on how to take ViewModel property data captured and changed by user interaction with Uwp app’s xaml UI and share that instantly with the systray extension?

    Like

    1. . . . for example is there anyway to give the ‘await FullTrustProcessLauncher.LaunchFullTrustProcessForCurrentAppAsync()’ launched system tray winform app access to the uwp app’s Windows.Storage.ApplicationData.Current.LocalSettings as that would make this straight forward.

      Like

      1. Both sides see the same ‘LocalSettings’ so this should already be straightforward without passing anything into that API. Anything you write from the UWP into the settings is already accessible from the Winforms app (and vice versa).

        Like

  25. Thanks for the response and pointers.

    Tested and yes the winform .net framework full trust process will compile with Windows.Storage.ApplicationData.Current.LocalSettings calls in place presumably through its reference to %programfiles(x86)%\Reference Assemblies\Microsoft\Framework\.NETCore\v4.5\System.Runtime.WindowsRuntime.dll vs the uwp store apps support for that type from whatever *.winmd it references by default.

    Is there a specific scenario that outlines when a person shouldn’t use that simple Windows.Storage.ApplicationData.Current.LocalSettings mechanism for passing uwp app xaml UI modifiable datacontext/viewmodel bound data over to the winform .net framework full trust process vs adopting the seemingly more complicated one time argument passing when launching the full trust process [ https://stefanwick.com/2018/04/06/uwp-with-desktop-extension-part-2/ ] or after launch using two way communication channel [ https://stefanwick.com/2018/04/16/uwp-with-desktop-extension-part-3/ ].

    Like

    1. The one-time hardcoded arguments are for scenarios where you can’t change the fulltrust EXE (legacy scenario) and you have to work with whatever launch parameters they support.

      The two-way communication via AppService enables event-driven communication between the processes, which the LocalSettings approach does not provide.

      Liked by 1 person

  26. Hi. Considering using systray like this example as a companion to a simple uwp app. My question is. What happens if the win32 systray program is running when we update the complete uwp app through the store.
    Will it fail updating due to file locking, or will the store kill the win32 process in order to replace the file, or is there an “pre install app package event”, where I can stop the win32 process by code

    Like

    1. It’s been a few years since I worked in this space and things may have changed. But if I remember correctly, the update will be on hold until the user shuts down the app. There is some timeout though (like after 3 days) when the system will shutdown the process and update it.

      Like

  27. @stefanwick I know this is a super old post but its the only example I’ve found to move a UWP app to the system tray.

    I have the same question @Madhav asked a few years ago- is there way that when you bring the app back from the system tray you can make the app act like it is coming from from a minimized state, not a closed state?

    Example:

    Lets say your first page is a page with a button in the middle of the screen that says “go to next page” – when the user hits that it goes to another page with a list of items.

    if I minimize the app, and then bring it back, I will see the page with the list of items.

    From your github example – the app acts like Its coming back from a closed state, so if I implement this am I correct that it will always start fresh and go to the first page? Is this a limitation?

    Thanks

    Like

    1. Yes, it comes back from a closed state in the example. To get the desired behavior you follow the recommended practice for saving state during OnSuspending and restoring state during OnResuming. You should do that anyway, regardless of the Systray, because your app may get closed and resumed by the system, for example during an update.

      Alternatively, you can change the sample to not close the app when minimizing to Systray, but to suspend to the taskbar instead.

      Liked by 1 person

      1. First – thanks for the quick reply, really appreciate it. I don’t understand your solution though.

        When you say change the sample to suspend to taskbar instead – wouldn’t that defeat the whole purpose ? We want it to go to the system tray, not taskbar.

        Also if I understand the logic of OnSuspending/OnResuming – that would require re-building the app state right? That doesn’t happen when minimizing the app.

        To use a simple dumb example:

        Say you have the first page with a button that says “go to next page”
        When you hit that button it goes to another page with combo button options. Say I select option 3 and minimize. When I bring it up, it doesn’t show the splash screen, navigate to the second screen and select option 3, it just shows the app in the state it was in before I minimized instantly.

        If I do the OnSuspending/OnResuming logic that would mean on minimize to system tray I would have to save that the user is on page 2 and selected option 3 – then on resume code have it navigate to page 2 and select option 3.

        Essentially I am looking for the app to act in the same exact way it acts now if you minimize the app – only different being instead of going to taskbar it goes to system tray. So when I bring it back from system tray I want it to act like if I was bringing it back from the taskbar.

        Thanks

        Like

      2. True, suspending to taskbar defeats the purpose of the systray. I guess I just mentioned it for completeness 🙂

        The typical pattern is that each page has a suspending/resuming handler that saves the state of the local page. And in the global suspending handler you just save what page you are on. Then on resume you navigate to the right page, and restore its state.

        Like

      3. Related to this does something jump to mind as to how one would detect case when uwp app is being initially launched, either manually or resulting from windows signin processed %appdata%\Microsoft\Windows\Start Menu\Programs\Startup\ entry, so that the uwp UI can be automatically closed following starting of the full trust .net framework systray process? In other words the only time i want to open and leave uwp UI running is when user takes action in full trust .net framework systray process, e.g. show usage context menu, that requires surfacing that uwp UI.

        Like

  28. I know you said the typical pattern is to suspend/resume – but is there anyway to make minimize to tray act like minimize to taskbar only have it go to the system tray?

    In your sample – if I add

    System.Collections.Generic.IList infos = await Windows.System.AppDiagnosticInfo.RequestInfoForAppAsync();
    System.Collections.Generic.IList resourceInfos = infos[0].GetResourceGroups();
    await resourceInfos[0].StartSuspendAsync();

    It minimizes to taskbar AND system tray- and if I bring it back from system tray it acts like I want it to – like if I was bring it back from the taskbar.

    I don’t want to have to reload the state because my app is a UWP Unity app – to navigate back to the scene will be quite a process – and re-loading the Unity scene will take time. But resuming from taskbar is instant since it doesn’t requite re-loading the state.

    Like

    1. I am afraid there isn’t a great answer to this. There is no such feature as “minimize UWP to systray”. This sample just “imitates” a minimize to systray by closing it and restarting it. As a result you get the undesired behavior you see.

      Liked by 1 person

  29. I noticed when you send a message to UWP from the systray it resumes the UWP app.

    Is there anyways to keep the UWP app in the background whilst sending messages from systray?

    Like

  30. Hi stefanwick,
    Thanks for your great article again 🙂
    I’m confused with the “background” of UWP after user click “x”.
    Is the UWP side still working?
    I mean it’s just invisible, but it can handle AppService request just like it works in foreground?

    Like

Leave a comment