【问题标题】:Programmatically get list of installed application executables (Windows10, C#)以编程方式获取已安装应用程序可执行文件的列表(Windows10、C#)
【发布时间】:2019-10-09 10:17:04
【问题描述】:

我想获取在 Windows 10 上安装和可执行的应用程序列表——即用户可以启动的应用程序的可执行文件(UWP 和非 UWP)。对于 UWP 应用,我想获取它的 AppUserModelId。对于非 UWP 应用,我想获取可执行文件的文件位置。

到目前为止,我已经尝试过: 1) 遍历注册表并遍历installed applications。但是,此方法会遍历所有已安装的应用程序,返回的列表将包括 microsoft asp.net core 2.1.8 shared framework (x64) 之类的内容,我想将其删除。

2) 我发现我可以通过特殊的Appsfolder 访问我想要的内容,方法是在资源管理器窗口或运行中键入shell:Appsfolder。到目前为止,我能够访问此文件夹,并遍历项目并列出文件的名称。但我不确定如何收集有关 AppUserModelId(对于 UWP 应用)和文件路径(对于 WPF 应用)的信息。

我用来枚举Appsfolder文件的相关代码:

namespace AppsFolder
{
    ...
    public static class AppsFolder
    {
       ...

       // GUID for shell:Appsfolder
       public static readonly Guid KnownFolderID = new Guid("1e87508d-89c2-42f0-8a7e-645a0f50ca58");

       public static IEnumerable<string> GetItemNames(ESHGDN gdn)
       {
           IShellFolder appsFolder = GetAppsFolder();
           try
           {
               IntPtr enumIDListInterfacePointer;
               appsFolder.EnumObjects(IntPtr.Zero,
                    ESHCONTF.SHCONTF_NONFOLDERS | ESHCONTF.SHCONTF_FOLDERS,
                   out enumIDListInterfacePointer);
               using (var enumIDList = new
                      EnumIDListWrapper(enumIDListInterfacePointer))
               {
                  var names = new List<string>();
                  foreach (var pidl in enumIDList)
                  {
                      STRRET name;
                      recycleBin.GetDisplayNameOf(
                              pidl.DangerousGetHandle(),
                             gdn,
                             out name);
                      names.Add(STRRETToString(ref name, pidl));
                      if (name.uType != (int)STRRETType.Offset)
                         Marshal.FreeCoTaskMem(name.data.pOleStr);
                  }
                  return names;
               }
           }
           finally
           {
               Marshal.FinalReleaseComObject(recycleBin);
           }
        }

        private static IShellFolder GetAppsFolder()
        {
            IShellFolder desktop;
            NativeMethod.SHGetDesktopFolder(out desktop);
            try
            {
                Guid shellFolderInterfaceId = typeof(IShellFolder).GUID;
                IntPtr recycleBinShellFolderInterfacePointer;
                desktop.BindToObject(
                    GetItemIDList().DangerousGetHandle(),
                    IntPtr.Zero,
                    ref shellFolderInterfaceId,
                    out recycleBinShellFolderInterfacePointer);
                return (IShellFolder)Marshal.GetObjectForIUnknown(
                             recycleBinShellFolderInterfacePointer);
            }
            finally
            {
                Marshal.FinalReleaseComObject(desktop);
            }
         }

         public static SafeCoTaskMemHandleZeroIsInvalid GetItemIDList()
         {
             SafeCoTaskMemHandleZeroIsInvalid pidl;
             Guid guid = KnownFolderID;
             NativeMethod.SHGetKnownFolderIDList(
                ref guid, 0, IntPtr.Zero, out pidl);
             return pidl;
         }
     }
}

执行为:

string[] names = AppsFolder.GetItemNames(AppsFolder.ESHGDN.SHGDN_INFOLDER).ToArray();
for (int i=0; i<names.Length; i++)
{
    Console.WriteLine(names[i]);
}

ILaunchSourceAppUserModelID 接口提供了获取 AppUserModelID 的方法,但我不确定如何通过我的 IShellFolder 接口访问该接口。另外,如何将 AppsFolder 虚拟文件夹中的实际文件位置获取到实际 exe 的文件路径?

