7

I would like to query Windows using a file extension as a parameter (e.g. ".jpg") and be returned the path of whatever app windows has configured as the default application for this file type.

Ideally the solution would look something like this:

from stackoverflow import get_default_windows_app

default_app = get_default_windows_app(".jpg")

print(default_app)
"c:\path\to\default\application\application.exe"

I have been investigating the winreg builtin library which holds the registry infomation for windows but I'm having trouble understanding its structure and the documentation is quite complex.

I'm running Windows 10 and Python 3.6.

Does anyone have any ideas to help?

Hetzroni
  • 2,109
  • 1
  • 14
  • 29
user3535074
  • 1,268
  • 8
  • 26
  • 48
  • See also https://stackoverflow.com/questions/3729187/how-to-open-file-with-default-application-in-cmd – tripleee Oct 24 '22 at 11:47

2 Answers2

16

The registry isn't a simple well-structured database. The Windows shell executor has some pretty complex logic to it. But for the simple cases, this should do the trick:

import shlex
import winreg

def get_default_windows_app(suffix):
    class_root = winreg.QueryValue(winreg.HKEY_CLASSES_ROOT, suffix)
    with winreg.OpenKey(winreg.HKEY_CLASSES_ROOT, r'{}\shell\open\command'.format(class_root)) as key:
        command = winreg.QueryValueEx(key, '')[0]
        return shlex.split(command)[0]

>>> get_default_windows_app('.pptx')
'C:\\Program Files\\Microsoft Office 15\\Root\\Office15\\POWERPNT.EXE'

Though some error handling should definitely be added too.

Hetzroni
  • 2,109
  • 1
  • 14
  • 29
  • This looks great! Works on .wav, .mp3, .pptx etc but throws errors on .png and .jpg. "FileNotFoundError: [WinError 2] The system cannot find the file specified" Investigating, it looks like class_root is actually an "AssocFile". If there isn't an Assoc file in the registry, the function throws an exception (though there must be a default app as double clicking a jpg file opens it). Thanks for the answer though! Already great! – user3535074 Jan 05 '18 at 22:47
  • What open/query failed? What were the parameters? – Hetzroni Jan 05 '18 at 23:00
  • I meant - which specific call to one of the functions in `winreg` failed + what were the parameters? – Hetzroni Jan 05 '18 at 23:11
  • @user3535074, if this answered your question, please accept the answer. If not, add more elaboration so that it'll be possible to supply a better answer. – Hetzroni Jan 09 '18 at 10:32
  • Class_root =winreg.queryValue(winreg.HKEY_CLASSES_ROOT, ".png" is the line by throws the error. If you can understand why that might be when pngs open with a default app when double clicked it would be great to share. The bounty and the answer is yours though. Thanks so much! – user3535074 Jan 09 '18 at 22:09
  • Windows Explorer goes a long way before giving up. After checking `HKCU\.png`, it goes on to check `HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\.png\OpenWithProgids`. Does that key exist in your registry? – Hetzroni Jan 09 '18 at 22:49
  • searching in the winreg, I found this "Computer\HKEY_CLASSES_ROOT\.jpg", but the error "FileNotFoundError: [WinError 2] The system cannot find the file specified" persists. – user3535074 Jan 10 '18 at 20:17
  • That's odd. Try running a registry monitor, e.g. `procmon.exe` while executing the command, and see that it accesses the correct key. – Hetzroni Jan 11 '18 at 09:27
  • I need to trap the opening of the png file handler for an automation job. However when I use the above code it returns ```%SystemRoot%System32rundll32.exe``` which is not much use!! The program that opens on my machine is photos, but I can not guarantee that all target machines will be the same, or indeed that all will be running windows 10!! My only other option is to place the picture on the clipboard and open an instance of Paint to handle it... I am automating a third party app. – Andy Thompson Sep 15 '21 at 17:46
0

Added some improvements to the nice code by Hetzroni, in order to handle more cases:

import os
import shlex
import winreg

def get_default_windows_app(ext):
    try:  # UserChoice\ProgId lookup initial
        with winreg.OpenKey(winreg.HKEY_CURRENT_USER, r'SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\FileExts\{}\UserChoice'.format(ext)) as key:
            progid = winreg.QueryValueEx(key, 'ProgId')[0]
        with winreg.OpenKey(winreg.HKEY_CURRENT_USER, r'SOFTWARE\Classes\{}\shell\open\command'.format(progid)) as key:
            path = winreg.QueryValueEx(key, '')[0]
    except:  # UserChoice\ProgId not found
        try:
            class_root = winreg.QueryValue(winreg.HKEY_CLASSES_ROOT, ext)
            if not class_root:  # No reference from ext
                class_root = ext  # Try direct lookup from ext
            with winreg.OpenKey(winreg.HKEY_CLASSES_ROOT, r'{}\shell\open\command'.format(class_root)) as key:
                path = winreg.QueryValueEx(key, '')[0]
        except:  # Ext not found
            path = None
    # Path clean up, if any
    if path:  # Path found
        path = os.path.expandvars(path)  # Expand env vars, e.g. %SystemRoot% for ext .txt
        path = shlex.split(path, posix=False)[0]  # posix False for Windows operation
        path = path.strip('"')  # Strip quotes
    # Return
    return path
EquipDev
  • 5,573
  • 10
  • 37
  • 63
  • Does this retrieve the default Windows app or the ACTUAL default app that is currently used. Because in my case when trying this with the .eml extension I first received Outlook as the return value, but after changing the default email client to Chrome this script was still returning Outlook – Dimo Dimchev Jan 17 '23 at 12:25
  • @DimoDimchev: It sounds like changing the default email client in Windows does not change the default program used to open files with .eml extension. So you probably have to change the default program for files with .eml extension, either through File Explorer or the the NirSoft utility [FileTypesMan](https://www.nirsoft.net/utils/file_types_manager.html), which can also show the currently default program. – Morten Zilmer Jan 20 '23 at 09:17