我知道之前已经问过这个问题,我看了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特定功能?
谢谢!
答案 0 :(得分:3)
我知道我会回答我自己的问题,但我已经做了很多研究,并找到了我的问题的答案。这需要一段时间才能找到,所以我也会展示我的解决方案,并列出我学习的有关metro应用程序的其他一些内容。
start [ProtocolName]:
我修改了我的功能,以便将两个应用程序放在一个包中。我还根据应用程序是否具有协议名称来过滤列表。
[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);
}
}
}
}
}
}
}
}