Fixing WinUI3 COM Exception On App Relaunch With Admin Privileges
Hey guys! Ever run into a nasty COMException when trying to relaunch your WinUI3 app with administrator privileges? I was banging my head against the wall with this one, and I'm here to share the solution. Let's dive into the problem, the code, and how to fix it, so your app can launch smoothly, even when it needs those elevated permissions.
The Bug: COMException on Relaunch
So, here's the deal. I've got this WinUI3 C# app, and it's set up to handle file, protocol, and regular launch activations. The app is installed for a standard user account. When the app detects certain files, it needs to relaunch itself with administrator privileges. I use ActivateApplication for this, as shown in the code. That's when things go wrong.
After the relaunch, the app tries to grab its activation arguments using AppInstance.GetCurrent().GetActivatedEventArgs(). But boom! I get a COMException: "A specified logon session does not exist. It may already have been terminated."
This error basically means the app can't access the activation information after the relaunch, causing the entire process to fail. Not cool!
The Code: Where the Problem Lies
Let's take a look at the key parts of the code where the issue arises. It's pretty straightforward, but the devil's in the details. Here's the relevant code snippet from the App.xaml.cs file:
protected override async void OnLaunched(Microsoft.UI.Xaml.LaunchActivatedEventArgs args)
{
    AppActivationArguments activatedEventArgs = AppInstance.GetCurrent().GetActivatedEventArgs();
    if (activatedEventArgs.Kind is ExtendedActivationKind.File)
    {
        // Handle file activation...
    }
    else if (activatedEventArgs.Kind is ExtendedActivationKind.Protocol)
    {
        // Handle protocol activation...
    }
    // ... other activation handling
    // Attempt to relaunch with admin privileges if required...
    if (!IsElevated && Settings.PromptForElevationOnStartup || !IsElevated && requireAdminPrivilege)
    {
        if (Relaunch.RelaunchAppElevated(AUMID, BuildRelaunchArguments()))
        {
            Environment.Exit(0);
        }
    }
}
The problem occurs when the app is relaunched elevated and calls AppInstance.GetCurrent().GetActivatedEventArgs(). Because the app is now running under a different user context (the administrator account), the initial activation arguments are no longer accessible in the new process.
Steps to Reproduce the Bug
To reproduce the error, you'll need the following steps:
- Install Your App: Make sure your WinUI3 app is installed for a standard user account. This is important because the issue arises when the app needs to elevate privileges.
- Trigger Activation: Set up your app to handle file or protocol activation. For file activation, make sure your app is associated with certain file types. For protocol activation, create a custom protocol and trigger it.
- Implement Relaunch: Within your app's activation handling (e.g., inside the OnLaunchedmethod), check if admin privileges are needed (e.g., to access a file). If so, useActivateApplication(or a similar method) to relaunch the app with elevated privileges.
- Call GetActivatedEventArgs(): After the app is relaunched, inside theOnLaunchedmethod, callAppInstance.GetCurrent().GetActivatedEventArgs(). This is where theCOMExceptionwill occur.
The Fix: Passing Activation Arguments
The core of the solution lies in correctly passing the activation arguments during the relaunch process. The key is to serialize the relevant activation information from the initial process and pass it to the elevated instance. Here's a breakdown of the solution:
- 
Build Relaunch Arguments: Before relaunching the app with elevated privileges, you need to build the command-line arguments that will be passed to the new instance. This is done in the BuildRelaunchArgumentsmethod:private static string? BuildRelaunchArguments() { List<string> parts = []; if (!string.IsNullOrWhiteSpace(_activationAction)) { parts.Add({{content}}quot;--action={_activationAction}"); } else if (_activationIsFileActivation && !string.IsNullOrWhiteSpace(_activationFilePath)) { parts.Add("--action=PolicyEditor"); } if (!string.IsNullOrWhiteSpace(_activationFilePath)) { // Properly quote the file path for command line parsing (double embedded quotes if any). string safePath = _activationFilePath.Replace("\"", "\"\""); parts.Add({{content}}quot;--file=\"safePath\""); } if (parts.Count == 0) { return null; } StringBuilder builder = new(); for (int i = 0; i < parts.Count; i++) { if (i > 0) { _ = builder.Append(' '); } _ = builder.Append(parts[i]); } return builder.ToString(); }- Here, we're capturing the action and file path, if any, that triggered the initial activation. We serialize these into command-line arguments.
- If a file was activated, and the app needs to elevate, the file path is included.
 
- 
Relaunch with Arguments: Use the RelaunchAppElevatedmethod (or a similar approach) to relaunch the application, passing the built arguments:if (Relaunch.RelaunchAppElevated(AUMID, BuildRelaunchArguments())) { Environment.Exit(0); }
- 
Parse Arguments in the Elevated Instance: In the OnLaunchedmethod of the elevated instance, parse the command-line arguments to retrieve the activation information:string[] possibleArgs = Environment.GetCommandLineArgs(); // Look for our two keys string? actionArg = possibleArgs.FirstOrDefault(a => a.StartsWith("--action=", StringComparison.OrdinalIgnoreCase)); string? fileArg = possibleArgs.FirstOrDefault(a => a.StartsWith("--file=", StringComparison.OrdinalIgnoreCase)); // Action is mandatory if (actionArg is not null) { // Extract the action string action = actionArg["--action=".Length..].Trim(); if (!string.IsNullOrWhiteSpace(action)) { Logger.Write({{content}}quot;Parsed Action: {action}"); _activationAction = action; } // File is optional if (fileArg is not null) { string filePath = fileArg["--file=".Length..].Trim("\""); if (!string.IsNullOrWhiteSpace(filePath)) { Logger.Write({{content}}quot;Parsed File: {filePath}"); _activationFilePath = filePath; // If the selected file is not accessible with the privileges the app is currently running with, prompt for elevation requireAdminPrivilege = !FileAccessCheck.IsFileAccessibleForWrite(filePath); } } }- This code checks for the --actionand--filearguments. It then extracts the relevant data. Note: It's important to properly quote the file path for command line parsing and handle potential exceptions during the process.
- Now, you have the necessary activation information, allowing your app to correctly handle the relaunch and continue processing.
 
- This code checks for the 
Key Takeaways
- Serialize Activation Data: The core fix involves serializing the activation data (file paths, protocol arguments, etc.) before relaunching and passing it via command-line arguments.
- Parse Arguments in Elevated Instance: In the elevated instance, parse the command-line arguments to retrieve the activation data and handle the activation process.
- Handle File Paths Correctly: When working with file paths, make sure to properly quote them to avoid issues with command-line parsing. Handle the cases where the file might not be accessible and implement the necessary exception handling and logging.
By following these steps, you can successfully relaunch your WinUI3 app with administrator privileges and avoid the dreaded COMException. Your app will be able to handle file and protocol activations correctly, providing a seamless user experience. Happy coding, folks!