获得"现代"的图标桌面应用程序中的Windows应用程序?

时间:2015-08-20 15:44:42

标签: c# wpf windows-store-apps icons pinvoke

我开发了一个函数,它返回给定窗口句柄的窗口图标。它看起来像这样。

private static BitmapSource GetWindowIcon(IntPtr windowHandle)
{
    var hIcon = default(IntPtr);
    hIcon = SendMessage(windowHandle, WM_GETICON, ICON_BIG, IntPtr.Zero);

    if (hIcon == IntPtr.Zero)
        hIcon = GetClassLongPtr(windowHandle, GCL_HICON);

    if (hIcon == IntPtr.Zero)
    {
        hIcon = LoadIcon(IntPtr.Zero, (IntPtr)0x7F00/*IDI_APPLICATION*/);
    }

    if (hIcon != IntPtr.Zero)
    {
        return Imaging.CreateBitmapSourceFromHIcon(hIcon, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions());
    } else {
        throw new InvalidOperationException("Could not load window icon.");
    }
}

我将此功能与GetForegroundWindow结合使用,以获取活动窗口的图标。

然而,它似乎为通用应用程序产生了相同的暗淡图标。

是否有可能以某种方式从正在运行的通用应用程序中获取平铺图像或图标?

2 个答案:

答案 0 :(得分:12)

