如何在Python中通过WinSDK/WinRT从source_app_user_model_id获取应用显示名称(含非UWP应用兼容方案)
如何在Python中通过WinSDK/WinRT从source_app_user_model_id获取应用显示名称(含非UWP应用兼容方案)
我完全理解你现在的困境:用WinRT拿到媒体会话的source_app_user_model_id后,UWP专属的AppInfo和PackageManager完全搞不定Chrome、Firefox这类传统桌面应用。下面给你一套覆盖UWP和非UWP场景的完整解决方案,每个步骤都给你讲清楚逻辑:
一、先理清楚核心问题
首先要明确:
AppInfo.get_from_app_user_model_id仅对UWP应用或用MSIX打包的桌面应用有效,传统exe安装的应用(Chrome、Firefox)没有对应的AppInfo记录,所以会报"Element not found"。PackageManager只能枚举Windows部署模型下的包(UWP/MSIX),传统桌面应用不属于这个范畴,所以完全找不到。
因此我们需要做的是:优先用WinRT方法处理UWP应用,针对非UWP应用,通过Win32 API或注册表查询的方式 fallback。
二、分步实现解决方案
1. UWP应用:正确使用AppInfo的方法
先保留对UWP应用的支持,这部分你的代码本身是对的,我们把它封装成一个单独的函数,并且加入异常处理来触发后续的fallback:
from winsdk.windows.applicationmodel import AppInfo def get_uwp_display_name(app_id): try: app_info = AppInfo.get_from_app_user_model_id(app_id) return app_info.display_name except OSError: # 非UWP应用,返回None让后续逻辑处理 return None
2. 非UWP桌面应用:通过注册表+文件版本信息获取名称
传统桌面应用的source_app_user_model_id通常会在注册表中关联到其exe路径,我们可以通过这个路径再去获取应用的友好名称,具体分两步:
步骤1:从AppUserModelID找到对应exe路径
桌面应用的AppUserModelID会在注册表的HKCU\Software\Classes\AppUserModelId或HKLM\Software\Classes\AppUserModelId下有记录,我们可以从中解析出启动命令,进而提取exe路径。对于一些常见应用,还可以做硬编码的兜底。
步骤2:从exe路径获取友好名称
拿到exe路径后,我们有两种方式获取名称:
- 查注册表
HKCR\Applications\<exe_name>下的FriendlyAppName键 - 读取exe文件本身的版本信息中的
FileDescription
下面是完整的fallback实现(需要先安装pywin32:pip install pywin32):
import winreg import shlex import win32api import win32con import os def get_desktop_app_display_name(app_id): exe_path = None # 尝试从注册表查找AppUserModelID对应的exe路径 for hkey in [winreg.HKEY_CURRENT_USER, winreg.HKEY_LOCAL_MACHINE]: try: with winreg.OpenKey(hkey, f"Software\\Classes\\AppUserModelId\\{app_id}") as key: with winreg.OpenKey(key, r"Shell\Open\Command") as cmd_key: cmd_value, _ = winreg.QueryValueEx(cmd_key, "") # 解析带引号的启动命令,提取exe路径 exe_path = shlex.split(cmd_value)[0] break except FileNotFoundError: continue # 注册表找不到的话,针对常见应用做硬编码兜底 if not exe_path: app_id_lower = app_id.lower() if "chrome" in app_id_lower: exe_path = r"C:\Program Files\Google\Chrome\Application\chrome.exe" if not os.path.exists(exe_path): exe_path = r"C:\Program Files (x86)\Google\Chrome\Application\chrome.exe" elif "firefox" in app_id_lower: exe_path = r"C:\Program Files\Mozilla Firefox\firefox.exe" if not os.path.exists(exe_path): exe_path = r"C:\Program Files (x86)\Mozilla Firefox\firefox.exe" if not exe_path or not os.path.exists(exe_path): return app_id # 完全找不到的话,返回原始ID # 从exe路径获取友好名称 try: # 先查注册表的FriendlyAppName exe_name = exe_path.split("\\")[-1] with winreg.OpenKey(winreg.HKEY_CLASSES_ROOT, f"Applications\\{exe_name}") as app_key: friendly_name, _ = winreg.QueryValueEx(app_key, "FriendlyAppName") return friendly_name except FileNotFoundError: # 读取exe文件的版本信息 try: file_version_info = win32api.GetFileVersionInfo(exe_path, "\\") # 获取语言和代码页 lang, codepage = win32api.GetFileVersionInfo(exe_path, "\\VarFileInfo\\Translation")[0] str_info = f"\\StringFileInfo\\{lang:04x}{codepage:04x}\\FileDescription" return file_version_info[str_info] except Exception: # 终极兜底:返回不带扩展名的文件名 return exe_path.split("\\")[-1].split(".")[0]
3. 整合通用函数
把UWP和非UWP的逻辑整合起来,形成一个可以直接调用的通用函数:
from winsdk.windows.applicationmodel import AppInfo import winreg import shlex import win32api import win32con import os def get_app_display_name(app_id): # 优先处理UWP应用 uwp_name = get_uwp_display_name(app_id) if uwp_name: return uwp_name # 非UWP应用走fallback逻辑 return get_desktop_app_display_name(app_id) # 测试你的场景 if __name__ == "__main__": print(get_app_display_name("Chrome.AI6Y5TE6AH3DDYXV66D2XOWVLA")) # 应该返回"Google Chrome" print(get_app_display_name("308046B0AF4A39CB")) # 应该返回"Mozilla Firefox"
三、补充说明
- 为什么
PackageManager找不到Chrome/Firefox?因为PackageManager仅管理Windows部署模型下的包(UWP/MSIX),而Chrome、Firefox是传统exe安装的桌面应用,不属于这个体系,所以完全不会被枚举到。 - 对于一些非常见的桌面应用,硬编码兜底可能不生效,但注册表查询的逻辑通常能覆盖大部分场景,因为正规的桌面应用都会注册AppUserModelID到注册表中。
内容来源于stack exchange
相关产品推荐
相关产品推荐

