mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-04-06 03:07:04 +02:00
[AOT][CmdPal] Enable NativeAOT Compatibility for CmdPal Apps Extension (#39678)
<!-- Enter a brief description/summary of your PR here. What does it fix/what does it change/how was it tested (even manually, if necessary)? --> ## Summary of the Pull Request This PR introduces necessary changes to make the CmdPal.Apps extension compatible with NativeAOT publishing. The main updates include: 1. Project configuration updates: Added NativeAOT-related properties to the .csproj. 2. Native interop adjustments: > - Introduced NativeMethods.json and used [CsWin32](https://github.com/microsoft/cswin32) to generate P/Invoke bindings. > - Replaced some DllImport declarations with source-generated [LibraryImport] for improved AOT support.
This commit is contained in:
@@ -1,14 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Microsoft.CmdPal.Ext.Apps.Programs;
|
||||
|
||||
// Reference : https://stackoverflow.com/questions/32122679/getting-icon-of-modern-windows-app-from-a-desktop-application
|
||||
[Guid("5842a140-ff9f-4166-8f5c-62f5b7b0c781")]
|
||||
[ComImport]
|
||||
public class AppxFactory
|
||||
{
|
||||
}
|
||||
@@ -2,42 +2,75 @@
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
using ManagedCommon;
|
||||
using Microsoft.CmdPal.Ext.Apps.Utils;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Windows.Win32;
|
||||
using Windows.Win32.Foundation;
|
||||
using Windows.Win32.Storage.Packaging.Appx;
|
||||
using Windows.Win32.System.Com;
|
||||
using static Microsoft.CmdPal.Ext.Apps.Utils.Native;
|
||||
|
||||
namespace Microsoft.CmdPal.Ext.Apps.Programs;
|
||||
|
||||
public static class AppxPackageHelper
|
||||
{
|
||||
private static readonly IAppxFactory AppxFactory = (IAppxFactory)new AppxFactory();
|
||||
|
||||
// This function returns a list of attributes of applications
|
||||
internal static IEnumerable<IAppxManifestApplication> GetAppsFromManifest(IStream stream)
|
||||
internal static unsafe List<IntPtr> GetAppsFromManifest(IStream* stream)
|
||||
{
|
||||
var reader = AppxFactory.CreateManifestReader(stream);
|
||||
var manifestApps = reader.GetApplications();
|
||||
PInvoke.CoCreateInstance(typeof(AppxFactory).GUID, null, CLSCTX.CLSCTX_INPROC_SERVER, out IAppxFactory* appxFactory).ThrowOnFailure();
|
||||
using var handle = new SafeComHandle((IntPtr)appxFactory);
|
||||
|
||||
while (manifestApps.GetHasCurrent())
|
||||
IAppxManifestReader* reader = null;
|
||||
IAppxManifestApplicationsEnumerator* manifestApps = null;
|
||||
var result = new List<IntPtr>();
|
||||
|
||||
appxFactory->CreateManifestReader(stream, &reader);
|
||||
using var readerHandle = new SafeComHandle((IntPtr)reader);
|
||||
reader->GetApplications(&manifestApps);
|
||||
using var manifestAppsHandle = new SafeComHandle((IntPtr)manifestApps);
|
||||
|
||||
while (true)
|
||||
{
|
||||
var manifestApp = manifestApps.GetCurrent();
|
||||
var hr = manifestApp.GetStringValue("AppListEntry", out var appListEntry);
|
||||
_ = CheckHRAndReturnOrThrow(hr, appListEntry);
|
||||
if (appListEntry != "none")
|
||||
manifestApps->GetHasCurrent(out var hasCurrent);
|
||||
if (hasCurrent == false)
|
||||
{
|
||||
yield return manifestApp;
|
||||
break;
|
||||
}
|
||||
|
||||
manifestApps.MoveNext();
|
||||
}
|
||||
}
|
||||
IAppxManifestApplication* manifestApp = null;
|
||||
|
||||
internal static T CheckHRAndReturnOrThrow<T>(HRESULT hr, T result)
|
||||
{
|
||||
if (hr != HRESULT.S_OK)
|
||||
{
|
||||
Marshal.ThrowExceptionForHR((int)hr);
|
||||
try
|
||||
{
|
||||
manifestApps->GetCurrent(&manifestApp).ThrowOnFailure();
|
||||
|
||||
var hr = manifestApp->GetStringValue("AppListEntry", out var appListEntryPtr);
|
||||
var appListEntry = ComFreeHelper.GetStringAndFree(hr, appListEntryPtr);
|
||||
|
||||
if (appListEntry != "none")
|
||||
{
|
||||
result.Add((IntPtr)manifestApp);
|
||||
}
|
||||
else if (manifestApp != null)
|
||||
{
|
||||
manifestApp->Release();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (manifestApp != null)
|
||||
{
|
||||
manifestApp->Release();
|
||||
}
|
||||
|
||||
Logger.LogError($"Failed to get current application from manifest: {ex.Message}");
|
||||
}
|
||||
|
||||
manifestApps->MoveNext(out var hasNext);
|
||||
if (hasNext == false)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Microsoft.CmdPal.Ext.Apps.Programs;
|
||||
|
||||
// Reference : https://github.com/MicrosoftEdge/edge-launcher/blob/108e63df0b4cb5cd9d5e45aa7a264690851ec51d/MIcrosoftEdgeLauncherCsharp/Program.cs
|
||||
[Flags]
|
||||
public enum ActivateOptions
|
||||
{
|
||||
None = 0x00000000,
|
||||
DesignMode = 0x00000001,
|
||||
NoErrorUI = 0x00000002,
|
||||
NoSplashScreen = 0x00000004,
|
||||
}
|
||||
|
||||
// ApplicationActivationManager
|
||||
[ComImport]
|
||||
[Guid("2e941141-7f97-4756-ba1d-9decde894a3d")]
|
||||
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
|
||||
public interface IApplicationActivationManager
|
||||
{
|
||||
IntPtr ActivateApplication([In] string appUserModelId, [In] string arguments, [In] ActivateOptions options, [Out] out uint processId);
|
||||
|
||||
IntPtr ActivateForFile([In] string appUserModelId, [In] IntPtr /*IShellItemArray* */ itemArray, [In] string verb, [Out] out uint processId);
|
||||
|
||||
IntPtr ActivateForProtocol([In] string appUserModelId, [In] IntPtr /* IShellItemArray* */itemArray, [Out] out uint processId);
|
||||
}
|
||||
|
||||
// Application Activation Manager Class
|
||||
[ComImport]
|
||||
[Guid("45BA127D-10A8-46EA-8AB7-56EA9078943C")]
|
||||
public class ApplicationActivationManager : IApplicationActivationManager
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)/*, PreserveSig*/]
|
||||
public extern IntPtr ActivateApplication([In] string appUserModelId, [In] string arguments, [In] ActivateOptions options, [Out] out uint processId);
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
|
||||
public extern IntPtr ActivateForFile([In] string appUserModelId, [In] IntPtr /*IShellItemArray* */ itemArray, [In] string verb, [Out] out uint processId);
|
||||
|
||||
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
|
||||
public extern IntPtr ActivateForProtocol([In] string appUserModelId, [In] IntPtr /* IShellItemArray* */itemArray, [Out] out uint processId);
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using Windows.Win32.System.Com;
|
||||
|
||||
namespace Microsoft.CmdPal.Ext.Apps.Programs;
|
||||
|
||||
[Guid("BEB94909-E451-438B-B5A7-D79E767B75D8")]
|
||||
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
|
||||
public interface IAppxFactory
|
||||
{
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.NamingRules", "SA1300:Element should begin with upper-case letter", Justification = "Implements COM Interface")]
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "Implements COM Interface")]
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores", Justification = "Implements COM Interface")]
|
||||
void _VtblGap0_2(); // skip 2 methods
|
||||
|
||||
internal IAppxManifestReader CreateManifestReader(IStream inputStream);
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using static Microsoft.CmdPal.Ext.Apps.Utils.Native;
|
||||
|
||||
namespace Microsoft.CmdPal.Ext.Apps.Programs;
|
||||
|
||||
[Guid("5DA89BF4-3773-46BE-B650-7E744863B7E8")]
|
||||
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
|
||||
public interface IAppxManifestApplication
|
||||
{
|
||||
[PreserveSig]
|
||||
HRESULT GetStringValue([MarshalAs(UnmanagedType.LPWStr)] string name, [MarshalAs(UnmanagedType.LPWStr)] out string value);
|
||||
|
||||
[PreserveSig]
|
||||
HRESULT GetAppUserModelId([MarshalAs(UnmanagedType.LPWStr)] out string value);
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Microsoft.CmdPal.Ext.Apps.Programs;
|
||||
|
||||
[Guid("9EB8A55A-F04B-4D0D-808D-686185D4847A")]
|
||||
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
|
||||
public interface IAppxManifestApplicationsEnumerator
|
||||
{
|
||||
IAppxManifestApplication GetCurrent();
|
||||
|
||||
bool GetHasCurrent();
|
||||
|
||||
bool MoveNext();
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using static Microsoft.CmdPal.Ext.Apps.Utils.Native;
|
||||
|
||||
namespace Microsoft.CmdPal.Ext.Apps.Programs;
|
||||
|
||||
[Guid("03FAF64D-F26F-4B2C-AAF7-8FE7789B8BCA")]
|
||||
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
|
||||
public interface IAppxManifestProperties
|
||||
{
|
||||
[PreserveSig]
|
||||
HRESULT GetBoolValue([MarshalAs(UnmanagedType.LPWStr)] string name, out bool value);
|
||||
|
||||
[PreserveSig]
|
||||
HRESULT GetStringValue([MarshalAs(UnmanagedType.LPWStr)] string name, [MarshalAs(UnmanagedType.LPWStr)] out string value);
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Microsoft.CmdPal.Ext.Apps.Programs;
|
||||
|
||||
[Guid("4E1BD148-55A0-4480-A3D1-15544710637C")]
|
||||
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
|
||||
public interface IAppxManifestReader
|
||||
{
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.NamingRules", "SA1300:Element should begin with upper-case letter", Justification = "Implements COM Interface")]
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "Implements COM Interface")]
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores", Justification = "Implements COM Interface")]
|
||||
void _VtblGap0_1(); // skip 1 method
|
||||
|
||||
IAppxManifestProperties GetProperties();
|
||||
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.NamingRules", "SA1300:Element should begin with upper-case letter", Justification = "Implements COM Interface")]
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "Implements COM Interface")]
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores", Justification = "Implements COM Interface")]
|
||||
void _VtblGap1_5(); // skip 5 methods
|
||||
|
||||
IAppxManifestApplicationsEnumerator GetApplications();
|
||||
}
|
||||
@@ -5,6 +5,7 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.IO;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
@@ -16,7 +17,7 @@ namespace Microsoft.CmdPal.Ext.Apps.Programs;
|
||||
/// <summary>
|
||||
/// Provides access to NTFS reparse points in .Net.
|
||||
/// </summary>
|
||||
public static class ReparsePoint
|
||||
public static partial class ReparsePoint
|
||||
{
|
||||
#pragma warning disable SA1310 // Field names should not contain underscore
|
||||
|
||||
@@ -36,7 +37,7 @@ public static class ReparsePoint
|
||||
#pragma warning restore SA1310 // Field names should not contain underscore
|
||||
|
||||
[Flags]
|
||||
private enum FileAccessType : uint
|
||||
internal enum FileAccessType : uint
|
||||
{
|
||||
DELETE = 0x00010000,
|
||||
READ_CONTROL = 0x00020000,
|
||||
@@ -100,7 +101,7 @@ public static class ReparsePoint
|
||||
}
|
||||
|
||||
[Flags]
|
||||
private enum FileShareType : uint
|
||||
internal enum FileShareType : uint
|
||||
{
|
||||
None = 0x00000000,
|
||||
Read = 0x00000001,
|
||||
@@ -108,7 +109,7 @@ public static class ReparsePoint
|
||||
Delete = 0x00000004,
|
||||
}
|
||||
|
||||
private enum CreationDisposition : uint
|
||||
internal enum CreationDisposition : uint
|
||||
{
|
||||
New = 1,
|
||||
CreateAlways = 2,
|
||||
@@ -118,7 +119,7 @@ public static class ReparsePoint
|
||||
}
|
||||
|
||||
[Flags]
|
||||
private enum FileAttributes : uint
|
||||
internal enum FileAttributes : uint
|
||||
{
|
||||
Readonly = 0x00000001,
|
||||
Hidden = 0x00000002,
|
||||
@@ -195,8 +196,9 @@ public static class ReparsePoint
|
||||
public AppExecutionAliasReparseTagBufferLayoutVersion Version;
|
||||
}
|
||||
|
||||
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
|
||||
private static extern bool DeviceIoControl(
|
||||
[LibraryImport("kernel32.dll", SetLastError = true)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
internal static partial bool DeviceIoControl(
|
||||
IntPtr hDevice,
|
||||
uint dwIoControlCode,
|
||||
IntPtr inBuffer,
|
||||
@@ -206,8 +208,8 @@ public static class ReparsePoint
|
||||
out int pBytesReturned,
|
||||
IntPtr lpOverlapped);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
|
||||
private static extern IntPtr CreateFile(
|
||||
[LibraryImport("kernel32.dll", SetLastError = true, StringMarshalling = StringMarshalling.Utf16)]
|
||||
internal static partial int CreateFile(
|
||||
string lpFileName,
|
||||
FileAccessType dwDesiredAccess,
|
||||
FileShareType dwShareMode,
|
||||
@@ -284,15 +286,18 @@ public static class ReparsePoint
|
||||
ThrowLastWin32Error("Unable to get information about reparse point.");
|
||||
}
|
||||
|
||||
AppExecutionAliasReparseTagHeader aliasReparseHeader = Marshal.PtrToStructure<AppExecutionAliasReparseTagHeader>(outBuffer);
|
||||
|
||||
if (aliasReparseHeader.ReparseTag == IO_REPARSE_TAG_APPEXECLINK)
|
||||
unsafe
|
||||
{
|
||||
var metadata = AppExecutionAliasMetadata.FromPersistedRepresentationIntPtr(
|
||||
outBuffer,
|
||||
aliasReparseHeader.Version);
|
||||
var aliasReparseHeader = Unsafe.Read<AppExecutionAliasReparseTagHeader>((void*)outBuffer);
|
||||
|
||||
return metadata.ExePath;
|
||||
if (aliasReparseHeader.ReparseTag == IO_REPARSE_TAG_APPEXECLINK)
|
||||
{
|
||||
var metadata = AppExecutionAliasMetadata.FromPersistedRepresentationIntPtr(
|
||||
outBuffer,
|
||||
aliasReparseHeader.Version);
|
||||
|
||||
return metadata.ExePath;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
@@ -319,61 +324,65 @@ public static class ReparsePoint
|
||||
|
||||
public static AppExecutionAliasMetadata FromPersistedRepresentationIntPtr(IntPtr reparseDataBufferPtr, AppExecutionAliasReparseTagBufferLayoutVersion version)
|
||||
{
|
||||
var dataOffset = Marshal.SizeOf<AppExecutionAliasReparseTagHeader>();
|
||||
var dataBufferPtr = reparseDataBufferPtr + dataOffset;
|
||||
|
||||
string? packageFullName = null;
|
||||
string? packageFamilyName = null;
|
||||
string? aumid = null;
|
||||
string? exePath = null;
|
||||
|
||||
VerifyVersion(version);
|
||||
|
||||
switch (version)
|
||||
unsafe
|
||||
{
|
||||
case AppExecutionAliasReparseTagBufferLayoutVersion.Initial:
|
||||
packageFullName = Marshal.PtrToStringUni(dataBufferPtr);
|
||||
if (packageFullName is not null)
|
||||
{
|
||||
dataBufferPtr += Encoding.Unicode.GetByteCount(packageFullName) + Encoding.Unicode.GetByteCount("\0");
|
||||
aumid = Marshal.PtrToStringUni(dataBufferPtr);
|
||||
var dataOffset = Unsafe.SizeOf<AppExecutionAliasReparseTagHeader>();
|
||||
|
||||
if (aumid is not null)
|
||||
var dataBufferPtr = reparseDataBufferPtr + dataOffset;
|
||||
|
||||
string? packageFullName = null;
|
||||
string? packageFamilyName = null;
|
||||
string? aumid = null;
|
||||
string? exePath = null;
|
||||
|
||||
VerifyVersion(version);
|
||||
|
||||
switch (version)
|
||||
{
|
||||
case AppExecutionAliasReparseTagBufferLayoutVersion.Initial:
|
||||
packageFullName = Marshal.PtrToStringUni(dataBufferPtr);
|
||||
if (packageFullName is not null)
|
||||
{
|
||||
dataBufferPtr += Encoding.Unicode.GetByteCount(aumid) + Encoding.Unicode.GetByteCount("\0");
|
||||
exePath = Marshal.PtrToStringUni(dataBufferPtr);
|
||||
dataBufferPtr += Encoding.Unicode.GetByteCount(packageFullName) + Encoding.Unicode.GetByteCount("\0");
|
||||
aumid = Marshal.PtrToStringUni(dataBufferPtr);
|
||||
|
||||
if (aumid is not null)
|
||||
{
|
||||
dataBufferPtr += Encoding.Unicode.GetByteCount(aumid) + Encoding.Unicode.GetByteCount("\0");
|
||||
exePath = Marshal.PtrToStringUni(dataBufferPtr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
break;
|
||||
|
||||
case AppExecutionAliasReparseTagBufferLayoutVersion.PackageFamilyName:
|
||||
case AppExecutionAliasReparseTagBufferLayoutVersion.MultiAppTypeSupport:
|
||||
packageFamilyName = Marshal.PtrToStringUni(dataBufferPtr);
|
||||
case AppExecutionAliasReparseTagBufferLayoutVersion.PackageFamilyName:
|
||||
case AppExecutionAliasReparseTagBufferLayoutVersion.MultiAppTypeSupport:
|
||||
packageFamilyName = Marshal.PtrToStringUni(dataBufferPtr);
|
||||
|
||||
if (packageFamilyName is not null)
|
||||
{
|
||||
dataBufferPtr += Encoding.Unicode.GetByteCount(packageFamilyName) + Encoding.Unicode.GetByteCount("\0");
|
||||
aumid = Marshal.PtrToStringUni(dataBufferPtr);
|
||||
|
||||
if (aumid is not null)
|
||||
if (packageFamilyName is not null)
|
||||
{
|
||||
dataBufferPtr += Encoding.Unicode.GetByteCount(aumid) + Encoding.Unicode.GetByteCount("\0");
|
||||
dataBufferPtr += Encoding.Unicode.GetByteCount(packageFamilyName) + Encoding.Unicode.GetByteCount("\0");
|
||||
aumid = Marshal.PtrToStringUni(dataBufferPtr);
|
||||
|
||||
exePath = Marshal.PtrToStringUni(dataBufferPtr);
|
||||
if (aumid is not null)
|
||||
{
|
||||
dataBufferPtr += Encoding.Unicode.GetByteCount(aumid) + Encoding.Unicode.GetByteCount("\0");
|
||||
|
||||
exePath = Marshal.PtrToStringUni(dataBufferPtr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
break;
|
||||
}
|
||||
|
||||
return new AppExecutionAliasMetadata
|
||||
{
|
||||
PackageFullName = packageFullName ?? string.Empty,
|
||||
PackageFamilyName = packageFamilyName ?? string.Empty,
|
||||
Aumid = aumid ?? string.Empty,
|
||||
ExePath = exePath ?? string.Empty,
|
||||
};
|
||||
}
|
||||
|
||||
return new AppExecutionAliasMetadata
|
||||
{
|
||||
PackageFullName = packageFullName ?? string.Empty,
|
||||
PackageFamilyName = packageFamilyName ?? string.Empty,
|
||||
Aumid = aumid ?? string.Empty,
|
||||
ExePath = exePath ?? string.Empty,
|
||||
};
|
||||
}
|
||||
|
||||
private static void VerifyVersion(AppExecutionAliasReparseTagBufferLayoutVersion version)
|
||||
|
||||
@@ -3,17 +3,19 @@
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.IO.Abstractions;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml.Linq;
|
||||
using ManagedCommon;
|
||||
using Microsoft.CmdPal.Ext.Apps.Utils;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
using Windows.Win32;
|
||||
using Windows.Win32.Foundation;
|
||||
using Windows.Win32.Storage.Packaging.Appx;
|
||||
using Windows.Win32.System.Com;
|
||||
using static Microsoft.CmdPal.Ext.Apps.Utils.Native;
|
||||
|
||||
namespace Microsoft.CmdPal.Ext.Apps.Programs;
|
||||
|
||||
@@ -55,7 +57,7 @@ public partial class UWP
|
||||
FamilyName = package.FamilyName;
|
||||
}
|
||||
|
||||
public void InitializeAppInfo(string installedLocation)
|
||||
public unsafe void InitializeAppInfo(string installedLocation)
|
||||
{
|
||||
Location = installedLocation;
|
||||
LocationLocalized = ShellLocalization.Instance.GetLocalizedPath(installedLocation);
|
||||
@@ -65,26 +67,31 @@ public partial class UWP
|
||||
InitPackageVersion(namespaces);
|
||||
|
||||
const uint noAttribute = 0x80;
|
||||
|
||||
var access = (uint)STGM.READ;
|
||||
var hResult = PInvoke.SHCreateStreamOnFileEx(path, access, noAttribute, false, null, out IStream stream);
|
||||
|
||||
// S_OK
|
||||
if (hResult == 0)
|
||||
const uint STGMREAD = 0x00000000;
|
||||
try
|
||||
{
|
||||
Apps = AppxPackageHelper.GetAppsFromManifest(stream).Select(appInManifest => new UWPApplication(appInManifest, this)).Where(a =>
|
||||
IStream* stream = null;
|
||||
PInvoke.SHCreateStreamOnFileEx(path, STGMREAD, noAttribute, false, null, &stream).ThrowOnFailure();
|
||||
using var streamHandle = new SafeComHandle((IntPtr)stream);
|
||||
|
||||
Apps = AppxPackageHelper.GetAppsFromManifest(stream).Select(appInManifest =>
|
||||
{
|
||||
using var appHandle = new SafeComHandle(appInManifest);
|
||||
return new UWPApplication((IAppxManifestApplication*)appInManifest, this);
|
||||
}).Where(a =>
|
||||
{
|
||||
var valid =
|
||||
!string.IsNullOrEmpty(a.UserModelId) &&
|
||||
!string.IsNullOrEmpty(a.DisplayName) &&
|
||||
a.AppListEntry != "none";
|
||||
|
||||
!string.IsNullOrEmpty(a.UserModelId) &&
|
||||
!string.IsNullOrEmpty(a.DisplayName) &&
|
||||
a.AppListEntry != "none";
|
||||
return valid;
|
||||
}).ToList();
|
||||
}
|
||||
else
|
||||
catch (Exception ex)
|
||||
{
|
||||
Apps = Array.Empty<UWPApplication>();
|
||||
Logger.LogError($"Failed to initialize UWP app info for {Name} ({FullName}): {ex.Message}");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,35 +130,36 @@ public partial class UWP
|
||||
{
|
||||
var windows10 = new Version(10, 0);
|
||||
var support = Environment.OSVersion.Version.Major >= windows10.Major;
|
||||
if (support)
|
||||
{
|
||||
var applications = CurrentUserPackages().AsParallel().SelectMany(p =>
|
||||
{
|
||||
UWP u;
|
||||
try
|
||||
{
|
||||
u = new UWP(p);
|
||||
u.InitializeAppInfo(p.InstalledLocation);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError(ex.Message);
|
||||
return Array.Empty<UWPApplication>();
|
||||
}
|
||||
|
||||
return u.Apps;
|
||||
});
|
||||
|
||||
var updatedListWithoutDisabledApps = applications
|
||||
.Where(t1 => AllAppsSettings.Instance.DisabledProgramSources.All(x => x.UniqueIdentifier != t1.UniqueIdentifier))
|
||||
.Select(x => x);
|
||||
|
||||
return updatedListWithoutDisabledApps.ToArray();
|
||||
}
|
||||
else
|
||||
if (!support)
|
||||
{
|
||||
return Array.Empty<UWPApplication>();
|
||||
}
|
||||
|
||||
var appsBag = new ConcurrentBag<UWPApplication>();
|
||||
|
||||
Parallel.ForEach(CurrentUserPackages(), p =>
|
||||
{
|
||||
try
|
||||
{
|
||||
var u = new UWP(p);
|
||||
u.InitializeAppInfo(p.InstalledLocation);
|
||||
|
||||
foreach (var app in u.Apps)
|
||||
{
|
||||
if (AllAppsSettings.Instance.DisabledProgramSources.All(x => x.UniqueIdentifier != app.UniqueIdentifier))
|
||||
{
|
||||
appsBag.Add(app);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError(ex.Message);
|
||||
}
|
||||
});
|
||||
|
||||
return appsBag.ToArray();
|
||||
}
|
||||
|
||||
private static IEnumerable<IPackage> CurrentUserPackages()
|
||||
|
||||
@@ -6,6 +6,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO.Abstractions;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Xml;
|
||||
using ManagedCommon;
|
||||
@@ -13,7 +14,9 @@ using Microsoft.CmdPal.Ext.Apps.Commands;
|
||||
using Microsoft.CmdPal.Ext.Apps.Properties;
|
||||
using Microsoft.CmdPal.Ext.Apps.Utils;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
using static Microsoft.CmdPal.Ext.Apps.Utils.Native;
|
||||
using Windows.Win32;
|
||||
using Windows.Win32.Foundation;
|
||||
using Windows.Win32.Storage.Packaging.Appx;
|
||||
using PackageVersion = Microsoft.CmdPal.Ext.Apps.Programs.UWP.PackageVersion;
|
||||
using Theme = Microsoft.CmdPal.Ext.Apps.Utils.Theme;
|
||||
|
||||
@@ -97,27 +100,27 @@ public class UWPApplication : IProgram
|
||||
return commands;
|
||||
}
|
||||
|
||||
public UWPApplication(IAppxManifestApplication manifestApp, UWP package)
|
||||
internal unsafe UWPApplication(IAppxManifestApplication* manifestApp, UWP package)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(manifestApp);
|
||||
|
||||
var hr = manifestApp.GetAppUserModelId(out var tmpUserModelId);
|
||||
UserModelId = AppxPackageHelper.CheckHRAndReturnOrThrow(hr, tmpUserModelId);
|
||||
var hr = manifestApp->GetAppUserModelId(out var tmpUserModelIdPtr);
|
||||
UserModelId = ComFreeHelper.GetStringAndFree(hr, tmpUserModelIdPtr);
|
||||
|
||||
hr = manifestApp.GetAppUserModelId(out var tmpUniqueIdentifier);
|
||||
UniqueIdentifier = AppxPackageHelper.CheckHRAndReturnOrThrow(hr, tmpUniqueIdentifier);
|
||||
manifestApp->GetAppUserModelId(out var tmpUniqueIdentifierPtr);
|
||||
UniqueIdentifier = ComFreeHelper.GetStringAndFree(hr, tmpUniqueIdentifierPtr);
|
||||
|
||||
hr = manifestApp.GetStringValue("DisplayName", out var tmpDisplayName);
|
||||
DisplayName = AppxPackageHelper.CheckHRAndReturnOrThrow(hr, tmpDisplayName);
|
||||
manifestApp->GetStringValue("DisplayName", out var tmpDisplayNamePtr);
|
||||
DisplayName = ComFreeHelper.GetStringAndFree(hr, tmpDisplayNamePtr);
|
||||
|
||||
hr = manifestApp.GetStringValue("Description", out var tmpDescription);
|
||||
Description = AppxPackageHelper.CheckHRAndReturnOrThrow(hr, tmpDescription);
|
||||
manifestApp->GetStringValue("Description", out var tmpDescriptionPtr);
|
||||
Description = ComFreeHelper.GetStringAndFree(hr, tmpDescriptionPtr);
|
||||
|
||||
hr = manifestApp.GetStringValue("BackgroundColor", out var tmpBackgroundColor);
|
||||
BackgroundColor = AppxPackageHelper.CheckHRAndReturnOrThrow(hr, tmpBackgroundColor);
|
||||
manifestApp->GetStringValue("BackgroundColor", out var tmpBackgroundColorPtr);
|
||||
BackgroundColor = ComFreeHelper.GetStringAndFree(hr, tmpBackgroundColorPtr);
|
||||
|
||||
hr = manifestApp.GetStringValue("EntryPoint", out var tmpEntryPoint);
|
||||
EntryPoint = AppxPackageHelper.CheckHRAndReturnOrThrow(hr, tmpEntryPoint);
|
||||
manifestApp->GetStringValue("EntryPoint", out var tmpEntryPointPtr);
|
||||
EntryPoint = ComFreeHelper.GetStringAndFree(hr, tmpEntryPointPtr);
|
||||
|
||||
Package = package ?? throw new ArgumentNullException(nameof(package));
|
||||
|
||||
@@ -166,7 +169,7 @@ public class UWPApplication : IProgram
|
||||
return false;
|
||||
}
|
||||
|
||||
internal string ResourceFromPri(string packageFullName, string resourceReference)
|
||||
internal unsafe string ResourceFromPri(string packageFullName, string resourceReference)
|
||||
{
|
||||
const string prefix = "ms-resource:";
|
||||
|
||||
@@ -200,30 +203,8 @@ public class UWPApplication : IProgram
|
||||
parsedFallback = prefix + "///" + key;
|
||||
}
|
||||
|
||||
var outBuffer = new StringBuilder(128);
|
||||
var source = $"@{{{packageFullName}? {parsed}}}";
|
||||
var capacity = (uint)outBuffer.Capacity;
|
||||
var hResult = SHLoadIndirectString(source, outBuffer, capacity, IntPtr.Zero);
|
||||
if (hResult != HRESULT.S_OK)
|
||||
if (string.IsNullOrEmpty(parsedFallback))
|
||||
{
|
||||
if (!string.IsNullOrEmpty(parsedFallback))
|
||||
{
|
||||
var sourceFallback = $"@{{{packageFullName}? {parsedFallback}}}";
|
||||
hResult = SHLoadIndirectString(sourceFallback, outBuffer, capacity, IntPtr.Zero);
|
||||
if (hResult == HRESULT.S_OK)
|
||||
{
|
||||
var loaded = outBuffer.ToString();
|
||||
if (!string.IsNullOrEmpty(loaded))
|
||||
{
|
||||
return loaded;
|
||||
}
|
||||
else
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// https://github.com/Wox-launcher/Wox/issues/964
|
||||
// known hresult 2147942522:
|
||||
// 'Microsoft Corporation' violates pattern constraint of '\bms-resource:.{1,256}'.
|
||||
@@ -232,17 +213,40 @@ public class UWPApplication : IProgram
|
||||
// Microsoft.BingFoodAndDrink_3.0.4.336_x64__8wekyb3d8bbwe: ms-resource:AppDescription
|
||||
return string.Empty;
|
||||
}
|
||||
else
|
||||
|
||||
var capacity = 1024U;
|
||||
PWSTR outBuffer = new PWSTR((char*)(void*)Marshal.AllocHGlobal((int)capacity * sizeof(char)));
|
||||
var source = $"@{{{packageFullName}? {parsed}}}";
|
||||
void* reserved = null;
|
||||
|
||||
try
|
||||
{
|
||||
PInvoke.SHLoadIndirectString(source, outBuffer, capacity, ref reserved).ThrowOnFailure();
|
||||
|
||||
var loaded = outBuffer.ToString();
|
||||
if (!string.IsNullOrEmpty(loaded))
|
||||
return string.IsNullOrEmpty(loaded) ? string.Empty : loaded;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
try
|
||||
{
|
||||
return loaded;
|
||||
var sourceFallback = $"@{{{packageFullName}?{parsedFallback}}}";
|
||||
PInvoke.SHLoadIndirectString(sourceFallback, outBuffer, capacity, ref reserved).ThrowOnFailure();
|
||||
var loaded = outBuffer.ToString();
|
||||
return string.IsNullOrEmpty(loaded) ? string.Empty : loaded;
|
||||
}
|
||||
else
|
||||
catch (Exception)
|
||||
{
|
||||
// ProgramLogger.Exception($"Unable to load resource {resourceReference} from {packageFullName}", new InvalidOperationException(), GetType(), packageFullName);
|
||||
return string.Empty;
|
||||
}
|
||||
finally
|
||||
{
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
Marshal.FreeHGlobal((IntPtr)outBuffer.Value);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -258,13 +262,12 @@ public class UWPApplication : IProgram
|
||||
{ PackageVersion.Windows8, "SmallLogo" },
|
||||
};
|
||||
|
||||
internal string LogoUriFromManifest(IAppxManifestApplication app)
|
||||
internal unsafe string LogoUriFromManifest(IAppxManifestApplication* app)
|
||||
{
|
||||
if (_logoKeyFromVersion.TryGetValue(Package.Version, out var key))
|
||||
{
|
||||
var hr = app.GetStringValue(key, out var logoUriFromApp);
|
||||
_ = AppxPackageHelper.CheckHRAndReturnOrThrow(hr, logoUriFromApp);
|
||||
return logoUriFromApp;
|
||||
var hr = app->GetStringValue(key, out var logoUriFromAppPtr);
|
||||
return ComFreeHelper.GetStringAndFree(hr, logoUriFromAppPtr);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -349,7 +352,7 @@ public class UWPApplication : IProgram
|
||||
var prefix = path.Substring(0, end);
|
||||
var paths = new List<string> { };
|
||||
const int appIconSize = 36;
|
||||
var targetSizes = new List<int> { 16, 24, 30, 36, 44, 60, 72, 96, 128, 180, 256 }.AsParallel();
|
||||
var targetSizes = new List<int> { 16, 24, 30, 36, 44, 60, 72, 96, 128, 180, 256 };
|
||||
var pathFactorPairs = new Dictionary<string, int>();
|
||||
|
||||
foreach (var factor in targetSizes)
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Design;
|
||||
using System.Diagnostics;
|
||||
@@ -841,18 +842,69 @@ public class Win32Program : IProgram
|
||||
var disabledProgramsList = settings.DisabledProgramSources;
|
||||
|
||||
// Get all paths but exclude all normal .Executables
|
||||
paths.UnionWith(sources
|
||||
.AsParallel()
|
||||
.SelectMany(source => source.IsEnabled ? source.GetPaths() : Enumerable.Empty<string>())
|
||||
.Where(programPath => disabledProgramsList.All(x => x.UniqueIdentifier != programPath))
|
||||
.Where(path => !ExecutableApplicationExtensions.Contains(Extension(path))));
|
||||
runCommandPaths.UnionWith(runCommandSources
|
||||
.AsParallel()
|
||||
.SelectMany(source => source.IsEnabled ? source.GetPaths() : Enumerable.Empty<string>())
|
||||
.Where(programPath => disabledProgramsList.All(x => x.UniqueIdentifier != programPath)));
|
||||
var pathBag = new ConcurrentBag<string>();
|
||||
|
||||
var programs = paths.AsParallel().Select(source => GetProgramFromPath(source));
|
||||
var runCommandPrograms = runCommandPaths.AsParallel().Select(source => GetRunCommandProgramFromPath(source));
|
||||
Parallel.ForEach(sources, source =>
|
||||
{
|
||||
if (!source.IsEnabled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var path in source.GetPaths())
|
||||
{
|
||||
if (disabledProgramsList.All(x => x.UniqueIdentifier != path) &&
|
||||
!ExecutableApplicationExtensions.Contains(Extension(path)))
|
||||
{
|
||||
pathBag.Add(path);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
paths.UnionWith(pathBag);
|
||||
|
||||
var runCommandPathBag = new ConcurrentBag<string>();
|
||||
|
||||
Parallel.ForEach(runCommandSources, source =>
|
||||
{
|
||||
if (!source.IsEnabled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var path in source.GetPaths())
|
||||
{
|
||||
if (disabledProgramsList.All(x => x.UniqueIdentifier != path))
|
||||
{
|
||||
runCommandPathBag.Add(path);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
runCommandPaths.UnionWith(runCommandPathBag);
|
||||
|
||||
var programsList = new ConcurrentBag<Win32Program>();
|
||||
Parallel.ForEach(paths, source =>
|
||||
{
|
||||
var program = GetProgramFromPath(source);
|
||||
if (program != null)
|
||||
{
|
||||
programsList.Add(program);
|
||||
}
|
||||
});
|
||||
|
||||
var runCommandProgramsList = new ConcurrentBag<Win32Program>();
|
||||
Parallel.ForEach(runCommandPaths, source =>
|
||||
{
|
||||
var program = GetRunCommandProgramFromPath(source);
|
||||
if (program != null)
|
||||
{
|
||||
runCommandProgramsList.Add(program);
|
||||
}
|
||||
});
|
||||
|
||||
var programs = programsList.ToList();
|
||||
var runCommandPrograms = runCommandProgramsList.ToList();
|
||||
|
||||
return DeduplicatePrograms(programs.Concat(runCommandPrograms).Where(program => program?.Valid == true));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user