Search code examples
pythonwindowsuwpicons

Load UWP icon programmatically


I'm writing a Python-based launcher that is automatically populated form Windows' Start menu. I plan to extend it to include UWP apps. Listing and launching them isn't hard, but accessing their icons seems to be tricky.

As the apps reside under \program files\windowsapps\, their icons seem to be inaccessible due to permission issue. I don't plan to run my program elevated.

I have the current leads:

  • Their icons are parsed and cached in the iconcache*.db under %homepath%\AppData\Local\Microsoft\Windows\Explorer (errors of displaying their icons can be fixed by removing these cache files), but I didn't find any solutions to read the image data from there.
  • Their icons might be fetched from the Windows Store also (but I would prefer an offline solution).
  • I'm also open to any other solution ideas to get their icon data.

Solution

  • TLDR:

    1. get the 'store logo path' from the manifest
    2. don't believe it, but get the stem of it
    3. find the true paths based on the stem

    Example:

    This example will display the icon for an UWP app, by its name. Names can be listed by the powershell command:

    Get-AppxPackage | Select-Object -ExpandProperty Name
    

    Microsoft.WindowsCalculator is used for the example, but I've tried it out with other apps (Terminal, Spotify, Teams, Paint, ZuneMusic, DuckDuckGo, Outlook, WindowsStore etc.) and it worked well : )

    Python code:

    from pathlib import Path
    from subprocess import run
    from tkinter import Label, Tk
    
    from PIL import Image, ImageTk
    
    
    def get_powershell_output(command: str) -> str:
        process = run(command, capture_output=True, text=True, shell=True)
        return process.stdout.strip()
    
    
    def get_icon_name(app_name: str) -> Path:
        command = f"""powershell "(Get-AppxPackage -Name {app_name} | Get-AppxPackageManifest).package.properties.logo" """
        return Path(get_powershell_output(command))
    
    
    def get_install_path(app_name: str) -> Path:
        command = f"""powershell "(Get-AppxPackage -Name {app_name}).InstallLocation" """
        return Path(get_powershell_output(command))
    
    
    def locate_icon(icon: Path, install_path: Path) -> Path:
        matches = install_path.glob(f"**/{icon.stem}*.png")
        # usually 3 matches (default, black, white), let's use default
        return list(matches)[0]
    
    
    def show_icon(icon_path: Path) -> None:
        root = Tk()
        root.title("Display Icon")
        pil_image = Image.open(icon_path)
        tk_image = ImageTk.PhotoImage(pil_image)
        label = Label(root, image=tk_image)
        label.pack()
        root.mainloop()
    
    
    def main(current_name: str) -> None:
        icon_path = get_icon_name(current_name)
        print(icon_path)
        # Assets\CalculatorStoreLogo.png
    
        install_path = get_install_path(current_name)
        print(install_path)
        # C:\Program Files\WindowsApps\Microsoft.WindowsCalculator_11.2411.1.0_x64__8wekyb3d8bbwe
    
        selected_icon = locate_icon(icon_path, install_path)
        print(selected_icon)
        # C:\Program Files\WindowsApps\Microsoft.WindowsCalculator_11.2411.1.0_x64__8wekyb3d8bbwe\Assets\CalculatorStoreLogo.scale-200.png
    
        show_icon(selected_icon)
        # see the proof
    
    
    if __name__ == "__main__":
        main("Microsoft.WindowsCalculator")
    
    

    Output: The console output as written in the code and the icon displayed in a TkInter window.