py-window-styles icon indicating copy to clipboard operation
py-window-styles copied to clipboard

`apply_dnd()` doesn't work correctly with paths that contain Unicode characters

Open Valer100 opened this issue 1 year ago • 1 comments

When I try to drag and drop a file or a folder that has Unicode characters in their path, apply_dnd() will throw a UnicodeDecodeError. Here's the full exception message:

Exception ignored on calling ctypes callback function: <function apply_dnd.__init__.<locals>.py_drop_func at 0x0000026A686B23E0>
Traceback (most recent call last):
  File "C:\Program Files\Python311\Lib\site-packages\pywinstyles\py_win_style.py", line 205, in py_drop_func
    drop_name = file_buffer.value.decode("utf-8")
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xe2 in position 25: invalid continuation byte

Minimal reproducible example:

import tkinter, pywinstyles
from tkinter import messagebox

window = tkinter.Tk()

def print_dnd_paths(file): print(file)
pywinstyles.apply_dnd(window, print_dnd_paths)

window.mainloop()

Here's a screen recording:

https://github.com/user-attachments/assets/b3199fc0-9848-4a6d-bb44-f26f0d8b96e5

The folder's name that included Unicode characters: ăâîșț

Windows 11 23H2 (Build 22631.4391) Python 3.11.9 pywinstyles 1.8

Valer100 avatar Oct 27 '24 14:10 Valer100

You can fix the issue in this way:

    def py_drop_func(hwnd, msg, wp, lp):
        global files
        if msg == WM_DROP_FILES:
            count = func_DragQueryFile(typ(wp), -1, None, None)
            file_buffer = create_buffer(char_limit)
            files = []
            for i in range(count):
                func_DragQueryFile(typ(wp), i, file_buffer, sizeof(file_buffer))
                try:
                    drop_name = file_buffer.value.decode("utf-8")
                except UnicodeDecodeError:
                    # If UTF-8 decoding fails, attempt to decode with a different encoding (e.g., Windows-1252)
                    drop_name = file_buffer.value.decode("Windows-1252")
                 
                files.append(drop_name)
            func(files)
            windll.shell32.DragFinish(typ(wp))

        return windll.user32.CallWindowProcW(
            *map(typ, (globals()[old], hwnd, msg, wp, lp))
        )

B16f00t avatar Jan 25 '25 11:01 B16f00t