From d1cdc89ecd6e2d39aad3d767c72afc9d12c20df0 Mon Sep 17 00:00:00 2001 From: Jonathan Harris <jonathan@marginal.org.uk> Date: Sun, 20 Nov 2016 00:58:04 +0000 Subject: [PATCH] Work around Windows limit on ShellExecute arg len --- README.md | 3 ++ ttkHyperlinkLabel.py | 66 +++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 68 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 21c72ee2..73a4c017 100644 --- a/README.md +++ b/README.md @@ -134,6 +134,9 @@ This app uses Elite: Dangerous' “journal” files to track the systems and sta ### Error: Can't connect to EDDN EDMC needs to talk to eddn-gateway.elite-markets.net on port 8080. If you consistently receive this error check that your router or VPN configuration allows port 8080 / tcp outbound. +### Import failed: No ship loadout found +Complex ship loadouts with Engineers' mods can cause you to hit an Internet Explorer / Edge limitation on the length of URLs. Switch to a browser that doesn't suck. + Running from source -------- diff --git a/ttkHyperlinkLabel.py b/ttkHyperlinkLabel.py index 1d084235..7c2fe0bb 100644 --- a/ttkHyperlinkLabel.py +++ b/ttkHyperlinkLabel.py @@ -5,6 +5,33 @@ import Tkinter as tk import ttk import tkFont +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)] + # A clickable ttk Label # @@ -89,7 +116,7 @@ class HyperlinkLabel(platform == 'darwin' and tk.Label or ttk.Label, object): url = self.url(self['text']) if callable(self.url) else self.url if url: self._leave(event) # Remove underline before we change window to browser - webbrowser.open(url) + openurl(url) def _contextmenu(self, event): if self['text'] and (self.popup_copy(self['text']) if callable(self.popup_copy) else self.popup_copy): @@ -100,3 +127,40 @@ class HyperlinkLabel(platform == 'darwin' and tk.Label or ttk.Label, object): self.clipboard_append(self['text']) +def openurl(url): + if platform == 'win32': + # On Windows webbrowser.open calls os.startfile which calls ShellExecute which can't handle long arguments, + # 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(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) + + 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(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: + subprocess.Popen(buf.value.replace('%1', url)) + else: + subprocess.Popen('%s "%s"' % (buf.value, url)) + return + RegCloseKey(hkey) + + webbrowser.open(url)