using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Runtime.InteropServices.ComTypes; using System.Runtime.InteropServices; using System.Text; using System.Threading.Tasks; namespace FocusVolumeControl.AudioHelpers; public sealed class AppxPackage { private AppxPackage() { } public string Path { get; private set; } public string Logo { get; private set; } public string DisplayName { get; private set; } public static AppxPackage FromProcess(Process process) { try { return FromProcess(process.Handle); } catch { return null; } } public static AppxPackage FromProcess(int processId) { const int QueryLimitedInformation = 0x1000; IntPtr hProcess = OpenProcess(QueryLimitedInformation, false, processId); try { return FromProcess(hProcess); } finally { if (hProcess != IntPtr.Zero) { CloseHandle(hProcess); } } } static AppxPackage FromProcess(IntPtr hProcess) { if (hProcess == IntPtr.Zero) { return null; } int len = 0; GetPackageFullName(hProcess, ref len, null); if (len == 0) { return null; } var sb = new StringBuilder(len); string fullName = GetPackageFullName(hProcess, ref len, sb) == 0 ? sb.ToString() : null; if (string.IsNullOrEmpty(fullName)) // not an AppX { return null; } var package = QueryPackageInfo(fullName, PackageConstants.PACKAGE_FILTER_HEAD).First(); return package; } private static IEnumerable QueryPackageInfo(string fullName, PackageConstants flags) { IntPtr infoRef; OpenPackageInfoByFullName(fullName, 0, out infoRef); if (infoRef != IntPtr.Zero) { IntPtr infoBuffer = IntPtr.Zero; try { int len = 0; int count; GetPackageInfo(infoRef, flags, ref len, IntPtr.Zero, out count); if (len > 0) { var factory = (IAppxFactory)new AppxFactory(); infoBuffer = Marshal.AllocHGlobal(len); int res = GetPackageInfo(infoRef, flags, ref len, infoBuffer, out count); for (int i = 0; i < count; i++) { var info = (PACKAGE_INFO)Marshal.PtrToStructure(infoBuffer + i * Marshal.SizeOf(typeof(PACKAGE_INFO)), typeof(PACKAGE_INFO)); var package = new AppxPackage(); package.Path = Marshal.PtrToStringUni(info.path); // read manifest string manifestPath = System.IO.Path.Combine(package.Path, "AppXManifest.xml"); const int STGM_SHARE_DENY_NONE = 0x40; SHCreateStreamOnFileEx(manifestPath, STGM_SHARE_DENY_NONE, 0, false, IntPtr.Zero, out var strm); if (strm != null) { var reader = factory.CreateManifestReader(strm); var properties = reader.GetProperties(); properties.GetStringValue("DisplayName", out var displayName); if(displayName.StartsWith("ms-resource:")) { var packageFullName = Marshal.PtrToStringUni(info.packageFullName); displayName = LoadResourceString(fullName, displayName); } package.DisplayName = displayName; properties.GetStringValue("Logo", out var logo); package.Logo = logo; /* var apps = reader.GetApplications(); while (apps.GetHasCurrent()) { var app = apps.GetCurrent(); var appx = new AppxApp(app); appx.Description = GetStringValue(app, "Description"); appx.DisplayName = GetStringValue(app, "DisplayName"); appx.EntryPoint = GetStringValue(app, "EntryPoint"); appx.Executable = GetStringValue(app, "Executable"); appx.Id = GetStringValue(app, "Id"); appx.Logo = GetStringValue(app, "Logo"); appx.SmallLogo = GetStringValue(app, "SmallLogo"); appx.StartPage = GetStringValue(app, "StartPage"); appx.Square150x150Logo = GetStringValue(app, "Square150x150Logo"); appx.Square30x30Logo = GetStringValue(app, "Square30x30Logo"); appx.BackgroundColor = GetStringValue(app, "BackgroundColor"); appx.ForegroundText = GetStringValue(app, "ForegroundText"); appx.WideLogo = GetStringValue(app, "WideLogo"); appx.Wide310x310Logo = GetStringValue(app, "Wide310x310Logo"); appx.ShortName = GetStringValue(app, "ShortName"); appx.Square310x310Logo = GetStringValue(app, "Square310x310Logo"); appx.Square70x70Logo = GetStringValue(app, "Square70x70Logo"); appx.MinWidth = GetStringValue(app, "MinWidth"); package._apps.Add(appx); apps.MoveNext(); } */ Marshal.ReleaseComObject(strm); } yield return package; } Marshal.ReleaseComObject(factory); } } finally { if (infoBuffer != IntPtr.Zero) { Marshal.FreeHGlobal(infoBuffer); } ClosePackageInfo(infoRef); } } } static string LoadResourceString(string packageFullName, string resource) { if (packageFullName == null) throw new ArgumentNullException("packageFullName"); if (string.IsNullOrWhiteSpace(resource)) return null; const string resourceScheme = "ms-resource:"; if (!resource.StartsWith(resourceScheme)) return null; string part = resource.Substring(resourceScheme.Length); string url; if (part.StartsWith("/")) { url = resourceScheme + "//" + part; } else { url = resourceScheme + "///resources/" + part; } string source = string.Format("@{{{0}? {1}}}", packageFullName, url); var sb = new StringBuilder(1024); int i = SHLoadIndirectString(source, sb, sb.Capacity, IntPtr.Zero); if (i != 0) return null; return sb.ToString(); } [Guid("5842a140-ff9f-4166-8f5c-62f5b7b0c781"), ComImport] private class AppxFactory { } [Guid("BEB94909-E451-438B-B5A7-D79E767B75D8"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] private interface IAppxFactory { void _VtblGap0_2(); // skip 2 methods IAppxManifestReader CreateManifestReader(IStream inputStream); } [Guid("4E1BD148-55A0-4480-A3D1-15544710637C"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] private interface IAppxManifestReader { void _VtblGap0_1(); // skip 1 method IAppxManifestProperties GetProperties(); void _VtblGap1_5(); // skip 5 methods IAppxManifestApplicationsEnumerator GetApplications(); } [Guid("9EB8A55A-F04B-4D0D-808D-686185D4847A"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] private interface IAppxManifestApplicationsEnumerator { IAppxManifestApplication GetCurrent(); bool GetHasCurrent(); bool MoveNext(); } [Guid("5DA89BF4-3773-46BE-B650-7E744863B7E8"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] internal interface IAppxManifestApplication { [PreserveSig] int GetStringValue([MarshalAs(UnmanagedType.LPWStr)] string name, [MarshalAs(UnmanagedType.LPWStr)] out string vaue); } [Guid("03FAF64D-F26F-4B2C-AAF7-8FE7789B8BCA"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] private interface IAppxManifestProperties { [PreserveSig] int GetBoolValue([MarshalAs(UnmanagedType.LPWStr)] string name, out bool value); [PreserveSig] int GetStringValue([MarshalAs(UnmanagedType.LPWStr)] string name, [MarshalAs(UnmanagedType.LPWStr)] out string vaue); } [DllImport("shlwapi.dll", CharSet = CharSet.Unicode)] private static extern int SHLoadIndirectString(string pszSource, StringBuilder pszOutBuf, int cchOutBuf, IntPtr ppvReserved); [DllImport("shlwapi.dll", CharSet = CharSet.Unicode)] private static extern int SHCreateStreamOnFileEx(string fileName, int grfMode, int attributes, bool create, IntPtr reserved, out IStream stream); [DllImport("user32.dll")] private static extern int GetWindowThreadProcessId(IntPtr hWnd, out int lpdwProcessId); [DllImport("kernel32.dll")] private static extern IntPtr OpenProcess(int dwDesiredAccess, bool bInheritHandle, int dwProcessId); [DllImport("kernel32.dll")] private static extern bool CloseHandle(IntPtr hObject); [DllImport("kernel32.dll", CharSet = CharSet.Unicode)] private static extern int OpenPackageInfoByFullName(string packageFullName, int reserved, out IntPtr packageInfoReference); [DllImport("kernel32.dll", CharSet = CharSet.Unicode)] private static extern int GetPackageInfo(IntPtr packageInfoReference, PackageConstants flags, ref int bufferLength, IntPtr buffer, out int count); [DllImport("kernel32.dll", CharSet = CharSet.Unicode)] private static extern int ClosePackageInfo(IntPtr packageInfoReference); [DllImport("kernel32.dll", CharSet = CharSet.Unicode)] private static extern int GetPackageFullName(IntPtr hProcess, ref int packageFullNameLength, StringBuilder packageFullName); [Flags] private enum PackageConstants { PACKAGE_FILTER_ALL_LOADED = 0x00000000, PACKAGE_PROPERTY_FRAMEWORK = 0x00000001, PACKAGE_PROPERTY_RESOURCE = 0x00000002, PACKAGE_PROPERTY_BUNDLE = 0x00000004, PACKAGE_FILTER_HEAD = 0x00000010, PACKAGE_FILTER_DIRECT = 0x00000020, PACKAGE_FILTER_RESOURCE = 0x00000040, PACKAGE_FILTER_BUNDLE = 0x00000080, PACKAGE_INFORMATION_BASIC = 0x00000000, PACKAGE_INFORMATION_FULL = 0x00000100, PACKAGE_PROPERTY_DEVELOPMENT_MODE = 0x00010000, } [StructLayout(LayoutKind.Sequential, Pack = 4)] private struct PACKAGE_INFO { public int reserved; public int flags; public IntPtr path; public IntPtr packageFullName; public IntPtr packageFamilyName; public PACKAGE_ID packageId; } [StructLayout(LayoutKind.Sequential, Pack = 4)] private struct PACKAGE_ID { public int reserved; public AppxPackageArchitecture processorArchitecture; public ushort VersionRevision; public ushort VersionBuild; public ushort VersionMinor; public ushort VersionMajor; public IntPtr name; public IntPtr publisher; public IntPtr resourceId; public IntPtr publisherId; } } public enum AppxPackageArchitecture { x86 = 0, Arm = 5, x64 = 9, Neutral = 11, Arm64 = 12 }