以编程方式获取已安装应用程序可执行文件列表(Windows10,C#)

3
我希望获取在Windows 10上已安装并可执行的应用程序列表,即用户可以启动的应用程序的可执行文件(UWP和非UWP)。对于UWP应用程序,我想获取其AppUserModelId。对于非UWP应用程序,我想获取可执行文件的文件位置。
到目前为止,我尝试过: 1)通过查找注册表并迭代遍历已安装的应用程序(链接1)。但是,该方法会遍历所有已安装的应用程序,返回的列表将包括我想要消除的内容,例如“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文件的位置?


你所描述的只适用于已在操作系统中注册的应用程序,但这并非必需条件。应用程序可以安装和执行,而无需在操作系统中进行注册。如果你真的想找到“所有”的东西,很可能最终不得不枚举整个文件系统。 - Remy Lebeau
对,我明白了。目前我只想找到那些已在操作系统中注册的程序。你能推荐一种更好的方法吗? - Asy
4个回答

7

请看这个答案。

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

// GUID taken from https://learn.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);

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

提供的软件包已不再维护。这是一个新的软件包,支持.NET 6:https://www.nuget.org/packages/Microsoft-WindowsAPICodePack-Shell/ - Damian Ubowski
此外,当AppUserModelID中包含GUID(例如{F38BF404-1D43-42F2-9305-67DE0B28FC23}\regedit.exe)时,使用Process.Start()方法启动应用程序无效。它只会打开默认的文档文件夹。 - Damian Ubowski
@DamianUbowski 我正在编写自己的代码来读取虚拟文件夹的内容,以便完全摆脱WindowsAPICodePack-Shell。 至于另一个话题,你应该像这样开始它:Process.Start("explorer.exe", @"shell:appsFolder\{F38BF404-1D43-42F2-9305-67DE0B28FC23}\regedit.exe"); 刚刚测试过,运行良好。 - user1969903

0

你可以使用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);     
}

0

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

get-StartApps

要获取为另一个用户安装的Windows Store应用程序的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

更详细的信息可以参考 "查找已安装应用程序的应用用户模型ID"。

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


看起来这是一种方法,我会试试看。难道不能用winapi吗? - Asy
1
这不列出已安装为应用程序的Chrome / Edge网页。 - user1969903

0
我有一个解决方案,你不需要添加任何额外的dll或命名空间,就可以从Windows应用商店获取应用程序名称:
            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();

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接