Skip to content

Commit ec72cf7

Browse files
committed
Fix issue with window not moving to the foreground
1 parent 9006c67 commit ec72cf7

File tree

1 file changed

+55
-9
lines changed

1 file changed

+55
-9
lines changed

hub/apps/windows-app-sdk/applifecycle/applifecycle-single-instance.md

Lines changed: 55 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,8 @@ A customized Program.cs file must be created instead of running the default Main
6767

6868
```csharp
6969
using System;
70+
using System.Diagnostics;
71+
using System.Runtime.InteropServices;
7072
using System.Threading;
7173
using System.Threading.Tasks;
7274
using Microsoft.UI.Dispatching;
@@ -80,10 +82,10 @@ A customized Program.cs file must be created instead of running the default Main
8082
public class Program
8183
{
8284
[STAThread]
83-
static async Task<int> Main(string[] args)
85+
static int Main(string[] args)
8486
{
8587
WinRT.ComWrappersSupport.InitializeComWrappers();
86-
bool isRedirect = await DecideRedirection();
88+
bool isRedirect = DecideRedirection();
8789

8890
if (!isRedirect)
8991
{
@@ -92,7 +94,7 @@ A customized Program.cs file must be created instead of running the default Main
9294
var context = new DispatcherQueueSynchronizationContext(
9395
DispatcherQueue.GetForCurrentThread());
9496
SynchronizationContext.SetSynchronizationContext(context);
95-
new App();
97+
_ = new App();
9698
});
9799
}
98100

@@ -106,7 +108,7 @@ A customized Program.cs file must be created instead of running the default Main
106108
1. Define the **DecideRedirection** method below the **Main** method:
107109

108110
```csharp
109-
private static async Task<bool> DecideRedirection()
111+
private static bool DecideRedirection()
110112
{
111113
bool isRedirect = false;
112114
AppActivationArguments args = AppInstance.GetCurrent().GetActivatedEventArgs();
@@ -120,27 +122,71 @@ A customized Program.cs file must be created instead of running the default Main
120122
else
121123
{
122124
isRedirect = true;
123-
await keyInstance.RedirectActivationToAsync(args);
125+
RedirectActivationTo(args, keyInstance);
124126
}
125127

126128
return isRedirect;
127129
}
128130
```
129131

130-
**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.
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.
131180

132181
1. Finally, define the helper method **OnActivated** below the **DecideRedirection** method:
133182

134183
```csharp
135184
private static void OnActivated(object sender, AppActivationArguments args)
136185
{
137186
ExtendedActivationKind kind = args.Kind;
138-
// TODO: Handle the activation kind
139187
}
140188
```
141189

142-
**OnActivated** is called when the app is activated. You can handle the activation arguments here.
143-
144190
## Test single-instancing via app deployment
145191

146192
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.

0 commit comments

Comments
 (0)