MegaBites-AI/Windows-powershell
0372
1// Copyright (c) Microsoft Corporation.2// Licensed under the MIT License.3 4using System;5using System.Diagnostics;6using System.Management.Automation;7using System.Reflection;8using System.Threading;9 10using static Microsoft.PowerShell.ComInterfaces;11 12namespace Microsoft.PowerShell13{14 internal static class TaskbarJumpList15 {16 // Creating a JumpList entry takes around 55ms when the PowerShell process is interactive and17 // owns the current window (otherwise it does a fast exit anyway). Since there is no 'GET' like API,18 // we always have to execute this call because we do not know if it has been created yet.19 // The JumpList does persist as long as the filepath of the executable does not change but there20 // could be disruptions to it like e.g. the bi-annual Windows update, we decided to21 // not over-optimize this and always create the JumpList as a non-blocking background STA thread instead.22 internal static void CreateRunAsAdministratorJumpList()23 {24 // The STA apartment state is not supported on NanoServer and Windows IoT.25 // Plus, there is not need to create jump list in those environment anyways.26 if (!Platform.IsWindowsDesktop)27 {28 return;29 }30 31 // Some COM APIs are implicitly STA only, therefore the executing thread must run in STA.32 var thread = new Thread(() =>33 {34 try35 {36 CreateElevatedEntry(ConsoleHostStrings.RunAsAdministrator);37 }38 catch (Exception)39 {40 // Due to COM threading complexity there might still be sporadic failures but they can be41 // ignored as creating the JumpList is not critical and persists after its first creation.42 }43 });44 45 try46 {47 thread.SetApartmentState(ApartmentState.STA);48 thread.Start();49 }50 catch (ThreadStartException)51 {52 // STA may not be supported on some platforms53 }54 }55 56 private static void CreateElevatedEntry(string title)57 {58 // Check startupInfo first to know if the current shell is interactive and owns a window before proceeding59 // This check is fast (less than 1ms) and allows for quick-exit60 GetStartupInfo(out StartUpInfo startupInfo);61 const uint STARTF_USESHOWWINDOW = 0x00000001;62 const ushort SW_HIDE = 0;63 if (((startupInfo.dwFlags & STARTF_USESHOWWINDOW) == 1) && (startupInfo.wShowWindow != SW_HIDE))64 {65 string cmdPath = Assembly.GetEntryAssembly().Location.Replace(".dll", ".exe");66 67 // Check for maximum available slots in JumpList and start creating the custom Destination List68 var CLSID_DestinationList = new Guid(@"77f10cf0-3db5-4966-b520-b7c54fd35ed6");69 const uint CLSCTX_INPROC_SERVER = 1;70 var IID_IUnknown = new Guid("00000000-0000-0000-C000-000000000046");71 var hResult = CoCreateInstance(ref CLSID_DestinationList, null, CLSCTX_INPROC_SERVER, ref IID_IUnknown, out object pCustDestListobj);72 if (hResult < 0)73 {74 Debug.Fail($"Creating ICustomDestinationList failed with HResult '{hResult}'.");75 return;76 }77 78 var pCustDestList = (ICustomDestinationList)pCustDestListobj;79 hResult = pCustDestList.BeginList(out uint uMaxSlots, new Guid(@"92CA9DCD-5622-4BBA-A805-5E9F541BD8C9"), out object pRemovedItems);80 if (hResult < 0)81 {82 Debug.Fail($"BeginList on ICustomDestinationList failed with HResult '{hResult}'.");83 return;84 }85 86 if (uMaxSlots >= 1)87 {88 // Create JumpListLink89 var nativeShellLink = (IShellLinkW)new CShellLink();90 var nativePropertyStore = (IPropertyStore)nativeShellLink;91 nativeShellLink.SetPath(cmdPath);92 nativeShellLink.SetShowCmd(0);93 var shellLinkDataList = (IShellLinkDataListW)nativeShellLink;94 shellLinkDataList.GetFlags(out uint flags);95 flags |= 0x00800000; // SLDF_ALLOW_LINK_TO_LINK96 flags |= 0x00002000; // SLDF_RUNAS_USER97 shellLinkDataList.SetFlags(flags);98 var PKEY_TITLE = new PropertyKey(new Guid("{F29F85E0-4FF9-1068-AB91-08002B27B3D9}"), 2);99 hResult = nativePropertyStore.SetValue(in PKEY_TITLE, new PropVariant(title));100 if (hResult < 0)101 {102 pCustDestList.AbortList();103 Debug.Fail($"SetValue on IPropertyStore with title '{title}' failed with HResult '{hResult}'.");104 return;105 }106 107 hResult = nativePropertyStore.Commit();108 if (hResult < 0)109 {110 pCustDestList.AbortList();111 Debug.Fail($"Commit on IPropertyStore failed with HResult '{hResult}'.");112 return;113 }114 115 // Create collection and add JumpListLink116 var CLSID_EnumerableObjectCollection = new Guid(@"2d3468c1-36a7-43b6-ac24-d3f02fd9607a");117 const uint CLSCTX_INPROC_HANDLER = 2;118 const uint CLSCTX_INPROC = CLSCTX_INPROC_SERVER | CLSCTX_INPROC_HANDLER;119 hResult = CoCreateInstance(ref CLSID_EnumerableObjectCollection, null, CLSCTX_INPROC, ref IID_IUnknown, out object instance);120 if (hResult < 0)121 {122 pCustDestList.AbortList();123 Debug.Fail($"Creating IObjectCollection failed with HResult '{hResult}'.");124 return;125 }126 127 var pShortCutCollection = (IObjectCollection)instance;128 pShortCutCollection.AddObject((IShellLinkW)nativePropertyStore);129 130 // Add collection to custom destination list and commit the result131 hResult = pCustDestList.AddUserTasks((IObjectArray)pShortCutCollection);132 if (hResult < 0)133 {134 pCustDestList.AbortList();135 Debug.Fail($"AddUserTasks on ICustomDestinationList failed with HResult '{hResult}'.");136 return;137 }138 139 pCustDestList.CommitList();140 }141 }142 }143 }144}145 