CommandLauncher.cs
1 // Copyright (c) Microsoft Corporation 2 // The Microsoft Corporation licenses this file to you under the MIT license. 3 // See the LICENSE file in the project root for more information. 4 5 using System.ComponentModel; 6 using System.Runtime.InteropServices; 7 using ManagedCommon; 8 9 namespace Microsoft.CmdPal.Ext.Bookmarks.Helpers; 10 11 internal static class CommandLauncher 12 { 13 /// <summary> 14 /// Launches the classified item. 15 /// </summary> 16 /// <param name="classification">Classification produced by CommandClassifier.</param> 17 /// <param name="runAsAdmin">Optional: force elevation if possible.</param> 18 public static bool Launch(Classification classification, bool runAsAdmin = false) 19 { 20 switch (classification.Launch) 21 { 22 case LaunchMethod.ExplorerOpen: 23 // Folders and shell: URIs are best handled by explorer.exe 24 // You can notice the difference with Recycle Bin for example: 25 // - "explorer ::{645FF040-5081-101B-9F08-00AA002F954E}" 26 // - "::{645FF040-5081-101B-9F08-00AA002F954E}" 27 return ShellHelpers.OpenInShell("explorer.exe", classification.Target); 28 29 case LaunchMethod.ActivateAppId: 30 return ActivateAppId(classification.Target, classification.Arguments); 31 32 case LaunchMethod.ShellExecute: 33 default: 34 return ShellHelpers.OpenInShell(classification.Target, classification.Arguments, classification.WorkingDirectory, runAsAdmin ? ShellHelpers.ShellRunAsType.Administrator : ShellHelpers.ShellRunAsType.None); 35 } 36 } 37 38 private static bool ActivateAppId(string aumidOrAppsFolder, string? arguments) 39 { 40 const string shellAppsFolder = "shell:AppsFolder\\"; 41 try 42 { 43 if (aumidOrAppsFolder.StartsWith(shellAppsFolder, StringComparison.OrdinalIgnoreCase)) 44 { 45 aumidOrAppsFolder = aumidOrAppsFolder[shellAppsFolder.Length..]; 46 } 47 48 ApplicationActivationManager.ActivateApplication(aumidOrAppsFolder, arguments, 0, out _); 49 return true; 50 } 51 catch (Exception ex) 52 { 53 Logger.LogError($"Can't activate AUMID using app store '{aumidOrAppsFolder}'", ex); 54 } 55 56 try 57 { 58 ShellHelpers.OpenInShell(shellAppsFolder + aumidOrAppsFolder, arguments); 59 return true; 60 } 61 catch (Exception ex) 62 { 63 Logger.LogError($"Can't activate AUMID using shell '{aumidOrAppsFolder}'", ex); 64 } 65 66 return false; 67 } 68 69 private static class ApplicationActivationManager 70 { 71 public static void ActivateApplication(string aumid, string? args, int options, out uint pid) 72 { 73 var mgr = (IApplicationActivationManager)new _ApplicationActivationManager(); 74 var hr = mgr.ActivateApplication(aumid, args ?? string.Empty, options, out pid); 75 if (hr < 0) 76 { 77 throw new Win32Exception(hr); 78 } 79 } 80 81 [ComImport] 82 [Guid("45BA127D-10A8-46EA-8AB7-56EA9078943C")] 83 [System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.NamingRules", "SA1300:Element should begin with upper-case letter", Justification = "Private class")] 84 private class _ApplicationActivationManager; 85 86 [ComImport] 87 [Guid("2E941141-7F97-4756-BA1D-9DECDE894A3D")] 88 [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] 89 private interface IApplicationActivationManager 90 { 91 int ActivateApplication( 92 [MarshalAs(UnmanagedType.LPWStr)] string appUserModelId, 93 [MarshalAs(UnmanagedType.LPWStr)] string arguments, 94 int options, 95 out uint processId); 96 } 97 } 98 }