以下是一些示例代码,演示如何完成此操作。请注意:

  1. 你应该运行这个平均值,否则你将无法访问具有应用程序资源的文件夹(我认为,并没有真正检查自己,因为调查我授予自己访问该文件夹的内容)。
  2. 现代应用程序在ApplicationFrameHost主机进程下运行。你需要一些技巧来获取实际的可执行文件(比如Calculator.exe),这些技巧在代码中被注释。
  3. 现代应用程序清单包含徽标的路径,但可能有几个徽标(黑色,白色,constract white为例)。你需要一些逻辑来选择一个。我没有详细调查这个。
  4. 我在Windows 10中的计算器应用程序上测试了它,它工作正常。但是,当然需要更多应用程序以确保一切正常。
  5. 以下是代码:

    public static class IconHelper {
        public static BitmapSource GetForegroundWindowIcon() {
            var hwnd = GetForegroundWindow();
            uint pid;
            GetWindowThreadProcessId(hwnd, out pid);
            Process proc = Process.GetProcessById((int) pid);
            // modern apps run under ApplicationFrameHost host process in windows 10
            // don't forget to check if that is true for windows 8 - maybe they use another host there
            if (proc.MainModule.ModuleName == "ApplicationFrameHost.exe") {
                // this should be modern app
                return GetModernAppLogo(hwnd);
            }
            return GetWindowIcon(hwnd);
        }
    
        public static BitmapSource GetModernAppLogo(IntPtr hwnd) {
            // get folder where actual app resides
            var exePath = GetModernAppProcessPath(hwnd); 
            var dir = System.IO.Path.GetDirectoryName(exePath);
            var manifestPath = System.IO.Path.Combine(dir, "AppxManifest.xml");            
            if (File.Exists(manifestPath)) {
                // this is manifest file
                string pathToLogo;
                using (var fs = File.OpenRead(manifestPath)) {
                    var manifest = XDocument.Load(fs);
                    const string ns = "http://schemas.microsoft.com/appx/manifest/foundation/windows10";
                    // rude parsing - take more care here
                    pathToLogo = manifest.Root.Element(XName.Get("Properties", ns)).Element(XName.Get("Logo", ns)).Value;
                }
                // now here it is tricky again - there are several files that match logo, for example
                // black, white, contrast white. Here we choose first, but you might do differently
                string finalLogo = null;
                // serach for all files that match file name in Logo element but with any suffix (like "Logo.black.png, Logo.white.png etc)
                foreach (var logoFile in Directory.GetFiles(System.IO.Path.Combine(dir, System.IO.Path.GetDirectoryName(pathToLogo)),
                    System.IO.Path.GetFileNameWithoutExtension(pathToLogo) + "*" + System.IO.Path.GetExtension(pathToLogo))) {
                    finalLogo = logoFile;
                    break;
                }
    
                if (System.IO.File.Exists(finalLogo)) {
                    using (var fs = File.OpenRead(finalLogo)) {
                        var img = new BitmapImage() {
                        };
                        img.BeginInit();
                        img.StreamSource = fs;
                        img.CacheOption = BitmapCacheOption.OnLoad;
                        img.EndInit();
                        return img;
                    }
                }
            }
            return null;
        }
    
        private static string GetModernAppProcessPath(IntPtr hwnd) {
            uint pid = 0;
            GetWindowThreadProcessId(hwnd, out pid);            
            // now this is a bit tricky. Modern apps are hosted inside ApplicationFrameHost process, so we need to find
            // child window which does NOT belong to this process. This should be the process we need
            var children = GetChildWindows(hwnd);
            foreach (var childHwnd in children) {
                uint childPid = 0;
                GetWindowThreadProcessId(childHwnd, out childPid);
                if (childPid != pid) {
                    // here we are
                    Process childProc = Process.GetProcessById((int) childPid);
                    return childProc.MainModule.FileName;
                }
            }
    
            throw new Exception("Cannot find a path to Modern App executable file");
        }
    
        public static BitmapSource GetWindowIcon(IntPtr windowHandle) {
            var hIcon = default(IntPtr);
            hIcon = SendMessage(windowHandle, WM_GETICON, (IntPtr) ICON_BIG, IntPtr.Zero);
    
            if (hIcon == IntPtr.Zero)
                hIcon = GetClassLongPtr(windowHandle, GCL_HICON);
    
            if (hIcon == IntPtr.Zero) {
                hIcon = LoadIcon(IntPtr.Zero, (IntPtr) 0x7F00 /*IDI_APPLICATION*/);
            }
    
            if (hIcon != IntPtr.Zero) {
                return Imaging.CreateBitmapSourceFromHIcon(hIcon, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions());
            }
            else {
                throw new InvalidOperationException("Could not load window icon.");
            }
        }
    
        #region Helper methods
        const UInt32 WM_GETICON = 0x007F;
        const int ICON_BIG = 1;
        const int GCL_HICON = -14;
    
        private static List<IntPtr> GetChildWindows(IntPtr parent)
        {
            List<IntPtr> result = new List<IntPtr>();
            GCHandle listHandle = GCHandle.Alloc(result);
            try
            {
                EnumWindowProc childProc = new EnumWindowProc(EnumWindow);
                EnumChildWindows(parent, childProc, GCHandle.ToIntPtr(listHandle));
            }
            finally
            {
                if (listHandle.IsAllocated)
                    listHandle.Free();
            }
            return result;
        }
    
        private static bool EnumWindow(IntPtr handle, IntPtr pointer)
        {
            GCHandle gch = GCHandle.FromIntPtr(pointer);
            List<IntPtr> list = gch.Target as List<IntPtr>;
            if (list == null)
            {
                throw new InvalidCastException("GCHandle Target could not be cast as List<IntPtr>");
            }
            list.Add(handle);
            //  You can modify this to check to see if you want to cancel the operation, then return a null here
            return true;
        }
    
        public delegate bool EnumWindowProc(IntPtr hwnd, IntPtr lParam);
        [DllImport("user32.Dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool EnumChildWindows(IntPtr parentHandle, EnumWindowProc callback, IntPtr lParam);
    
        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern int GetWindowThreadProcessId(IntPtr handle, out uint processId);
    
        [DllImport("user32.dll")]
        private static extern IntPtr GetForegroundWindow();
    
        [DllImport("user32.dll")]
        static extern IntPtr LoadIcon(IntPtr hInstance, IntPtr lpIconName);
    
        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);
    
        private static IntPtr GetClassLongPtr(IntPtr hWnd, int nIndex)
        {
            if (IntPtr.Size > 4)
                return GetClassLongPtr64(hWnd, nIndex);
            else
                return new IntPtr(GetClassLongPtr32(hWnd, nIndex));
        }
    
        [DllImport("user32.dll", EntryPoint = "GetClassLong")]
        public static extern uint GetClassLongPtr32(IntPtr hWnd, int nIndex);
    
        [DllImport("user32.dll", EntryPoint = "GetClassLongPtr")]
        public static extern IntPtr GetClassLongPtr64(IntPtr hWnd, int nIndex);
        #endregion
    
    }
    

    用法只是:

    var icon = IconHelper.GetForegroundWindowIcon();
    

答案 1 :(得分:5)

大多数“现代应用”(或Windows应用商店应用,或AppX应用模型中的应用)信息都可以从官方API查询。

您可以从GetPackageFullName function开始(它获取指定进程的程序包全名)。获得包名全名后,您可以使用Package Query API获取更多信息。

这些API是原生的,因此根据我的知识,它们在.NET Framework中没有等价物。但是,它们可以通过WinRT应用程序以某种方式访问​​(并且您确实可以从标准.NET Framework应用程序访问某些WinRT API,但这很糟糕)。

因此,我构建了一个实用程序类,允许您从这些应用程序中获取信息。这是一个示例应用程序,它转储所有当前加载的Windows应用商店包和应用

这些应用程序中包含的图像很特殊,因为它们被定义为可以使用限定符调整以形成最终路径的资源(资产)键。例如,这里记录了这一点:How to name resources using qualifiers (HTML)和此处:Quickstart: Using file or image resources (HTML)

你可以找到的问题在很大程度上取决于应用程序本身,因此确定你可以使用哪个图像并不是很容易,我没有找到任何API,所以我编写了一个获得最高规模的样本给定资源名称的图像作为示例(FindHighestScaleQualifiedImagePath)。您可以从此路径加载WPF BitmapSource(或任何其他成像平台资源)。

    static void Main(string[] args)
    {
        foreach (var p in Process.GetProcesses())
        {
            var package = AppxPackage.FromProcess(p);
            if (package != null)
            {
                Show(0, package);
                Console.WriteLine();
                Console.WriteLine();
            }
        }
    }

    private static void Show(int indent, AppxPackage package)
    {
        string sindent = new string(' ', indent);
        Console.WriteLine(sindent + "FullName               : " + package.FullName);
        Console.WriteLine(sindent + "FamilyName             : " + package.FamilyName);
        Console.WriteLine(sindent + "IsFramework            : " + package.IsFramework);
        Console.WriteLine(sindent + "ApplicationUserModelId : " + package.ApplicationUserModelId);
        Console.WriteLine(sindent + "Path                   : " + package.Path);
        Console.WriteLine(sindent + "Publisher              : " + package.Publisher);
        Console.WriteLine(sindent + "PublisherId            : " + package.PublisherId);
        Console.WriteLine(sindent + "Logo                   : " + package.Logo);
        Console.WriteLine(sindent + "Best Logo Path         : " + package.FindHighestScaleQualifiedImagePath(package.Logo));
        Console.WriteLine(sindent + "ProcessorArchitecture  : " + package.ProcessorArchitecture);
        Console.WriteLine(sindent + "Version                : " + package.Version);
        Console.WriteLine(sindent + "PublisherDisplayName   : " + package.PublisherDisplayName);
        Console.WriteLine(sindent + "   Localized           : " + package.LoadResourceString(package.PublisherDisplayName));
        Console.WriteLine(sindent + "DisplayName            : " + package.DisplayName);
        Console.WriteLine(sindent + "   Localized           : " + package.LoadResourceString(package.DisplayName));
        Console.WriteLine(sindent + "Description            : " + package.Description);
        Console.WriteLine(sindent + "   Localized           : " + package.LoadResourceString(package.Description));

        Console.WriteLine(sindent + "Apps                   :");
        int i = 0;
        foreach (var app in package.Apps)
        {
            Console.WriteLine(sindent + " App [" + i + "] Description       : " + app.Description);
            Console.WriteLine(sindent + "   Localized           : " + package.LoadResourceString(app.Description));
            Console.WriteLine(sindent + " App [" + i + "] DisplayName       : " + app.DisplayName);
            Console.WriteLine(sindent + "   Localized           : " + package.LoadResourceString(app.DisplayName));
            Console.WriteLine(sindent + " App [" + i + "] ShortName         : " + app.ShortName);
            Console.WriteLine(sindent + "   Localized           : " + package.LoadResourceString(app.ShortName));
            Console.WriteLine(sindent + " App [" + i + "] EntryPoint        : " + app.EntryPoint);
            Console.WriteLine(sindent + " App [" + i + "] Executable        : " + app.Executable);
            Console.WriteLine(sindent + " App [" + i + "] Id                : " + app.Id);
            Console.WriteLine(sindent + " App [" + i + "] Logo              : " + app.Logo);
            Console.WriteLine(sindent + " App [" + i + "] SmallLogo         : " + app.SmallLogo);
            Console.WriteLine(sindent + " App [" + i + "] StartPage         : " + app.StartPage);
            Console.WriteLine(sindent + " App [" + i + "] Square150x150Logo : " + app.Square150x150Logo);
            Console.WriteLine(sindent + " App [" + i + "] Square30x30Logo   : " + app.Square30x30Logo);
            Console.WriteLine(sindent + " App [" + i + "] BackgroundColor   : " + app.BackgroundColor);
            Console.WriteLine(sindent + " App [" + i + "] ForegroundText    : " + app.ForegroundText);
            Console.WriteLine(sindent + " App [" + i + "] WideLogo          : " + app.WideLogo);
            Console.WriteLine(sindent + " App [" + i + "] Wide310x310Logo   : " + app.Wide310x310Logo);
            Console.WriteLine(sindent + " App [" + i + "] Square310x310Logo : " + app.Square310x310Logo);
            Console.WriteLine(sindent + " App [" + i + "] Square70x70Logo   : " + app.Square70x70Logo);
            Console.WriteLine(sindent + " App [" + i + "] MinWidth          : " + app.MinWidth);
            Console.WriteLine(sindent + " App [" + i + "] Square71x71Logo   : " + app.GetStringValue("Square71x71Logzo"));
            i++;
        }

        Console.WriteLine(sindent + "Deps                   :");
        foreach (var dep in package.DependencyGraph)
        {
            Show(indent + 1, dep);
        }
    }

public sealed class AppxPackage
{
    private List<AppxApp> _apps = new List<AppxApp>();
    private IAppxManifestProperties _properties;

    private AppxPackage()
    {
    }

    public string FullName { get; private set; }
    public string Path { get; private set; }
    public string Publisher { get; private set; }
    public string PublisherId { get; private set; }
    public string ResourceId { get; private set; }
    public string FamilyName { get; private set; }
    public string ApplicationUserModelId { get; private set; }
    public string Logo { get; private set; }
    public string PublisherDisplayName { get; private set; }
    public string Description { get; private set; }
    public string DisplayName { get; private set; }
    public bool IsFramework { get; private set; }
    public Version Version { get; private set; }
    public AppxPackageArchitecture ProcessorArchitecture { get; private set; }

    public IReadOnlyList<AppxApp> Apps
    {
        get
        {
            return _apps;
        }
    }

    public IEnumerable<AppxPackage> DependencyGraph
    {
        get
        {
            return QueryPackageInfo(FullName, PackageConstants.PACKAGE_FILTER_ALL_LOADED).Where(p => p.FullName != FullName);
        }
    }

    public string FindHighestScaleQualifiedImagePath(string resourceName)
    {
        if (resourceName == null)
            throw new ArgumentNullException("resourceName");

        const string scaleToken = ".scale-";
        var sizes = new List<int>();
        string name = System.IO.Path.GetFileNameWithoutExtension(resourceName);
        string ext = System.IO.Path.GetExtension(resourceName);
        foreach (var file in Directory.EnumerateFiles(System.IO.Path.Combine(Path, System.IO.Path.GetDirectoryName(resourceName)), name + scaleToken + "*" + ext))
        {
            string fileName = System.IO.Path.GetFileNameWithoutExtension(file);
            int pos = fileName.IndexOf(scaleToken) + scaleToken.Length;
            string sizeText = fileName.Substring(pos);
            int size;
            if (int.TryParse(sizeText, out size))
            {
                sizes.Add(size);
            }
        }
        if (sizes.Count == 0)
            return null;

        sizes.Sort();
        return System.IO.Path.Combine(Path, System.IO.Path.GetDirectoryName(resourceName), name + scaleToken + sizes.Last() + ext);
    }

    public override string ToString()
    {
        return FullName;
    }

    public static AppxPackage FromWindow(IntPtr handle)
    {
        int processId;
        GetWindowThreadProcessId(handle, out processId);
        if (processId == 0)
            return null;

        return FromProcess(processId);
    }

    public static AppxPackage FromProcess(Process process)
    {
        if (process == null)
        {
            process = Process.GetCurrentProcess();
        }

        try
        {
            return FromProcess(process.Handle);
        }
        catch
        {
            // probably access denied on .Handle
            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);
            }
        }
    }

    public static AppxPackage FromProcess(IntPtr hProcess)
    {
        if (hProcess == IntPtr.Zero)
            return null;

        // hprocess must have been opened with QueryLimitedInformation
        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();

        len = 0;
        GetApplicationUserModelId(hProcess, ref len, null);
        sb = new StringBuilder(len);
        package.ApplicationUserModelId = GetApplicationUserModelId(hProcess, ref len, sb) == 0 ? sb.ToString() : null;
        return package;
    }

    public string GetPropertyStringValue(string name)
    {
        if (name == null)
            throw new ArgumentNullException("name");

        return GetStringValue(_properties, name);
    }

    public bool GetPropertyBoolValue(string name)
    {
        if (name == null)
            throw new ArgumentNullException("name");

        return GetBoolValue(_properties, name);
    }

    public string LoadResourceString(string resource)
    {
        return LoadResourceString(FullName, resource);
    }

    private static IEnumerable<AppxPackage> 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.FamilyName = Marshal.PtrToStringUni(info.packageFamilyName);
                        package.FullName = Marshal.PtrToStringUni(info.packageFullName);
                        package.Path = Marshal.PtrToStringUni(info.path);
                        package.Publisher = Marshal.PtrToStringUni(info.packageId.publisher);
                        package.PublisherId = Marshal.PtrToStringUni(info.packageId.publisherId);
                        package.ResourceId = Marshal.PtrToStringUni(info.packageId.resourceId);
                        package.ProcessorArchitecture = info.packageId.processorArchitecture;
                        package.Version = new Version(info.packageId.VersionMajor, info.packageId.VersionMinor, info.packageId.VersionBuild, info.packageId.VersionRevision);

                        // read manifest
                        string manifestPath = System.IO.Path.Combine(package.Path, "AppXManifest.xml");
                        const int STGM_SHARE_DENY_NONE = 0x40;
                        IStream strm;
                        SHCreateStreamOnFileEx(manifestPath, STGM_SHARE_DENY_NONE, 0, false, IntPtr.Zero, out strm);
                        if (strm != null)
                        {
                            var reader = factory.CreateManifestReader(strm);
                            package._properties = reader.GetProperties();
                            package.Description = package.GetPropertyStringValue("Description");
                            package.DisplayName = package.GetPropertyStringValue("DisplayName");
                            package.Logo = package.GetPropertyStringValue("Logo");
                            package.PublisherDisplayName = package.GetPropertyStringValue("PublisherDisplayName");
                            package.IsFramework = package.GetPropertyBoolValue("Framework");

                            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);
            }
        }
    }

    public 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();
    }

    private static string GetStringValue(IAppxManifestProperties props, string name)
    {
        if (props == null)
            return null;

        string value;
        props.GetStringValue(name, out value);
        return value;
    }

    private static bool GetBoolValue(IAppxManifestProperties props, string name)
    {
        bool value;
        props.GetBoolValue(name, out value);
        return value;
    }

    internal static string GetStringValue(IAppxManifestApplication app, string name)
    {
        string value;
        app.GetStringValue(name, out value);
        return value;
    }

    [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);

    [DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
    private static extern int GetApplicationUserModelId(IntPtr hProcess, ref int applicationUserModelIdLength, StringBuilder applicationUserModelId);

    [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 sealed class AppxApp
{
    private AppxPackage.IAppxManifestApplication _app;

    internal AppxApp(AppxPackage.IAppxManifestApplication app)
    {
        _app = app;
    }

    public string GetStringValue(string name)
    {
        if (name == null)
            throw new ArgumentNullException("name");

        return AppxPackage.GetStringValue(_app, name);
    }

    // we code well-known but there are others (like Square71x71Logo, Square44x44Logo, whatever ...)
    // https://msdn.microsoft.com/en-us/library/windows/desktop/hh446703.aspx
    public string Description { get; internal set; }
    public string DisplayName { get; internal set; }
    public string EntryPoint { get; internal set; }
    public string Executable { get; internal set; }
    public string Id { get; internal set; }
    public string Logo { get; internal set; }
    public string SmallLogo { get; internal set; }
    public string StartPage { get; internal set; }
    public string Square150x150Logo { get; internal set; }
    public string Square30x30Logo { get; internal set; }
    public string BackgroundColor { get; internal set; }
    public string ForegroundText { get; internal set; }
    public string WideLogo { get; internal set; }
    public string Wide310x310Logo { get; internal set; }
    public string ShortName { get; internal set; }
    public string Square310x310Logo { get; internal set; }
    public string Square70x70Logo { get; internal set; }
    public string MinWidth { get; internal set; }
}

public enum AppxPackageArchitecture
{
    x86 = 0,
    Arm = 5,
    x64 = 9,
    Neutral = 11,
    Arm64 = 12
}