From 823f8caf0a039b62a8d099692e0b353b29e86c50 Mon Sep 17 00:00:00 2001 From: Jonathan Harris Date: Mon, 30 Sep 2019 00:03:34 +0100 Subject: [PATCH] Switch to built-in winreg module on Windows --- config.py | 116 +++++++++++-------------------------------- ttkHyperlinkLabel.py | 69 ++++++++----------------- 2 files changed, 51 insertions(+), 134 deletions(-) diff --git a/config.py b/config.py index c1fda942..0b4e30f5 100644 --- a/config.py +++ b/config.py @@ -21,6 +21,7 @@ elif platform=='win32': import ctypes from ctypes.wintypes import * import uuid + from winreg import CloseKey, CreateKeyEx, OpenKeyEx, DeleteValue, QueryValueEx, SetValueEx, HKEY_CURRENT_USER, KEY_ALL_ACCESS, REG_SZ, REG_DWORD, REG_MULTI_SZ FOLDERID_Documents = uuid.UUID('{FDD39AD0-238F-46AF-ADB4-6C85480369C7}') FOLDERID_LocalAppData = uuid.UUID('{F1B32785-6FBA-4FCF-9D55-7B8E7F157091}') @@ -33,47 +34,6 @@ elif platform=='win32': CoTaskMemFree = ctypes.windll.ole32.CoTaskMemFree CoTaskMemFree.argtypes = [ctypes.c_void_p] - # _winreg that ships with Python 2 doesn't support unicode, so do this instead - HKEY_CURRENT_USER = 0x80000001 - KEY_ALL_ACCESS = 0x000F003F - REG_CREATED_NEW_KEY = 0x00000001 - REG_OPENED_EXISTING_KEY = 0x00000002 - REG_SZ = 1 - REG_DWORD = 4 - REG_MULTI_SZ = 7 - - RegCreateKeyEx = ctypes.windll.advapi32.RegCreateKeyExW - RegCreateKeyEx.restype = LONG - RegCreateKeyEx.argtypes = [HKEY, LPCWSTR, DWORD, LPCVOID, DWORD, DWORD, LPCVOID, ctypes.POINTER(HKEY), ctypes.POINTER(DWORD)] - - RegOpenKeyEx = ctypes.windll.advapi32.RegOpenKeyExW - RegOpenKeyEx.restype = LONG - RegOpenKeyEx.argtypes = [HKEY, LPCWSTR, DWORD, DWORD, ctypes.POINTER(HKEY)] - - RegCloseKey = ctypes.windll.advapi32.RegCloseKey - RegCloseKey.restype = LONG - RegCloseKey.argtypes = [HKEY] - - RegQueryValueEx = ctypes.windll.advapi32.RegQueryValueExW - RegQueryValueEx.restype = LONG - RegQueryValueEx.argtypes = [HKEY, LPCWSTR, LPCVOID, ctypes.POINTER(DWORD), LPCVOID, ctypes.POINTER(DWORD)] - - RegSetValueEx = ctypes.windll.advapi32.RegSetValueExW - RegSetValueEx.restype = LONG - RegSetValueEx.argtypes = [HKEY, LPCWSTR, LPCVOID, DWORD, LPCVOID, DWORD] - - RegCopyTree = ctypes.windll.advapi32.RegCopyTreeW - RegCopyTree.restype = LONG - RegCopyTree.argtypes = [HKEY, LPCWSTR, HKEY] - - RegDeleteKey = ctypes.windll.advapi32.RegDeleteTreeW - RegDeleteKey.restype = LONG - RegDeleteKey.argtypes = [HKEY, LPCWSTR] - - RegDeleteValue = ctypes.windll.advapi32.RegDeleteValueW - RegDeleteValue.restype = LONG - RegDeleteValue.argtypes = [HKEY, LPCWSTR] - def KnownFolderPath(guid): buf = ctypes.c_wchar_p() if SHGetKnownFolderPath(ctypes.create_string_buffer(guid.bytes_le), 0, 0, ctypes.byref(buf)): @@ -193,76 +153,58 @@ class Config(object): self.identifier = applongname - self.hkey = HKEY() - disposition = DWORD() - if RegCreateKeyEx(HKEY_CURRENT_USER, r'Software\Marginal\EDMarketConnector', 0, None, 0, KEY_ALL_ACCESS, None, ctypes.byref(self.hkey), ctypes.byref(disposition)): - raise Exception() - - if disposition.value == REG_CREATED_NEW_KEY: - - # Migrate pre-1.3.4 registry location - oldkey = HKEY() - if not RegOpenKeyEx(HKEY_CURRENT_USER, r'Software\EDMarketConnector', 0, KEY_ALL_ACCESS, ctypes.byref(oldkey)): - RegCopyTree(oldkey, None, self.hkey) - RegCloseKey(oldkey) - RegDeleteKey(HKEY_CURRENT_USER, r'Software\EDMarketConnector') - + self.hkey = CreateKeyEx(HKEY_CURRENT_USER, r'Software\Marginal\EDMarketConnector', access=KEY_ALL_ACCESS) + try: + sparklekey = OpenKeyEx(hkey, 'WinSparkle') + except: # set WinSparkle defaults - https://github.com/vslavik/winsparkle/wiki/Registry-Settings - sparklekey = HKEY() - if not RegCreateKeyEx(self.hkey, 'WinSparkle', 0, None, 0, KEY_ALL_ACCESS, None, ctypes.byref(sparklekey), ctypes.byref(disposition)): - if disposition.value == REG_CREATED_NEW_KEY: - buf = ctypes.create_unicode_buffer('1') - RegSetValueEx(sparklekey, 'CheckForUpdates', 0, 1, buf, len(buf)*2) - buf = ctypes.create_unicode_buffer(str(update_interval)) - RegSetValueEx(sparklekey, 'UpdateInterval', 0, 1, buf, len(buf)*2) - RegCloseKey(sparklekey) + sparklekey = CreateKeyEx(self.hkey, 'WinSparkle', access=KEY_ALL_ACCESS) + SetValueEx(sparklekey, 'CheckForUpdates', 0, REG_SZ, '1') + SetValueEx(sparklekey, 'UpdateInterval', 0, REG_SZ, str(update_interval)) + CloseKey(sparklekey) if not self.get('outdir') or not isdir(self.get('outdir')): self.set('outdir', KnownFolderPath(FOLDERID_Documents) or self.home) def get(self, key): - typ = DWORD() - size = DWORD() - if RegQueryValueEx(self.hkey, key, 0, ctypes.byref(typ), None, ctypes.byref(size)) or typ.value not in [REG_SZ, REG_MULTI_SZ]: + try: + (value, typ) = QueryValueEx(self.hkey, key) + except: return None - buf = ctypes.create_unicode_buffer(int(size.value / 2)) - if RegQueryValueEx(self.hkey, key, 0, ctypes.byref(typ), buf, ctypes.byref(size)): + if typ not in [REG_SZ, REG_MULTI_SZ]: return None - elif typ.value == REG_MULTI_SZ: - return [x for x in ctypes.wstring_at(buf, len(buf)-2).split(u'\x00')] - else: - return str(buf.value) + return value def getint(self, key): - typ = DWORD() - size = DWORD(4) - val = DWORD() - if RegQueryValueEx(self.hkey, key, 0, ctypes.byref(typ), ctypes.byref(val), ctypes.byref(size)) or typ.value != REG_DWORD: - return 0 - else: - return val.value + try: + (value, typ) = QueryValueEx(self.hkey, key) + except: + return None + if typ != REG_DWORD: + return None + return value def set(self, key, val): if isinstance(val, str): - buf = ctypes.create_unicode_buffer(val) - RegSetValueEx(self.hkey, key, 0, REG_SZ, buf, len(buf)*2) + SetValueEx(self.hkey, key, 0, REG_SZ, val) elif isinstance(val, numbers.Integral): - RegSetValueEx(self.hkey, key, 0, REG_DWORD, ctypes.byref(DWORD(val)), 4) + SetValueEx(self.hkey, key, 0, REG_DWORD, val) elif hasattr(val, '__iter__'): # iterable - stringval = u'\x00'.join([str(x) or u' ' for x in val] + [u'']) # null terminated non-empty strings - buf = ctypes.create_unicode_buffer(stringval) - RegSetValueEx(self.hkey, key, 0, REG_MULTI_SZ, buf, len(buf)*2) + SetValueEx(self.hkey, key, 0, REG_MULTI_SZ, val) else: raise NotImplementedError() def delete(self, key): - RegDeleteValue(self.hkey, key) + try: + DeleteValue(self.hkey, key) + except: + pass def save(self): pass # Redundant since registry keys are written immediately def close(self): - RegCloseKey(self.hkey) + CloseKey(self.hkey) self.hkey = None elif platform=='linux': diff --git a/ttkHyperlinkLabel.py b/ttkHyperlinkLabel.py index 8ab9f934..a04a8b3b 100644 --- a/ttkHyperlinkLabel.py +++ b/ttkHyperlinkLabel.py @@ -9,29 +9,7 @@ if platform == 'win32': import subprocess import ctypes from ctypes.wintypes import * - - HKEY_CLASSES_ROOT = 0x80000000 - HKEY_CURRENT_USER = 0x80000001 - - KEY_READ = 0x00020019 - KEY_ALL_ACCESS = 0x000F003F - - REG_SZ = 1 - REG_DWORD = 4 - REG_MULTI_SZ = 7 - - RegOpenKeyEx = ctypes.windll.advapi32.RegOpenKeyExW - RegOpenKeyEx.restype = LONG - RegOpenKeyEx.argtypes = [HKEY, LPCWSTR, DWORD, DWORD, ctypes.POINTER(HKEY)] - - RegCloseKey = ctypes.windll.advapi32.RegCloseKey - RegCloseKey.restype = LONG - RegCloseKey.argtypes = [HKEY] - - RegQueryValueEx = ctypes.windll.advapi32.RegQueryValueExW - RegQueryValueEx.restype = LONG - RegQueryValueEx.argtypes = [HKEY, LPCWSTR, LPCVOID, ctypes.POINTER(DWORD), LPCVOID, ctypes.POINTER(DWORD)] - + from winreg import CloseKey, OpenKeyEx, QueryValueEx, HKEY_CLASSES_ROOT, HKEY_CURRENT_USER, KEY_READ, REG_SZ, REG_MULTI_SZ # A clickable ttk Label # @@ -138,34 +116,31 @@ def openurl(url): # so discover and launch the browser directly. # https://blogs.msdn.microsoft.com/oldnewthing/20031210-00/?p=41553 - hkey = HKEY() - cls = 'http' - if not RegOpenKeyEx(HKEY_CURRENT_USER, r'Software\Microsoft\Windows\Shell\Associations\UrlAssociations\http\UserChoice', 0, KEY_READ, ctypes.byref(hkey)): - typ = DWORD() - size = DWORD() - if not RegQueryValueEx(hkey, 'ProgId', 0, ctypes.byref(typ), None, ctypes.byref(size)) and typ.value in [REG_SZ, REG_MULTI_SZ]: - buf = ctypes.create_unicode_buffer('', int(size.value / 2)) - if not RegQueryValueEx(hkey, 'ProgId', 0, ctypes.byref(typ), buf, ctypes.byref(size)): - if buf.value in ['IE.HTTP', 'AppXq0fevzme2pys62n3e0fbqa7peapykr8v']: - # IE and Edge can't handle long arguments so just use webbrowser.open and hope - # https://blogs.msdn.microsoft.com/ieinternals/2014/08/13/url-length-limits/ - cls = None - else: - cls = buf.value - RegCloseKey(hkey) + try: + hkey = OpenKeyEx(HKEY_CURRENT_USER, r'Software\Microsoft\Windows\Shell\Associations\UrlAssociations\https\UserChoice') + (value, typ) = QueryValueEx(hkey, 'ProgId') + CloseKey(hkey) + if value in ['IE.HTTP', 'AppXq0fevzme2pys62n3e0fbqa7peapykr8v']: + # IE and Edge can't handle long arguments so just use webbrowser.open and hope + # https://blogs.msdn.microsoft.com/ieinternals/2014/08/13/url-length-limits/ + cls = None + else: + cls = value + except: + cls = 'https' - if cls and not RegOpenKeyEx(HKEY_CLASSES_ROOT, r'%s\shell\open\command' % cls, 0, KEY_READ, ctypes.byref(hkey)): - typ = DWORD() - size = DWORD() - if not RegQueryValueEx(hkey, None, 0, ctypes.byref(typ), None, ctypes.byref(size)) and typ.value in [REG_SZ, REG_MULTI_SZ]: - buf = ctypes.create_unicode_buffer('', int(size.value / 2)) - if not RegQueryValueEx(hkey, None, 0, ctypes.byref(typ), buf, ctypes.byref(size)) and 'iexplore' not in buf.value.lower(): - RegCloseKey(hkey) - if '%1' in buf.value: + if cls: + try: + hkey = OpenKeyEx(HKEY_CLASSES_ROOT, r'%s\shell\open\command' % cls) + (value, typ) = QueryValueEx(hkey, None) + CloseKey(hkey) + if 'iexplore' not in value.lower(): + if '%1' in value: subprocess.Popen(buf.value.replace('%1', url)) else: subprocess.Popen('%s "%s"' % (buf.value, url)) return - RegCloseKey(hkey) + except: + pass webbrowser.open(url)