如何获取已安装的metro应用程序的显示名称

时间:2014-04-28 01:57:21

标签: c# .net windows-8

我知道之前已经问过这个问题,我看了this similar 问题的答案。我正在尝试获取计算机上安装的所有metro应用程序的显示名称,我想出了这个:

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

这非常有用,并且会在给出应用程序的名称之前,直到它到达文件夹/microsoft.windowscommunicationsapps_17.4.9600.16384_x64__8wekyb3d8bbwe/。 Visual Studio没有给出崩溃报告,它只是说“这个程序已经停止工作了”。我已经查看了xml文件的结构,看不出它应该崩溃的原因。

所以我的问题是:有什么方法可以解决崩溃问题,还是有更好的方法来获取metro应用程序显示名称而不使用任何Windows 8特定功能?

谢谢!

2 个答案:

答案 0 :(得分:3)

我知道我会回答我自己的问题,但我已经做了很多研究,并找到了我的问题的答案。这需要一段时间才能找到,所以我也会展示我的解决方案,并列出我学习的有关metro应用程序的其他一些内容。

        
  • Metro应用程序安装在C:/ Program Files / WindowsApps
  • 中的软件包(文件夹)中     
  • 一个包中可以有多个metro应用
  •     
  • 每个包中都有一个AppxManifest.xml,告诉包
  • 中的内容     
  • 在清单中,每个应用程序都有自己的应用程序标记
  •     
  • 可以使用DOS命令启动Metro应用程序:
  •     
    start [ProtocolName]:
        
  • 可以从AppxManifest.xml中的Protocol标签获取App的协议名称

我修改了我的功能,以便将两个应用程序放在一个包中。我还根据应用程序是否具有协议名称来过滤列表。

[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 "";
    }

代码似乎有点冗长,但它是我发现的唯一代码。如果您稍后再看,我希望这有助于您解决问题,但就目前而言,我已经了解了我的需求。

答案 1 :(得分:3)

也可以在

下的注册表中找到已安装的城域应用程序列表
HKEY_CLASSES_ROOT\Local Settings\Software\Microsoft\Windows\CurrentVersion\AppModel\Repository\Packages

每个app键通常都有一个“App”子键,其中包含“Capabilities”子键。此密钥将包含应用程序的名称和描述(您可能需要使用SHLoadIndirectString来获取数据)。

在“Capabilities”子键下有一个“URLAssociations”子键,它包含用于调用程序的所有协议或动词。

另外需要注意的是,此集合中包含的应用程序既是用户应用程序,也是Microsoft Edge等系统应用程序,它们位于c:\ Windows \ SystemApps

示范应用

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