mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Code to read programs capable of opening a specified file type from the windows registry
This commit is contained in:
parent
986adc521b
commit
9bda2318c3
@ -6,12 +6,17 @@ from __future__ import (unicode_literals, division, absolute_import,
|
|||||||
__license__ = 'GPL v3'
|
__license__ = 'GPL v3'
|
||||||
__copyright__ = '2015, Kovid Goyal <kovid at kovidgoyal.net>'
|
__copyright__ = '2015, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||||
|
|
||||||
import os, sys, time
|
import os, sys, time, ctypes
|
||||||
|
from ctypes.wintypes import HLOCAL, LPCWSTR
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
|
|
||||||
|
import winerror
|
||||||
|
|
||||||
from calibre import guess_type, prints
|
from calibre import guess_type, prints
|
||||||
from calibre.constants import is64bit, isportable, isfrozen, __version__, DEBUG
|
from calibre.constants import is64bit, isportable, isfrozen, __version__, DEBUG
|
||||||
from calibre.utils.winreg.lib import Key
|
from calibre.utils.winreg.lib import Key, HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE
|
||||||
|
|
||||||
|
# See https://msdn.microsoft.com/en-us/library/windows/desktop/cc144154(v=vs.85).aspx
|
||||||
|
|
||||||
def default_programs():
|
def default_programs():
|
||||||
return {
|
return {
|
||||||
@ -173,3 +178,113 @@ class Register(Thread):
|
|||||||
# Give the thread some time to finish in case the user quit the
|
# Give the thread some time to finish in case the user quit the
|
||||||
# application very quickly
|
# application very quickly
|
||||||
self.join(4.0)
|
self.join(4.0)
|
||||||
|
|
||||||
|
def get_prog_id_map(base, key_path):
|
||||||
|
desc, ans = None, {}
|
||||||
|
try:
|
||||||
|
k = Key(open_at=key_path, root=base)
|
||||||
|
except WindowsError as err:
|
||||||
|
if err.errno == winerror.ERROR_FILE_NOT_FOUND:
|
||||||
|
return desc, ans
|
||||||
|
raise
|
||||||
|
with k:
|
||||||
|
desc = k.get_mui_string('ApplicationDescription')
|
||||||
|
if desc is None:
|
||||||
|
return desc, ans
|
||||||
|
for ext, prog_id in k.itervalues(sub_key='FileAssociations', get_data=True):
|
||||||
|
ans[ext[1:].lower()] = prog_id
|
||||||
|
return desc, ans
|
||||||
|
|
||||||
|
def get_open_data(base, prog_id):
|
||||||
|
try:
|
||||||
|
k = Key(open_at=r'Software\Classes\%s' % prog_id, root=base)
|
||||||
|
except WindowsError as err:
|
||||||
|
if err.errno == winerror.ERROR_FILE_NOT_FOUND:
|
||||||
|
return None, None
|
||||||
|
with k:
|
||||||
|
return k.get(sub_key=r'shell\open\command'), k.get(sub_key='DefaultIcon'), k.get_mui_string('FriendlyTypeName') or k.get()
|
||||||
|
|
||||||
|
CommandLineToArgvW = ctypes.windll.shell32.CommandLineToArgvW
|
||||||
|
CommandLineToArgvW.arg_types = [LPCWSTR, ctypes.POINTER(ctypes.c_int)]
|
||||||
|
CommandLineToArgvW.restype = ctypes.POINTER(LPCWSTR)
|
||||||
|
LocalFree = ctypes.windll.kernel32.LocalFree
|
||||||
|
LocalFree.res_type = HLOCAL
|
||||||
|
LocalFree.arg_types = [HLOCAL]
|
||||||
|
|
||||||
|
def split_commandline(commandline):
|
||||||
|
# CommandLineToArgvW returns path to executable if called with empty string.
|
||||||
|
if not commandline.strip():
|
||||||
|
return []
|
||||||
|
num = ctypes.c_int(0)
|
||||||
|
result_pointer = CommandLineToArgvW(commandline.lstrip(), ctypes.byref(num))
|
||||||
|
if not result_pointer:
|
||||||
|
raise ctypes.WinError()
|
||||||
|
result_array_type = LPCWSTR * num.value
|
||||||
|
result = [arg for arg in result_array_type.from_address(ctypes.addressof(result_pointer.contents))]
|
||||||
|
LocalFree(result_pointer)
|
||||||
|
return result
|
||||||
|
|
||||||
|
def friendly_app_name(progid=None, exe=None):
|
||||||
|
from win32com.shell import shell, shellcon
|
||||||
|
a = shell.AssocCreate()
|
||||||
|
a.Init((shellcon.ASSOCF_INIT_BYEXENAME if exe else 0), exe or progid)
|
||||||
|
return a.GetString(shellcon.ASSOCF_REMAPRUNDLL, shellcon.ASSOCSTR_FRIENDLYAPPNAME)
|
||||||
|
|
||||||
|
def find_programs(extensions):
|
||||||
|
extensions = frozenset(extensions)
|
||||||
|
ans = []
|
||||||
|
seen_prog_ids, seen_cmdlines = set(), set()
|
||||||
|
|
||||||
|
# Search for programs registered using Default Programs that claim they are
|
||||||
|
# capable of handling the specified extensions.
|
||||||
|
|
||||||
|
for base in (HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE):
|
||||||
|
try:
|
||||||
|
k = Key(open_at=r'Software\RegisteredApplications', root=base)
|
||||||
|
except WindowsError as err:
|
||||||
|
if err.errno == winerror.ERROR_FILE_NOT_FOUND:
|
||||||
|
continue
|
||||||
|
raise
|
||||||
|
with k:
|
||||||
|
for name, key_path in k.itervalues(get_data=True):
|
||||||
|
try:
|
||||||
|
app_desc, prog_id_map = get_prog_id_map(base, key_path)
|
||||||
|
except Exception:
|
||||||
|
import traceback
|
||||||
|
traceback.print_exc()
|
||||||
|
continue
|
||||||
|
for ext in extensions:
|
||||||
|
prog_id = prog_id_map.get(ext)
|
||||||
|
if prog_id is not None and prog_id not in seen_prog_ids:
|
||||||
|
seen_prog_ids.add(prog_id)
|
||||||
|
cmdline, icon_resource, friendly_name = get_open_data(base, prog_id)
|
||||||
|
if cmdline and cmdline not in seen_cmdlines:
|
||||||
|
seen_cmdlines.add(cmdline)
|
||||||
|
ans.append({'name':app_desc, 'cmdline':cmdline, 'icon_resource':icon_resource})
|
||||||
|
|
||||||
|
# Now look for programs that only register with Windows Explorer instead of
|
||||||
|
# Default Programs (for example, FoxIt PDF reader)
|
||||||
|
for ext in extensions:
|
||||||
|
try:
|
||||||
|
k = Key(open_at=r'Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\.%s\OpenWithProgIDs' % ext, root=HKEY_CURRENT_USER)
|
||||||
|
except WindowsError as err:
|
||||||
|
if err.errno == winerror.ERROR_FILE_NOT_FOUND:
|
||||||
|
continue
|
||||||
|
for prog_id in k.itervalues():
|
||||||
|
if prog_id and prog_id not in seen_prog_ids:
|
||||||
|
seen_prog_ids.add(prog_id)
|
||||||
|
cmdline, icon_resource, friendly_name = get_open_data(base, prog_id)
|
||||||
|
if cmdline and cmdline not in seen_cmdlines:
|
||||||
|
seen_cmdlines.add(cmdline)
|
||||||
|
exe_name = None
|
||||||
|
exe = split_commandline(cmdline)
|
||||||
|
if exe:
|
||||||
|
exe_name = friendly_app_name(prog_id) or os.path.splitext(os.path.basename(exe[0]))[0]
|
||||||
|
name = exe_name or friendly_name
|
||||||
|
if name:
|
||||||
|
ans.append({'name':name, 'cmdline':cmdline, 'icon_resource':icon_resource})
|
||||||
|
return ans
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
from pprint import pprint
|
||||||
|
pprint(find_programs('jpeg pdf'.split()))
|
||||||
|
Loading…
x
Reference in New Issue
Block a user