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  }