سؤال

I know this has been asked before, and I've looked at the answer to this similar question. I'm trying to get the display names of all the metro apps installed on my computer, and I came up with this:

class Program {
        [DllImport("shlwapi.dll", BestFitMapping = false, CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = false, ThrowOnUnmappableChar = true)]
        public static extern int SHLoadIndirectString(string pszSource, StringBuilder pszOutBuf, int cchOutBuf, IntPtr ppvReserved);

        static void Main(string[] args) {
            foreach(string dir in Directory.GetDirectories(@"c:\program files\WindowsApps\")) {
                if(File.Exists(dir + @"\AppxManifest.xml")) {
                    XmlDocument doc = new XmlDocument(); 
                    doc.Load(dir + @"\AppxManifest.xml");

                    string name = doc.GetElementsByTagName("DisplayName")[0].InnerText;
                    string identity = doc.GetElementsByTagName("Identity")[0].Attributes["Name"].Value;

                    if(!name.Contains("ms-resource")) {
                        Console.WriteLine(name);
                    } else {
                        StringBuilder sb = new StringBuilder();

                        int result = SHLoadIndirectString(
                           @"@{" + Path.GetFileName(dir) + "? ms-resource://" + Identity + "/resources/" + name.Split(':')[1] + "}",
                           sb, -1,
                           IntPtr.Zero);

                        if(result == 0) Console.WriteLine(sb.ToString());
                    }
                }
            }
            Console.ReadLine();
        }
    }

This works great and gives me the names of the apps, until it reaches the folder /microsoft.windowscommunicationsapps_17.4.9600.16384_x64__8wekyb3d8bbwe/. Visual Studio doesn't give a crash report, it just says that "This program has stopped working." I've looked at the structure of the xml file and see no reason that it should crash.

So my question is this: Is there some way I can fix the crash, or is there a better way to get the metro app display name without using any windows 8 specific functions?

Thanks!

هل كانت مفيدة؟

المحلول

I know that I'm going to answer my own question, but I've been doing a lot of research and found the answer to my question. This took a while to find, so I'm also going to show my solution and list some other things I learned about metro apps.

