Reputation: 385
I develop a Python script to list all installed apps in my Windows 11 system. I would like to replicate the same results displayed in Settings > App > Installed Apps. Optionally, I would like to list apps installed not only for my user but in the entire system. Surfing the web, I found these four commands:
cmd1 = "Get-WmiObject -Class Win32_Product | Select-Object Name, Version, Architecture | Sort-Object Name"
cmd2 = "Get-AppxPackage | Select-Object Name, Version, Architecture | Sort-Object Name"
path = f"HKLM:\\Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\*"
cmd3 = f"Get-ItemProperty -Path {path} | Select-Object DisplayName, DisplayVersion, Architecture | Sort-Object Name"
path = f"HKLM:\\Software\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\*"
cmd4 = f"Get-ItemProperty -Path {path} | Select-Object DisplayName, DisplayVersion, Architecture | Sort-Object Name"
I develop a simple code to run these four commands sequentially:
def list_installed_apps(cmd: str):
# Run the PowerShell command and capture output
result = subprocess.run(["powershell", "-Command", cmd], capture_output=True, text=True)
# Split the result into lines and filter out unwanted lines
installed_apps = result.stdout.splitlines()
return installed_apps
##############################
# CMD 1
##############################
cmd = "Get-WmiObject -Class Win32_Product | Select-Object Name, Version, Architecture | Sort-Object Name"
apps_1 = list_installed_apps(cmd)
# Find the index of the header line and column label boundaries
header_index = [i for i, app in enumerate(apps_1) if app.find("Version") != -1][0]
header = apps_1[header_index]
bound_1 = header.find("Version")
bound_2 = header.find("Architecture")
# Remove the header and any empty lines
apps_1 = [app.strip() for app in apps_1 if app.strip() and "Name" not in app]
apps_1 = apps_1[1:] #removes the line of -------
apps_1_ordered = [
{"name": app[:bound_1], "version": app[bound_1:bound_2], "architecture": app[bound_2:]}
for app in apps_1
]
##############################
# CMD 2
##############################
cmd = "Get-AppxPackage | Select-Object Name, Version, Architecture | Sort-Object Name"
apps_2 = list_installed_apps(cmd)
# Find the index of the header line and column label boundaries
header_index = [i for i, app in enumerate(apps_2) if app.find("Version") != -1][0]
header = apps_2[header_index]
bound_1 = header.find("Version")
bound_2 = header.find("Architecture")
# Remove the header and any empty lines
apps_2 = [app.strip() for app in apps_2 if app.strip() and "Name" not in app]
apps_2 = apps_2[1:] #removes the line of -------
apps_2_ordered = [
{"name": app[:bound_1], "version": app[bound_1:bound_2], "architecture": app[bound_2:]}
for app in apps_2
]
##############################
# CMD 3
##############################
path = f"HKLM:\\Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\*"
cmd = f"Get-ItemProperty -Path {path} | Select-Object DisplayName, DisplayVersion, Architecture | Sort-Object Name"
apps_3 = list_installed_apps(cmd)
# Find the index of the header line and column label boundaries
header_index = [i for i, app in enumerate(apps_3) if app.find("DisplayVersion") != -1][0]
header = apps_3[header_index]
bound_1 = header.find("DisplayVersion")
bound_2 = header.find("Architecture")
# Remove the header and any empty lines
apps_3 = [app.strip() for app in apps_3 if app.strip() and "DisplayName" not in app]
apps_3 = apps_3[1:] #removes the line of -------
apps_3_ordered = [
{"name": app[:bound_1], "version": app[bound_1:bound_2], "architecture": app[bound_2:]}
for app in apps_3
]
##############################
# CMD 4
##############################
path = f"HKLM:\\Software\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\*"
cmd = f"Get-ItemProperty -Path {path} | Select-Object DisplayName, DisplayVersion, Architecture | Sort-Object Name"
apps_4 = list_installed_apps(cmd)
# Find the index of the header line and column label boundaries
header = apps_4[1]
bound_1 = header.find("DisplayVersion")
bound_2 = header.find("Architecture")
# Remove the header and any empty lines
apps_4 = [app.strip() for app in apps_4 if app.strip() and "DisplayName" not in app]
apps_4 = apps_4[1:] #removes the line of -------
apps_4_ordered = [
{"name": app[:bound_1], "version": app[bound_1:bound_2], "architecture": app[bound_2:]}
for app in apps_4
]
##############################
# SUM RESULTS OF ALL COMMANDS AND SAVE
##############################
all_apps = apps_1_ordered + apps_2_ordered + apps_3_ordered + apps_4_ordered
fileName = "" # SET YOUR NAME
with open(fileName, "w") as f:
for app in all_apps:
f.write(f"{app['name']} --- {app['version']} --- {app['architecture']}\n")
Unfortunately, this file content doesn't completely match the apps sorted in the settings. How can I obtain that list?
Upvotes: 0
Views: 100
Reputation: 385
Thanks to @buran answer/link, I post here my entire code:
import subprocess, winreg
import pandas as pd
def remove_duplicates(data: list):
# Create a set to store unique combinations of (name, version, architecture)
seen = set()
# Create a new list for unique elements
unique_data = []
# Iterate through the list of dictionaries
for item in data:
name = item["name"].strip()
version = item["version"].strip()
architecture = item["architecture"].strip()
# Create a tuple of the values for name, version, and architecture
identifier = (name, version, architecture)
# If this combination hasn't been seen before, add it to the result list and mark it as seen
if identifier not in seen:
seen.add(identifier)
unique_data.append({"name": name, "version": version, "architecture": architecture})
return unique_data
def list_installed_apps(cmd: str):
# Run the PowerShell command and capture output
result = subprocess.run(["powershell", "-Command", cmd], capture_output=True, text=True)
# Split the result into lines and filter out unwanted lines
all_apps = result.stdout.splitlines()
# Find the index of the header line and column label boundaries
header_index = [i for i, app in enumerate(all_apps) if app.find("Version") != -1][0]
header = all_apps[header_index]
bound_1 = header.find("Version")
bound_2 = header.find("Architecture")
#bound_3 = header.find("Publisher")
# Remove the header and any empty lines
all_apps = [app.strip() for app in all_apps if app.strip() and "Name" not in app]
all_apps = all_apps[1:] #removes the line of -------
all_apps_ordered = [
{
"name": app[:bound_1],
"version": app[bound_1:bound_2],
"architecture": app[bound_2:],
#"architecture": app[bound_2:bound_3],
#"publisher": app[bound_3:]
}
for app in all_apps
]
return all_apps_ordered
def list_installed_apps_from_registry(cmd: str):
# Run the PowerShell command and capture output
result = subprocess.run(["powershell", "-Command", cmd], capture_output=True, text=True)
# Split the result into lines and filter out unwanted lines
all_apps = result.stdout.splitlines()
# Find the index of the header line and column label boundaries
header_index = [i for i, app in enumerate(all_apps) if app.find("DisplayVersion") != -1][0]
header = all_apps[header_index]
bound_1 = header.find("DisplayVersion")
bound_2 = header.find("Architecture")
#bound_3 = header.find("Publisher")
# Remove the header and any empty lines
all_apps = [app.strip() for app in all_apps if app.strip() and "DisplayName" not in app]
all_apps = all_apps[1:] #removes the line of -------
all_apps_ordered = [
{
"name": app[:bound_1],
"version": app[bound_1:bound_2],
"architecture": app[bound_2:],
#"architecture": app[bound_2:bound_3],
#"publisher": app[bound_3:]
}
for app in all_apps
]
return all_apps_ordered
def query_registry(hive, flag):
"""
Queries the registry for a list of installed software.
Args:
hive: a winreg constant specifying the registry hive to query.
flag: a winreg constant specifying the access mode.
Returns:
A list of dictionaries, each containing the name, version, publisher, and
architecture of a piece of software installed on the machine.
"""
aReg = winreg.ConnectRegistry(None, hive)
aKey = winreg.OpenKey(key=aReg, sub_key=r"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall",
reserved=0, access=winreg.KEY_READ | flag)
count_subkey = winreg.QueryInfoKey(aKey)[0]
software_list = []
for i in range(count_subkey):
software = {}
try:
asubkey_name = winreg.EnumKey(aKey, i)
asubkey = winreg.OpenKey(key=aKey, sub_key=asubkey_name)
software['name'] = winreg.QueryValueEx(asubkey, "DisplayName")[0]
try:
software['version'] = winreg.QueryValueEx(asubkey, "DisplayVersion")[0]
except EnvironmentError:
software['version'] = ""
# try:
# software['publisher'] = winreg.QueryValueEx(asubkey, "Publisher")[0]
# except EnvironmentError:
# software['publisher'] = ""
try:
software['architecture'] = winreg.QueryValueEx(asubkey, "Architecture")[0]
except EnvironmentError:
software['architecture'] = ""
software_list.append(software)
except EnvironmentError:
continue
return software_list
##############################
# CMD 1
##############################
cmd = "Get-WmiObject -Class Win32_Product | Select-Object Name, Version, Architecture | Sort-Object Name"
apps_1 = list_installed_apps(cmd)
##############################
# CMD 2
##############################
cmd = "Get-AppxPackage | Select-Object Name, Version, Architecture | Sort-Object Name"
apps_2 = list_installed_apps(cmd)
##############################
# CMD 3
##############################
path = f"HKLM:\\Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\*"
cmd = f"Get-ItemProperty -Path {path} | Select-Object DisplayName, DisplayVersion, Architecture | Sort-Object Name"
apps_3 = list_installed_apps_from_registry(cmd)
##############################
# CMD 4
##############################
path = f"HKLM:\\Software\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\*"
cmd = f"Get-ItemProperty -Path {path} | Select-Object DisplayName, DisplayVersion, Architecture | Sort-Object Name"
apps_4 = list_installed_apps_from_registry(cmd)
##############################
# CMD5, CM6, CM7
##############################
apps_5 = query_registry(winreg.HKEY_LOCAL_MACHINE, winreg.KEY_WOW64_32KEY)
apps_6 = query_registry(winreg.HKEY_LOCAL_MACHINE, winreg.KEY_WOW64_64KEY)
apps_7 = query_registry(winreg.HKEY_CURRENT_USER, 0)
##############################
# SUM RESULTS OF ALL COMMANDS
##############################
all_apps = apps_1 + apps_2 + apps_3 + apps_4 + apps_5 + apps_6 + apps_7
##############################################
# ELIMINATE DUPLICATES AND SORT BY NAME KEY
##############################################
# Output the unique list
all_apps_no_duplicates = remove_duplicates(all_apps)
all_apps_no_duplicates_sorted = sorted(all_apps_no_duplicates, key=lambda x: x["name"])
Upvotes: 0