【问题讨论】:

  • 您所描述的仅适用于向操作系统注册的应用程序,但这不是必需的。无需向操作系统注册即可安装和执行应用程序。如果您真的想找到所有内容,您最终可能不得不枚举整个文件系统。
  • 好吧,我明白了。在这一点上,我只想找到那些在操作系统中注册的。你能推荐一个更好的方法吗?

标签: c# .net windows winapi known-folders


【解决方案1】:

您可以通过在 Windows PowerShell 命令提示符下运行以下命令来获取为当前用户安装的所有应用程序的应用程序用户模型 ID (AUMID):

get-StartApps

要获取为其他用户安装的 Windows 应用商店应用的 AUMID,请在 Windows PowerShell 命令提示符下运行以下命令:

$installedapps = get-AppxPackage

$aumidList = @()
foreach ($app in $installedapps)
{
    foreach ($id in (Get-AppxPackageManifest $app).package.applications.application.id)
    {
        $aumidList += $app.packagefamilyname + "!" + $id
    }
}

$aumidList

更多详细信息可以参考“Find the Application User Model ID of an installed app”。

您需要做的是从 C# 调用相关的注册表 API 或 Powershell 命令。

【讨论】:

  • 看起来是一种方法,我会尝试的。用winapi不行吗?
【解决方案2】:

您通过 IShellFolder2.GetDetailsExPKEY_AppUserModel_ID

获得 AppUserModelId

(在 Windows 10 上用 C# 测试)

[StructLayout(LayoutKind.Sequential, Pack = 4)]
public struct PROPERTYKEY
{
    private readonly Guid _fmtid;
    private readonly uint _pid;

    public PROPERTYKEY(Guid fmtid, uint pid)
    {
        _fmtid = fmtid;
        _pid = pid;
    }

    public static readonly PROPERTYKEY PKEY_AppUserModel_ID = new PROPERTYKEY(new Guid("9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3"), 5);     
}

【讨论】:

    【解决方案3】:

    查看this的答案。

    简而言之,将Microsoft.WindowsAPICodePack-Shell nuget 包添加到您的项目中,然后使用以下代码:

    // GUID taken from https://docs.microsoft.com/en-us/windows/win32/shell/knownfolderid
    var FODLERID_AppsFolder = new Guid("{1e87508d-89c2-42f0-8a7e-645a0f50ca58}");
    ShellObject appsFolder = (ShellObject)KnownFolderHelper.FromKnownFolderId(FODLERID_AppsFolder);
    
    foreach (var app in (IKnownFolder)appsFolder)
    {
        // The friendly app name
        string name = app.Name;
        // The ParsingName property is the AppUserModelID
        string appUserModelID = app.ParsingName; // or app.Properties.System.AppUserModel.ID
        // You can even get the Jumbo icon in one shot
        ImageSource icon =  app.Thumbnail.ExtraLargeBitmapSource;
    }
    

    启动应用程序:

    System.Diagnostics.Process.Start("explorer.exe", @" shell:appsFolder\" + appUserModelID);
    

    适用于常规应用和商店应用。

    【讨论】:

      【解决方案4】:

      我有一个解决方案,您无需添加任何额外的 dll 或命名空间即可从窗口应用商店获取应用名称:

                  Process p = new Process();
                  p.StartInfo.UseShellExecute = false;
                  p.StartInfo.CreateNoWindow = true;
                  p.StartInfo.RedirectStandardOutput = true;
                  p.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
                  p.StartInfo.FileName = "powershell.exe";
                  p.StartInfo.Arguments = @"Get-AppxPackage | select name";
                  p.Start();
                  output = p.StandardOutput.ReadToEnd();
                  p.WaitForExit();
      

      输出中包含的所有应用程序列表。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-09-13
        • 2015-03-30
        • 1970-01-01
        • 1970-01-01
        • 2014-09-23
        相关资源
        最近更新 更多