Skip to content

Commit a4754bc

Browse files
committed
Merged main into live
2 parents f293651 + 9b9637e commit a4754bc

File tree

3 files changed

+240
-14
lines changed

3 files changed

+240
-14
lines changed

hub/apps/toc.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ items:
4242
href: windows-app-sdk/applifecycle/applifecycle.md
4343
- name: App instancing
4444
href: windows-app-sdk/applifecycle/applifecycle-instancing.md
45+
- name: Create a single-instanced app
46+
href: windows-app-sdk/applifecycle/applifecycle-single-instance.md
4547
- name: Rich activation
4648
href: windows-app-sdk/applifecycle/applifecycle-rich-activation.md
4749
- name: Power management

hub/apps/windows-app-sdk/applifecycle/applifecycle-instancing.md

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,34 @@
11
---
2-
description: Describes how to use app instancing features with the app lifecycle API (Windows App SDK).
3-
title: App instancing with the app lifecycle API (Windows App SDK)
4-
ms.topic: article
5-
ms.date: 03/07/2024
2+
description: Describes how to use app instancing features with the app lifecycle API in WinUI with the Windows App SDK.
3+
title: App instancing with the app lifecycle API (WinUI)
4+
ms.topic: concept-article
5+
ms.date: 09/20/2024
66
keywords: AppLifecycle, Windows, ApplicationModel, instancing, single instance, multi instance
7-
ms.localizationpriority: medium
7+
#customer intent: As a Windows developer, I want to learn how to use app instancing features with the app lifecycle API in WinUI with the Windows App SDK so that I can control how many instances of my app can run at the same time.
88
---
99

1010
# App instancing with the app lifecycle API
1111

12-
An app's instancing model determines whether multiple instances of your app's process can run at the same time.
12+
An app's instancing model determines whether multiple instances of your app's process can run at the same time. The app lifecycle API in the Windows App SDK provides a way to control how many instances of your app can run at the same time, and to redirect activations to other instances when necessary.
13+
14+
This article describes how to use the app lifecycle API to control app instancing in your WinUI apps.
1315

1416
## Prerequisites
1517

16-
To use the app lifecycle API in the Windows App SDK:
18+
To use the app lifecycle API in WinUI 3 apps:
1719

18-
1. Download and install the latest release of the Windows App SDK. For more information, see [Get started with WinUI](../../get-started/start-here.md).
19-
2. Follow the instructions to [Create your first WinUI 3 project](../../winui/winui3/create-your-first-winui3-app.md) or to [use the Windows App SDK in an existing project](../use-windows-app-sdk-in-existing-project.md).
20+
- Download and install the latest release of the Windows App SDK. For more information, see [Get started with WinUI](../../get-started/start-here.md).
21+
- Follow the instructions to [Create your first WinUI 3 project](../../winui/winui3/create-your-first-winui3-app.md) or to [use the Windows App SDK in an existing project](../use-windows-app-sdk-in-existing-project.md).
2022

2123
## Single-instance apps
2224

23-
> [!NOTE]
24-
> For an example of how to implement single instancing in a WinUI 3 app with C#, see [Making the app single-instanced](https://blogs.windows.com/windowsdeveloper/2022/01/28/making-the-app-single-instanced-part-3/) on the Windows developer blog.
25-
2625
Apps are single-instanced if there can be only one main process running at a time. Attempting to launch a second instance of a single-instanced app typically results in the first instance's main window being activated instead. Note that this only applies to the main process. Single-instanced apps can create multiple background processes and still be considered single instanced.
2726

28-
UWP apps are single-instanced by default. but have the ability to become multi-instanced by deciding at launch-time whether to create an additional instance or activate an existing instance instead.
27+
WinUI apps are single-instanced by default but have the ability to become multi-instanced by deciding at launch-time whether to create an additional instance or activate an existing instance instead.
28+
29+
The [Microsoft Photos](https://www.microsoft.com/store/productId/9WZDNCRFJBH4) app is a good example of a single instanced WinUI app. When you launch Photos for the first time, a new window will be created. If you attempt to launch Photos again, the existing window will be activated instead.
2930

30-
The Windows Mail app is a good example of a single instanced app. When you launch Mail for the first time, a new window will be created. If you attempt to launch Mail again, the existing Mail window will be activated instead.
31+
For an example of how to implement single instancing in a WinUI 3 app with C#, see [Create a single-instanced WinUI app](applifecycle-single-instance.md).
3132

3233
## Multi-instanced apps
3334

@@ -427,3 +428,9 @@ void DumpExistingInstances()
427428
}
428429
}
429430
```
431+
432+
## Related content
433+
434+
[Create a single-instanced WinUI app](applifecycle-single-instance.md)
435+
436+
[Microsoft.Windows.AppLifeycle.AppInstance](/windows/windows-app-sdk/api/winrt/microsoft.windows.applifecycle.appinstance)
Lines changed: 217 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
1+
---
2+
description: Describes how to use app instancing features with the app lifecycle API in WinUI with C# and the Windows App SDK.
3+
title: How to create a single-instanced WinUI app with C#
4+
ms.topic: how-to
5+
ms.date: 09/20/2024
6+
keywords: AppLifecycle, Windows, ApplicationModel, instancing, single instance, multi instance, winui, windows app sdk, c#
7+
#customer intent: As a Windows developer, I want to learn how to create a single-instanced WinUI 3 app so that I can ensure only one instance of my app is running at a time.
8+
---
9+
10+
# Create a single-instanced WinUI app with C#
11+
12+
This how-to demonstrates how to create a single-instanced WinUI 3 app with C# and the Windows App SDK. Single-instanced apps only allow one instance of the app running at a time. WinUI apps are multi-instanced by default. They allow you to launch multiple instances of the same app simultaneously. That's referred to a multiple instances. However, you may want to implement single-instancing based on the use case of your app. Attempting to launch a second instance of a single-instanced app will only result in the first instance’s main window being activated instead. This tutorial demonstrates how to implement single-instancing in a WinUI app.
13+
14+
In this article, you will learn how to:
15+
16+
> [!div class="checklist"]
17+
> - Turn off XAML's generated `Program` code
18+
> - Define customized `Main` method for redirection
19+
> - Test single-instancing after app deployment
20+
21+
## Pre-requisites
22+
23+
This tutorial uses Visual Studio and builds on the WinUI blank app template. If you're new to WinUI development, you can get set up by following the instructions in [Get started with WinUI](../../get-started/start-here.md). There you'll install Visual Studio, configure it for developing apps with WinUI while ensuring you have the latest version of WinUI and the Windows App SDK, and create a Hello World project.
24+
25+
When you've done that, come back here to learn how to turn your "Hello World" project into a single-instanced app.
26+
27+
> [!NOTE]
28+
> This how-to is based on the [Making the app single-instanced (Part 3)](https://blogs.windows.com/windowsdeveloper/2022/01/28/making-the-app-single-instanced-part-3/) blog post from a Windows blog series on WinUI 3. The code for those articles is available on [GitHub](https://github.com/jingwei-a-zhang/WinAppSDK-DrumPad).
29+
30+
## Disable auto-generated Program code
31+
32+
We need to check for redirection as early as possible, before creating any windows. To do this, we must define the symbol “DISABLE_XAML_GENERATED_MAIN” in the project file. Follow these steps to disable the auto-generated Program code:
33+
34+
1. Right-click on the project name in Solution Explorer and select **Edit Project File**.
35+
1. Define the **DISABLE_XAML_GENERATED_MAIN** symbol for each configuration and platform. Add the following XML to the project file:
36+
37+
```xml
38+
<propertygroup condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
39+
<defineconstants>DISABLE_XAML_GENERATED_MAIN</defineconstants>
40+
</propertygroup>
41+
<propertygroup condition="'$(Configuration)|$(Platform)'=='Debug|x86'">
42+
<defineconstants>DISABLE_XAML_GENERATED_MAIN</defineconstants>
43+
</propertygroup>
44+
<propertygroup condition="'$(Configuration)|$(Platform)'=='Release|x86'">
45+
<defineconstants>DISABLE_XAML_GENERATED_MAIN</defineconstants>
46+
</propertygroup>
47+
<propertygroup condition="'$(Configuration)|$(Platform)'=='Release|x64'">
48+
<defineconstants>DISABLE_XAML_GENERATED_MAIN</defineconstants>
49+
</propertygroup>
50+
<propertygroup condition="'$(Configuration)|$(Platform)'=='Debug|arm64'">
51+
<defineconstants>DISABLE_XAML_GENERATED_MAIN</defineconstants>
52+
</propertygroup>
53+
<propertygroup condition="'$(Configuration)|$(Platform)'=='Release|arm64'">
54+
<defineconstants>DISABLE_XAML_GENERATED_MAIN</defineconstants>
55+
</propertygroup>
56+
```
57+
58+
Adding the **DISABLE_XAML_GENERATED_MAIN** symbol will disable the auto-generated Program code for your project.
59+
60+
## Define a Program class with a Main method
61+
62+
A customized Program.cs file must be created instead of running the default Main method. The code added to the **Program** class enables the app to check for redirection, which isn't the default behavior of WinUI apps.
63+
64+
1. Navigate to Solution Explorer, right-click on the project name, and select **Add | Class**.
65+
1. Name the new class `Program.cs` and select **Add**.
66+
1. Add the following namespaces to the Program class, replacing any existing namespaces:
67+
68+
```csharp
69+
using System;
70+
using System.Diagnostics;
71+
using System.Runtime.InteropServices;
72+
using System.Threading;
73+
using System.Threading.Tasks;
74+
using Microsoft.UI.Dispatching;
75+
using Microsoft.UI.Xaml;
76+
using Microsoft.Windows.AppLifecycle;
77+
```
78+
79+
1. Replace the empty **Program** class with the following:
80+
81+
```csharp
82+
public class Program
83+
{
84+
[STAThread]
85+
static int Main(string[] args)
86+
{
87+
WinRT.ComWrappersSupport.InitializeComWrappers();
88+
bool isRedirect = DecideRedirection();
89+
90+
if (!isRedirect)
91+
{
92+
Application.Start((p) =>
93+
{
94+
var context = new DispatcherQueueSynchronizationContext(
95+
DispatcherQueue.GetForCurrentThread());
96+
SynchronizationContext.SetSynchronizationContext(context);
97+
_ = new App();
98+
});
99+
}
100+
101+
return 0;
102+
}
103+
}
104+
```
105+
106+
The **Main** method determines whether the app should redirect to the first instance or start a new instance after calling **DecideRedirection**, which we will define next.
107+
108+
1. Define the **DecideRedirection** method below the **Main** method:
109+
110+
```csharp
111+
private static bool DecideRedirection()
112+
{
113+
bool isRedirect = false;
114+
AppActivationArguments args = AppInstance.GetCurrent().GetActivatedEventArgs();
115+
ExtendedActivationKind kind = args.Kind;
116+
AppInstance keyInstance = AppInstance.FindOrRegisterForKey("MySingleInstanceApp");
117+
118+
if (keyInstance.IsCurrent)
119+
{
120+
keyInstance.Activated += OnActivated;
121+
}
122+
else
123+
{
124+
isRedirect = true;
125+
RedirectActivationTo(args, keyInstance);
126+
}
127+
128+
return isRedirect;
129+
}
130+
```
131+
132+
**DecideRedirection** determines if the app has been registered by registering a unique key that represents your app instance. Based on the result of key registration, it can determine if there's a current instance of the app running. After making the determination, the method knows whether to redirect or allow the app to continue launching the new instance. The **RedirectActivationTo** method is called if redirection is necessary.
133+
134+
1. Next, let's create the RedirectActivationTo method below the DecideRedirection method, along with the required DllImport statements. Add the following code to the Program class:
135+
136+
```csharp
137+
[DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
138+
private static extern IntPtr CreateEvent(
139+
IntPtr lpEventAttributes, bool bManualReset,
140+
bool bInitialState, string lpName);
141+
142+
[DllImport("kernel32.dll")]
143+
private static extern bool SetEvent(IntPtr hEvent);
144+
145+
[DllImport("ole32.dll")]
146+
private static extern uint CoWaitForMultipleObjects(
147+
uint dwFlags, uint dwMilliseconds, ulong nHandles,
148+
IntPtr[] pHandles, out uint dwIndex);
149+
150+
[DllImport("user32.dll")]
151+
static extern bool SetForegroundWindow(IntPtr hWnd);
152+
153+
private static IntPtr redirectEventHandle = IntPtr.Zero;
154+
155+
// Do the redirection on another thread, and use a non-blocking
156+
// wait method to wait for the redirection to complete.
157+
public static void RedirectActivationTo(AppActivationArguments args,
158+
AppInstance keyInstance)
159+
{
160+
redirectEventHandle = CreateEvent(IntPtr.Zero, true, false, null);
161+
Task.Run(() =>
162+
{
163+
keyInstance.RedirectActivationToAsync(args).AsTask().Wait();
164+
SetEvent(redirectEventHandle);
165+
});
166+
167+
uint CWMO_DEFAULT = 0;
168+
uint INFINITE = 0xFFFFFFFF;
169+
_ = CoWaitForMultipleObjects(
170+
CWMO_DEFAULT, INFINITE, 1,
171+
[redirectEventHandle], out uint handleIndex);
172+
173+
// Bring the window to the foreground
174+
Process process = Process.GetProcessById((int)keyInstance.ProcessId);
175+
SetForegroundWindow(process.MainWindowHandle);
176+
}
177+
```
178+
179+
The **RedirectActivationTo** method is responsible for redirecting the activation to the first instance of the app. It creates an event handle, starts a new thread to redirect the activation, and waits for the redirection to complete. After the redirection is complete, the method brings the window to the foreground.
180+
181+
1. Finally, define the helper method **OnActivated** below the **DecideRedirection** method:
182+
183+
```csharp
184+
private static void OnActivated(object sender, AppActivationArguments args)
185+
{
186+
ExtendedActivationKind kind = args.Kind;
187+
}
188+
```
189+
190+
## Test single-instancing via app deployment
191+
192+
Until this point, we've been testing the app by debugging within Visual Studio. However, we can only have one debugger running at once. This limitation prevents us from knowing whether the app is single-instanced because we can’t debug the same project twice at the same time. For an accurate test, we'll deploy the application to our local Windows client. After deploying, we can launch the app from the desktop like you would with any app installed on Windows.
193+
194+
1. Navigate to Solution Explorer, right-click on the project name, and select **Deploy**.
195+
1. Open the Start menu and click into the search field.
196+
1. Type your app's name in the search field.
197+
1. Click the app icon from the search result to launch your app.
198+
199+
> [!NOTE]
200+
> If you experience app crashes in release mode, there are some known issues with trimmed apps in the Windows App SDK. You can disable trimming in the project by setting the **PublishTrimmed** property to **false** for all build configurations in your project's `.pubxml` files. For more information, see [this issue](https://github.com/microsoft/microsoft-ui-xaml/issues/9914#issuecomment-2303010651) on GitHub.
201+
202+
1. Repeat steps 2 to 4 to launch the same app again and see if another instance opens. If the app is single-instanced, the first instance will be activated instead of a new instance opening.
203+
204+
> [!TIP]
205+
> You can optionally add some logging code to the **OnActivated** method to verify that the existing instance has been activated.
206+
207+
## Summary
208+
209+
All the code covered here is on [GitHub](https://github.com/jingwei-a-zhang/WinAppSDK-DrumPad), with branches for the different steps in the original [Windows blog series](https://blogs.windows.com/windowsdeveloper/2022/01/28/making-the-app-single-instanced-part-3/). See the [single-instancing](https://github.com/jingwei-a-zhang/WinAppSDK-DrumPad/tree/single-instancing) branch for code specific to this how-to. The `main` branch is the most comprehensive. The other branches are intended to show you how the app architecture evolved.
210+
211+
## Related content
212+
213+
[App instancing with the app lifecycle API](applifecycle-instancing.md)
214+
215+
[Making the app single-instanced (Part 3)](https://blogs.windows.com/windowsdeveloper/2022/01/28/making-the-app-single-instanced-part-3/)
216+
217+
[WinAppSDK-DrumPad sample on GitHub](https://github.com/jingwei-a-zhang/WinAppSDK-DrumPad)

0 commit comments

Comments
 (0)