From ea74df97ec2f2ba4dd79742bc4cd7bc91182b1ea Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 12 Jun 2019 07:07:28 +0530 Subject: [PATCH] Get rid of the last use of win32com --- src/calibre/gui2/preferences/server.py | 56 +++++++------------ src/calibre/utils/windows/winutil.c | 5 ++ src/calibre/utils/windows/winutilpp.cpp | 72 +++++++++++++++++++++++-- 3 files changed, 93 insertions(+), 40 deletions(-) diff --git a/src/calibre/gui2/preferences/server.py b/src/calibre/gui2/preferences/server.py index ea630285a5..bab64f2094 100644 --- a/src/calibre/gui2/preferences/server.py +++ b/src/calibre/gui2/preferences/server.py @@ -2,6 +2,8 @@ # vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai # License: GPLv3 Copyright: 2010, Kovid Goyal +from __future__ import absolute_import, division, print_function, unicode_literals + import errno import json import numbers @@ -53,45 +55,26 @@ if iswindows and not isportable: startup_path = winutil.special_folder_path(winutil.CSIDL_STARTUP) return os.path.join(startup_path, "calibre.lnk") - class Shortcut(object): + def create_shortcut(shortcut_path, target, description, *args): + quoted_args = None + if args: + quoted_args = [] + for arg in args: + quoted_args.append('"{}"'.format(arg)) + quoted_args = ' '.join(quoted_args) + plugins['winutil'][0].manage_shortcut(shortcut_path, target, description, quoted_args) - def __enter__(self): - import pythoncom - from win32com.shell import shell - pythoncom.CoInitialize() - self.instance = pythoncom.CoCreateInstance(shell.CLSID_ShellLink, None, pythoncom.CLSCTX_INPROC_SERVER, shell.IID_IShellLink) - self.persist_file = self.instance.QueryInterface(pythoncom.IID_IPersistFile) - return self - - def __exit__(self, *a): - import pythoncom - del self.instance - del self.persist_file - pythoncom.CoUninitialize() - - def create_at(self, shortcut_path, target, description, *args): - shortcut = self.instance - shortcut.SetPath(target) - shortcut.SetIconLocation(target, 0) - shortcut.SetDescription(description) - if args: - quoted_args = [] - for arg in args: - quoted_args.append('"{}"'.format(arg)) - shortcut.SetArguments(' '.join(quoted_args)) - self.persist_file.Save(shortcut_path, 0) - - def exists_at(self, shortcut_path, target): - if not os.access(shortcut_path, os.R_OK): - return False - self.persist_file.Load(shortcut_path) - name = self.instance.GetPath(8)[0] - return os.path.normcase(os.path.abspath(name)) == os.path.normcase(os.path.abspath(get_exe())) + def shortcut_exists_at(shortcut_path, target): + if not os.access(shortcut_path, os.R_OK): + return False + name = plugins['winutil'][0].manage_shortcut(shortcut_path, None, None, None) + if name is None: + return False + return os.path.normcase(os.path.abspath(name)) == os.path.normcase(os.path.abspath(target)) def set_run_at_startup(run_at_startup=True): if run_at_startup: - with Shortcut() as shortcut: - shortcut.create_at(startup_shortcut_path(), get_exe(), 'calibre - E-book management', '--start-in-tray') + create_shortcut(startup_shortcut_path(), get_exe(), 'calibre - E-book management', '--start-in-tray') else: shortcut_path = startup_shortcut_path() if os.path.exists(shortcut_path): @@ -99,8 +82,7 @@ if iswindows and not isportable: def is_set_to_run_at_startup(): try: - with Shortcut() as shortcut: - return shortcut.exists_at(startup_shortcut_path(), get_exe()) + return shortcut_exists_at(startup_shortcut_path(), get_exe()) except Exception: import traceback traceback.print_exc() diff --git a/src/calibre/utils/windows/winutil.c b/src/calibre/utils/windows/winutil.c index a2b564d29f..651d5e8682 100644 --- a/src/calibre/utils/windows/winutil.c +++ b/src/calibre/utils/windows/winutil.c @@ -380,6 +380,7 @@ extern PyObject *winutil_file_association(PyObject *self, PyObject *args); extern PyObject *winutil_friendly_name(PyObject *self, PyObject *args); extern PyObject *winutil_notify_associations_changed(PyObject *self, PyObject *args); extern PyObject *winutil_move_to_trash(PyObject *self, PyObject *args); +extern PyObject *winutil_manage_shortcut(PyObject *self, PyObject *args); static PyMethodDef winutil_methods[] = { {"special_folder_path", winutil_folder_path, METH_VARARGS, @@ -467,6 +468,10 @@ be a unicode string. Returns unicode strings." "move_to_trash()\n\nMove the specified path to trash" }, + {"manage_shortcut", (PyCFunction)winutil_manage_shortcut, METH_VARARGS, + "manage_shortcut()\n\nManage a shortcut" + }, + {NULL, NULL, 0, NULL} }; diff --git a/src/calibre/utils/windows/winutilpp.cpp b/src/calibre/utils/windows/winutilpp.cpp index edde6a795f..b741aba19b 100644 --- a/src/calibre/utils/windows/winutilpp.cpp +++ b/src/calibre/utils/windows/winutilpp.cpp @@ -79,13 +79,15 @@ class wchar_raii { // {{{ wchar_raii() : handle(NULL) {} ~wchar_raii() { + if (handle) { #if PY_MAJOR_VERSION >= 3 - PyMem_Free(*handle); + PyMem_Free(*handle); #endif - *handle = NULL; + *handle = NULL; + } } - wchar_t *ptr() { return *handle; } + wchar_t *ptr() { return handle ? *handle : NULL; } void set_ptr(wchar_t **val) { handle = val; } }; // }}} @@ -215,4 +217,68 @@ winutil_move_to_trash(PyObject *self, PyObject *args) { Py_RETURN_NONE; } +PyObject * +winutil_manage_shortcut(PyObject *self, PyObject *args) { + wchar_raii path, target, description, quoted_args; + if (!PyArg_ParseTuple(args, "O&O&O&O&", py_to_wchar, &path, py_to_wchar, &target, py_to_wchar, &description, py_to_wchar, "ed_args)) return NULL; + if (!path.ptr()) { + PyErr_SetString(PyExc_TypeError, "Path must not be None"); + return NULL; + } + + scoped_com_initializer com; + if (!com.succeded()) { PyErr_SetString(PyExc_OSError, "Failed to initialize COM"); return NULL; } + + CComPtr shell_link; + if (FAILED(CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&shell_link)))) { + PyErr_SetString(PyExc_OSError, "Failed to create IShellLink instance"); + return NULL; + } + CComPtr persist_file; + if (FAILED(shell_link->QueryInterface(IID_PPV_ARGS(&persist_file)))) { + PyErr_SetString(PyExc_OSError, "Failed to create IPersistFile instance"); + return NULL; + } + + if (!target.ptr()) { + wchar_t buf[2048]; + if (FAILED(persist_file->Load(path.ptr(), 0))) Py_RETURN_NONE; + if (FAILED(shell_link->GetPath(buf, sizeof(buf), NULL, 0))) Py_RETURN_NONE; + return Py_BuildValue("u", buf); + } + + if (FAILED(shell_link->SetPath(target.ptr()))) { + PyErr_SetString(PyExc_OSError, "Failed to set shortcut target"); + return NULL; + } + + if (FAILED(shell_link->SetIconLocation(target.ptr(), 0))) { + PyErr_SetString(PyExc_OSError, "Failed to set shortcut icon"); + return NULL; + } + + if (description.ptr()) { + if (FAILED(shell_link->SetDescription(description.ptr()))) { + PyErr_SetString(PyExc_OSError, "Failed to set shortcut description"); + return NULL; + } + } + + if (quoted_args.ptr()) { + if (FAILED(shell_link->SetArguments(quoted_args.ptr()))) { + PyErr_SetString(PyExc_OSError, "Failed to set shortcut arguments"); + return NULL; + } + } + + if (FAILED(persist_file->Save(path.ptr(), FALSE))) { + PyErr_SetString(PyExc_OSError, "Failed to save the shortcut"); + return NULL; + } + + Py_RETURN_NONE; + +} + +// end extern "C" }