  • Metro apps are installed in packages (folders) in C:/Program Files/WindowsApps
  • There can be multiple metro apps in one package
  • There is a AppxManifest.xml in every package telling what is in the package
  • In the manifest, each application has its own Application Tag
  • Metro apps can be started with a DOS command:
  • start [ProtocolName]:
  • An App's protocol name can be obtained from the Protocol tag in the AppxManifest.xml

I modified my function to account for two apps being in a single package. I also filtered the list based on whether or not the app has a protocol name.

[DllImport("shlwapi.dll", BestFitMapping = false, CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = false, ThrowOnUnmappableChar = true)]
public static extern int SHLoadIndirectString(string pszSource, StringBuilder pszOutBuf, int cchOutBuf, IntPtr ppvReserved);

public static List<string> GetMetroAppnames() {
            List<string> names = new List<string>();

            foreach(string dir in Directory.GetDirectories(@"c:\program files\WindowsApps\")) {
                if(System.IO.File.Exists(dir + @"\AppxManifest.xml")) {
                    XmlDocument doc = new XmlDocument();
                    doc.Load(dir + @"\AppxManifest.xml");

                    if(doc.GetElementsByTagName("Framework")[0] != null)
                        if(doc.GetElementsByTagName("Framework")[0].InnerText.ToLower() == "true")
                            continue;
                    if(doc.GetElementsByTagName("Protocol")[0] == null) continue;

                    string name = doc.GetElementsByTagName("DisplayName")[0].InnerText;
                    string identity = doc.GetElementsByTagName("Identity")[0].Attributes["Name"].Value;
                    string appName = "";

                    if(!name.Contains("ms-resource")) {
                        names.Add(name);
                    } else {
                        if(doc.GetElementsByTagName("Application").Count > 1) {
                            foreach(XmlElement elem in doc.GetElementsByTagName("Application")) {
                                name = elem.GetElementsByTagName("m2:VisualElements")[0].Attributes["DisplayName"].Value;
                                if(name.Contains("AppName")) name = name.Replace("AppName", "AppTitle");
                                appName = GetName(dir, name, identity);
                                if(appName != "") names.Add(appName);
                            }
                        }
                        appName = GetName(dir, name, identity);
                        if(appName != "") names.Add(appName);
                    }
                }
            }
            return names.Distinct().ToList();
        }

private static string GetName(string dir, string name, string identity) {
        StringBuilder sb = new StringBuilder();
        int result;

        result = SHLoadIndirectString(
            @"@{" + Path.GetFileName(dir) + "? ms-resource://" + identity + "/resources/" + name.Split(':')[1] + "}",
            sb, -1,
            IntPtr.Zero
        );              

        if(result == 0) return sb.ToString();
        return "";
    }

The code seems a bit lengthy, but it was the only was I found to do it. If you're looking at this later, then I hope this helped you solve your problem, but for now, I've learned what I needed.

نصائح أخرى

A list of the installed metro apps can also be found in the registry under

HKEY_CLASSES_ROOT\Local Settings\Software\Microsoft\Windows\CurrentVersion\AppModel\Repository\Packages

Each app key usually has an "App" sub-key that contains a "Capabilities" sub-key. This key will contain a name and description of the app (and you may need to use SHLoadIndirectString to get the data).

Under the "Capabilities" sub-key there is a "URLAssociations" sub-key which will contain all of the protocols or verbs used to invoke the program.

Also to note is that the apps contained in this collection are both the User apps and the system apps like Microsoft Edge which would be located at c:\Windows\SystemApps

SAMPLE APP

[DllImport("shlwapi.dll", BestFitMapping = false, CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = false, ThrowOnUnmappableChar = true)]
internal static extern int SHLoadIndirectString(string pszSource, StringBuilder pszOutBuf, uint cchOutBuf, IntPtr ppvReserved);

public static string GetResourceString(string resString)
{
    StringBuilder sb = new StringBuilder(1024);

    if(SHLoadIndirectString(resString, sb, (uint)sb.Capacity, IntPtr.Zero) == 0)
        return sb.ToString();

    return null;
}

internal sealed class StoreAppObject
{
    public StoreAppObject()
    {
        this.Protocols = new Dictionary<string, string>();
    }


    public string Name
    { get; set; }

    public string Description
    { get; set; }

    public string PackageId
    { get; set; }

    public string RootFolder
    { get; set; }

    public Dictionary<string, string> Protocols
    { get; set; }
}

private void LoadStoreApps()
{
    using (RegistryKey hkcr = RegistryKey.OpenBaseKey(RegistryHive.ClassesRoot, RegistryView.Registry32))
    {
        RegistryKey packagesKey = hkcr.OpenSubKey(@"Local Settings\Software\Microsoft\Windows\CurrentVersion\AppModel\Repository\Packages");
        string[] packageNames = packagesKey.GetSubKeyNames();

        foreach (string packageName in packageNames)
        {
            RegistryKey packageKey = packagesKey.OpenSubKey(packageName);

            if (packageKey != null && packageKey.SubKeyCount > 0)
            {
                string[] packageAppNames = packageKey.GetSubKeyNames();

                foreach (string packageAppName in packageAppNames)
                {
                    RegistryKey packageAppKey = packageKey.OpenSubKey(packageAppName);

                    if (packageAppKey != null && packageAppKey.SubKeyCount > 0)
                    {
                        RegistryKey capabilitiesKey = packageAppKey.OpenSubKey("Capabilities");

                        if (capabilitiesKey != null)
                        {
                            RegistryKey urlAssociationsKey = capabilitiesKey.OpenSubKey("URLAssociations");

                            if (urlAssociationsKey != null)
                            {
                                // My custom class
                                StoreAppObject sao = new StoreAppObject();
                                sao.Name = (string)capabilitiesKey.GetValue("ApplicationName");
                                sao.Description = (string)capabilitiesKey.GetValue("ApplicationDescription");
                                sao.PackageId = (string)packageKey.GetValue("PackageID");
                                sao.RootFolder = (string)packageKey.GetValue("PackageRootFolder");

                                string[] urlAssociationNames = urlAssociationsKey.GetValueNames();
                                foreach (string urlAssociationName in urlAssociationNames)
                                    sao.Protocols.Add(urlAssociationName, (string)urlAssociationsKey.GetValue(urlAssociationName));

                                if (sao.Name.StartsWith("@"))
                                    sao.Name = NativeMethods.GetResourceString(sao.Name);

                                if (sao.Description.StartsWith("@"))
                                    sao.Description = NativeMethods.GetResourceString(sao.Description);                             
                            }
                        }
                    }
                }
            }
        }
    }
}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top