Skip to content

Instantly share code, notes, and snippets.

@andrewleader
Last active November 24, 2023 14:12
Show Gist options
  • Save andrewleader/5adc742fe15b06576c1973ea6e999552 to your computer and use it in GitHub Desktop.
Save andrewleader/5adc742fe15b06576c1973ea6e999552 to your computer and use it in GitHub Desktop.
How to add single instancing to C# WinUI 3 app

Overall steps... Requires WinAppSDK 1.0.0 Preview 3 or greater and you must COMPILE FOR x64 (there's a known bug where this doesn't work from x86 apps)

  1. Add code to find existing instance, and if found, redirect and kill current process using System.Diagnostics.Process.GetCurrentProcess().Kill() (the WinUI 3 Exit() method doesn't work)
  2. Add code to handle the activation redirection
  3. Install the Microsoft.Windows.CsWin32 NuGet package
  4. Add the NativeMethods.txt file with two methods, ShowWindow and SetForegroundWindow (the WinUI 3 Activate and AppWindow Show methods don't work)
  5. In the redirection event, call ShowWindow and SetForegroundWindow

Full code sample: https://github.com/andrewleader/RedirectActivationWinUI3Sample

1. Install Microsoft.Windows.CsWin32 NuGet package

Include prerelease when searching, since it's a prerelease package

2. Add NativeMethods.txt file

The file contents should simply be...

SetForegroundWindow
ShowWindow

3. In App.xaml.cs OnLaunched, add the following...

protected override async void OnLaunched(Microsoft.UI.Xaml.LaunchActivatedEventArgs args)
{
    // Get the activation args
    var appArgs = Microsoft.Windows.AppLifecycle.AppInstance.GetCurrent().GetActivatedEventArgs();

    // Get or register the main instance
    var mainInstance = Microsoft.Windows.AppLifecycle.AppInstance.FindOrRegisterForKey("main");

    // If the main instance isn't this current instance
    if (!mainInstance.IsCurrent)
    {
        // Redirect activation to that instance
        await mainInstance.RedirectActivationToAsync(appArgs);

        // And exit our instance and stop
        System.Diagnostics.Process.GetCurrentProcess().Kill();
        return;
    }

    // Otherwise, register for activation redirection
    Microsoft.Windows.AppLifecycle.AppInstance.GetCurrent().Activated += App_Activated;

    m_window = new MainWindow();
    m_window.Activate();
}

private void App_Activated(object sender, Microsoft.Windows.AppLifecycle.AppActivationArguments e)
{
    // Bring the window to the foreground... first get the window handle...
    var hwnd = (Windows.Win32.Foundation.HWND)WinRT.Interop.WindowNative.GetWindowHandle(m_window);

    // Restore window if minimized... requires Microsoft.Windows.CsWin32 NuGet package and a NativeMethods.txt file with ShowWindow method
    Windows.Win32.PInvoke.ShowWindow(hwnd, Windows.Win32.UI.WindowsAndMessaging.SHOW_WINDOW_CMD.SW_RESTORE);

    // And call SetForegroundWindow... requires Microsoft.Windows.CsWin32 NuGet package and a NativeMethods.txt file with SetForegroundWindow method
    Windows.Win32.PInvoke.SetForegroundWindow(hwnd);
}

4. Build and deploy your app as x64

Only x64 works right now, x86 won't properly detect the existing instance.

@KWodarczyk
Copy link

Also, instead of all that pinvoke stuff i just do m_window.DispatcherQueue.TryEnqueue(() => { m_window?.Activate(); }); and works, it this not allowed ?

@andrewleader
Copy link
Author

@KWodarczyk that might work now, at the time I was writing this I don't think that was working (I tried simply using Activate and using the dispatcherqueue+activate but I could never get it working). I was writing this back with version 0.8 of WinAppSDK though I think.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment