From 2b3608cc1a5308314f5a675114777aaaac194def Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Thu, 21 Sep 2017 12:54:08 +0530 Subject: [PATCH] Make setting of app uids on windows more robust --- src/calibre/constants.py | 6 ++--- src/calibre/gui2/__init__.py | 38 ++++++++++++++++++++++++++++- src/calibre/gui2/main.py | 12 +++------ src/calibre/gui2/tweak_book/main.py | 10 ++------ src/calibre/gui2/viewer/main.py | 8 ++---- 5 files changed, 48 insertions(+), 26 deletions(-) diff --git a/src/calibre/constants.py b/src/calibre/constants.py index db8b9c2da1..229eeeece5 100644 --- a/src/calibre/constants.py +++ b/src/calibre/constants.py @@ -39,9 +39,9 @@ isworker = 'CALIBRE_WORKER' in os.environ or 'CALIBRE_SIMPLE_WORKER' in os.envir if isworker: os.environ.pop('CALIBRE_FORCE_ANSI', None) FAKE_PROTOCOL, FAKE_HOST = 'https', 'calibre-internal.invalid' -VIEWER_APP_UID = 'com.calibre-ebook.viewer' -EDITOR_APP_UID = 'com.calibre-ebook.edit-book' -MAIN_APP_UID = 'com.calibre-ebook.main-gui' +VIEWER_APP_UID = u'com.calibre-ebook.viewer' +EDITOR_APP_UID = u'com.calibre-ebook.edit-book' +MAIN_APP_UID = u'com.calibre-ebook.main-gui' try: preferred_encoding = locale.getpreferredencoding() codecs.lookup(preferred_encoding) diff --git a/src/calibre/gui2/__init__.py b/src/calibre/gui2/__init__.py index 5bb932d9fa..e11eeac1fd 100644 --- a/src/calibre/gui2/__init__.py +++ b/src/calibre/gui2/__init__.py @@ -11,7 +11,7 @@ from PyQt5.Qt import ( QDesktopServices, QFileDialog, QFileIconProvider, QSettings, QIcon, QStringListModel, QApplication, QDialog, QUrl, QFont, QFontDatabase, QLocale, QFontInfo) -from calibre import prints +from calibre import prints, as_unicode from calibre.constants import (islinux, iswindows, isbsd, isfrozen, isosx, is_running_from_develop, plugins, config_dir, filesystem_encoding, isxp, DEBUG, __version__, __appname__ as APP_UID) from calibre.ptempfile import base_dir @@ -1286,3 +1286,39 @@ def secure_web_page(qwebpage_or_qwebsettings): empty_model = QStringListModel(['']) empty_index = empty_model.index(0) + + +def get_app_uid(): + import ctypes + from ctypes import wintypes + lpBuffer = wintypes.LPWSTR() + try: + AppUserModelID = ctypes.windll.shell32.GetCurrentProcessExplicitAppUserModelID + except Exception: # Vista has no app uids + return + AppUserModelID.argtypes = [wintypes.LPWSTR] + AppUserModelID.restype = wintypes.HRESULT + try: + AppUserModelID(ctypes.cast(ctypes.byref(lpBuffer), wintypes.LPWSTR)) + except Exception: + return + appid = lpBuffer.value + ctypes.windll.ole32.CoTaskMemFree(lpBuffer) + return appid + + +def set_app_uid(val): + import ctypes + from ctypes import wintypes + try: + AppUserModelID = ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID + except Exception: # Vista has no app uids + return False + AppUserModelID.argtypes = [wintypes.LPCWSTR] + AppUserModelID.restype = wintypes.HRESULT + try: + AppUserModelID(unicode(val)) + except Exception as err: + prints(u'Failed to set app uid with error:', as_unicode(err)) + return False + return True diff --git a/src/calibre/gui2/main.py b/src/calibre/gui2/main.py index 38264661dd..df0bab24f9 100644 --- a/src/calibre/gui2/main.py +++ b/src/calibre/gui2/main.py @@ -15,12 +15,12 @@ from PyQt5.Qt import QCoreApplication, QIcon, QObject, QTimer from calibre import force_unicode, plugins, prints from calibre.constants import ( - DEBUG, __appname__, filesystem_encoding, get_portable_base, islinux, isosx, - iswindows, MAIN_APP_UID + DEBUG, MAIN_APP_UID, __appname__, filesystem_encoding, get_portable_base, + islinux, isosx, iswindows ) from calibre.gui2 import ( Application, choose_dir, error_dialog, gprefs, initialize_file_icon_provider, - question_dialog, setup_gui_option_parser + question_dialog, set_app_uid, setup_gui_option_parser ) from calibre.gui2.main_window import option_parser as _option_parser from calibre.gui2.splash_screen import SplashScreen @@ -522,11 +522,7 @@ def main(args=sys.argv): # Ensure that all ebook editor instances are grouped together in the task # bar. This prevents them from being grouped with viewer process when # launched from within calibre, as both use calibre-parallel.exe - import ctypes - try: - ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID(MAIN_APP_UID) - except Exception: - pass # Only available on windows 7 and newer + set_app_uid(MAIN_APP_UID) try: app, opts, args = init_qt(args) diff --git a/src/calibre/gui2/tweak_book/main.py b/src/calibre/gui2/tweak_book/main.py index 4c9934e1b2..7a419e3a94 100644 --- a/src/calibre/gui2/tweak_book/main.py +++ b/src/calibre/gui2/tweak_book/main.py @@ -11,7 +11,7 @@ from PyQt5.Qt import QIcon from calibre.constants import EDITOR_APP_UID, islinux, iswindows from calibre.gui2 import ( - Application, decouple, set_gui_prefs, setup_gui_option_parser + Application, decouple, set_app_uid, set_gui_prefs, setup_gui_option_parser ) from calibre.ptempfile import reset_base_dir from calibre.utils.config import OptionParser @@ -57,13 +57,7 @@ def _run(args, notify=None): # Ensure that all ebook editor instances are grouped together in the task # bar. This prevents them from being grouped with viewer process when # launched from within calibre, as both use calibre-parallel.exe - import ctypes - try: - ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID( - EDITOR_APP_UID - ) - except Exception: - pass # Only available on windows 7 and newer + set_app_uid(EDITOR_APP_UID) # The following two lines are needed to prevent circular imports causing # errors during initialization of plugins that use the polish container diff --git a/src/calibre/gui2/viewer/main.py b/src/calibre/gui2/viewer/main.py index b51e38dc5c..0a0d3c921b 100644 --- a/src/calibre/gui2/viewer/main.py +++ b/src/calibre/gui2/viewer/main.py @@ -22,7 +22,7 @@ from calibre.customize.ui import available_input_formats from calibre.ebooks.oeb.iterator.book import EbookIterator from calibre.gui2 import ( Application, choose_files, error_dialog, info_dialog, open_url, - setup_gui_option_parser + setup_gui_option_parser, set_app_uid ) from calibre.gui2.viewer.toc import TOC from calibre.gui2.viewer.ui import Main as MainWindow @@ -1262,11 +1262,7 @@ def main(args=sys.argv): # Ensure that all ebook editor instances are grouped together in the task # bar. This prevents them from being grouped with viewer process when # launched from within calibre, as both use calibre-parallel.exe - import ctypes - try: - ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID(VIEWER_APP_UID) - except Exception: - pass # Only available on windows 7 and newer + set_app_uid(VIEWER_APP_UID) parser = option_parser() opts, args = parser.parse_args(args)