1
0
mirror of https://github.com/EDCD/EDMarketConnector.git synced 2025-04-13 07:47:14 +03:00

prefs.py: Attempting to fix non-utf-8 case of "choose Output file location"

1. `SHGetPathFromIDListW` needing fixing, which was achieved, but...
2. ... then `SHBrowseForFolderW()` as-was returned `int` instead of a pointer
  to the correct structure.

Trying to fix 2 has proven intractable:

a. Trying to cast the `int` return just results in `exception: access violation
  reading <address>`.
b. Trying to define `SHBrowseForFolderW` properly, so it returns the correct
  type results in a *writing* access violation when called, despite passing
  the exact same data in as for the 'raw' call version.

So, this commit is a record, and I'm next going to try switching to
`IFileDialog` as recommended by the docs for `SHBrowseForFolderW` ('For
Windows Vista or later').
This commit is contained in:
Athanasius 2022-11-17 20:52:25 +00:00
parent f141fccd86
commit da530f135e
No known key found for this signature in database
GPG Key ID: 772697E181BB2767

View File

@ -181,7 +181,10 @@ if sys.platform == 'darwin':
elif sys.platform == 'win32':
import ctypes
import winreg
from ctypes.wintypes import HINSTANCE, HWND, LPARAM, LPCWSTR, LPVOID, LPWSTR, MAX_PATH, POINT, RECT, SIZE, UINT
from ctypes import POINTER, WINFUNCTYPE, Structure
from ctypes.wintypes import (
BOOL, BYTE, HINSTANCE, HWND, LPARAM, LPCWSTR, LPWSTR, MAX_PATH, POINT, RECT, SIZE, UINT, USHORT
)
is_wine = False
try:
WINE_REGISTRY_KEY = r'HKEY_LOCAL_MACHINE\Software\Wine'
@ -192,18 +195,68 @@ elif sys.platform == 'win32':
except OSError:
pass
###########################################################################
# From <https://github.com/hakril/PythonForWindows/blob/master/windows/generated_def/winstructs.py>
class _SHITEMID(ctypes.Structure):
_fields_ = [
("cb", USHORT),
("abID", BYTE * (1)),
]
SHITEMID = _SHITEMID
class _ITEMIDLIST(Structure):
_fields_ = [
("mkid", SHITEMID),
]
ITEMIDLIST = _ITEMIDLIST
PCIDLIST_ABSOLUTE = ctypes.POINTER(_ITEMIDLIST)
PIDLIST_ABSOLUTE = ctypes.POINTER(_ITEMIDLIST)
###########################################################################
###########################################################################
# From: <https://stackoverflow.com/questions/32979525/creating-choose-folder-dialog-from-python-using-windows-api>
BrowseCallbackProc = WINFUNCTYPE(ctypes.c_int, HWND, ctypes.c_uint, LPARAM, LPARAM)
class BROWSEINFOW(ctypes.Structure):
"""
Windows file browser fields.
Ref: <https://learn.microsoft.com/en-us/windows/win32/api/shlobj_core/ns-shlobj_core-browseinfow>
"""
_fields_ = [
("hwndOwner", HWND),
("pidlRoot", PCIDLIST_ABSOLUTE),
("pszDisplayName", LPWSTR),
("lpszTitle", LPCWSTR),
("ulFlags", UINT),
("lpfn", BrowseCallbackProc),
("lParam", LPARAM),
("iImage", ctypes.c_int)
]
LPBROWSEINFOW = POINTER(BROWSEINFOW)
###########################################################################
# https://msdn.microsoft.com/en-us/library/windows/desktop/bb762115
BIF_RETURNONLYFSDIRS = 0x00000001
BIF_USENEWUI = 0x00000050
BFFM_INITIALIZED = 1
BFFM_SETSELECTION = 0x00000467
BrowseCallbackProc = ctypes.WINFUNCTYPE(ctypes.c_int, HWND, ctypes.c_uint, LPARAM, LPARAM)
# SHGetPathFromIDListW
# Ref: <https://learn.microsoft.com/en-us/windows/win32/api/shlobj_core/nf-shlobj_core-shgetpathfromidlistw>
# BOOL SHGetPathFromIDListW([in] PCIDLIST_ABSOLUTE pidl,[out] LPWSTR pszPath);
prototype = WINFUNCTYPE(BOOL, PCIDLIST_ABSOLUTE, LPCWSTR)
paramflags = (1, "pidl"), (2, "pszPath", "")
SHGetPathFromIDListW = prototype(("SHGetPathFromIDListW", ctypes.windll.shell32), paramflags)
class BROWSEINFO(ctypes.Structure):
"""Windows file browser fields."""
_fields_ = [("hwndOwner", HWND), ("pidlRoot", LPVOID), ("pszDisplayName", LPWSTR), ("lpszTitle", LPCWSTR),
("ulFlags", UINT), ("lpfn", BrowseCallbackProc), ("lParam", LPCWSTR), ("iImage", ctypes.c_int)]
# SHBrowseForFolderW
# Ref: <https://learn.microsoft.com/en-us/windows/win32/api/shlobj_core/nf-shlobj_core-shbrowseforfolderw>
# PIDLIST_ABSOLUTE SHBrowseForFolderW([in] LPBROWSEINFOWW lpbi);
prototype = WINFUNCTYPE(PIDLIST_ABSOLUTE, LPBROWSEINFOW)
paramflags2 = (1, "lpbi"),
SHBrowseForFolderW = prototype(("SHGetPathFromIDListW", ctypes.windll.shell32), paramflags2)
CalculatePopupWindowPosition = None
if not is_wine:
@ -1037,17 +1090,23 @@ class PreferencesDialog(tk.Toplevel):
ctypes.windll.user32.SendMessageW(hwnd, BFFM_SETSELECTION, 1, lpData)
return 0
browseInfo = BROWSEINFO() # noqa: N806 # Windows convention
browseInfo = BROWSEINFOW() # noqa: N806 # Windows convention
# browseInfo.pidlRoot = None
browseInfo.pszDisplayName = ctypes.c_wchar_p("x" * MAX_PATH)
browseInfo.lpszTitle = title
browseInfo.ulFlags = BIF_RETURNONLYFSDIRS | BIF_USENEWUI
browseInfo.lpfn = BrowseCallbackProc(browsecallback)
browseInfo.lParam = pathvar.get().startswith('~') and join(config.home_path,
pathvar.get()[2:]) or pathvar.get()
# browseInfo.lParam = pathvar.get().startswith('~') and join(config.home_path,
# pathvar.get()[2:]) or pathvar.get()
ctypes.windll.ole32.CoInitialize(None)
pidl = ctypes.windll.shell32.SHBrowseForFolderW(ctypes.byref(browseInfo))
# ctypes.ArgumentError: argument 1: <class 'TypeError'>: expected LP__ITEMIDLIST instance instead of int
# pidl = SHBrowseForFolderW(ctypes.byref(browseInfo))
# OSError: exception: access violation writing 0x00007FFB582DC9BF
if pidl:
pidl = ctypes.cast(pidl, PIDLIST_ABSOLUTE)
path = ctypes.create_unicode_buffer(MAX_PATH)
ctypes.windll.shell32.SHGetPathFromIDListW(pidl, path)
path = SHGetPathFromIDListW(pidl)
ctypes.windll.ole32.CoTaskMemFree(pidl)
directory = path.value
else: