mirror of
https://github.com/arsenetar/send2trash.git
synced 2024-12-08 05:39:02 +00:00
020d05979d
By using the short path version of a file, we can manage to move long paths to the trash. Limitations: 1/ If the final short path is longer than what `SHFileOperationW` can handle, it will fail 2/ Still not able to trash long path from another drive, ie: trying to delete C:\temp\foo.txt while the script is running from D:\trash.py
87 lines
2.8 KiB
Python
87 lines
2.8 KiB
Python
# Copyright 2017 Virgil Dupras
|
|
|
|
# This software is licensed under the "BSD" License as described in the "LICENSE" file,
|
|
# which should be included with this package. The terms are also available at
|
|
# http://www.hardcoded.net/licenses/bsd_license
|
|
|
|
from __future__ import unicode_literals
|
|
|
|
from ctypes import (windll, Structure, byref, c_uint,
|
|
create_unicode_buffer, addressof)
|
|
from ctypes.wintypes import HWND, UINT, LPCWSTR, BOOL
|
|
import os.path as op
|
|
|
|
from .compat import text_type
|
|
|
|
kernel32 = windll.kernel32
|
|
GetShortPathNameW = kernel32.GetShortPathNameW
|
|
|
|
shell32 = windll.shell32
|
|
SHFileOperationW = shell32.SHFileOperationW
|
|
|
|
|
|
class SHFILEOPSTRUCTW(Structure):
|
|
_fields_ = [
|
|
("hwnd", HWND),
|
|
("wFunc", UINT),
|
|
("pFrom", LPCWSTR),
|
|
("pTo", LPCWSTR),
|
|
("fFlags", c_uint),
|
|
("fAnyOperationsAborted", BOOL),
|
|
("hNameMappings", c_uint),
|
|
("lpszProgressTitle", LPCWSTR),
|
|
]
|
|
|
|
|
|
FO_MOVE = 1
|
|
FO_COPY = 2
|
|
FO_DELETE = 3
|
|
FO_RENAME = 4
|
|
|
|
FOF_MULTIDESTFILES = 1
|
|
FOF_SILENT = 4
|
|
FOF_NOCONFIRMATION = 16
|
|
FOF_ALLOWUNDO = 64
|
|
FOF_NOERRORUI = 1024
|
|
|
|
|
|
def get_short_path_name(long_name):
|
|
if not long_name.startswith('\\\\?\\'):
|
|
long_name = '\\\\?\\' + long_name
|
|
buf_size = GetShortPathNameW(long_name, None, 0)
|
|
output = create_unicode_buffer(buf_size)
|
|
GetShortPathNameW(long_name, output, buf_size)
|
|
return output.value[4:] # Remove '\\?\' for SHFileOperationW
|
|
|
|
|
|
def send2trash(path):
|
|
if not isinstance(path, text_type):
|
|
path = text_type(path, 'mbcs')
|
|
if not op.isabs(path):
|
|
path = op.abspath(path)
|
|
path = get_short_path_name(path)
|
|
fileop = SHFILEOPSTRUCTW()
|
|
fileop.hwnd = 0
|
|
fileop.wFunc = FO_DELETE
|
|
# FIX: https://github.com/hsoft/send2trash/issues/17
|
|
# Starting in python 3.6.3 it is no longer possible to use:
|
|
# LPCWSTR(path + '\0') directly as embedded null characters are no longer
|
|
# allowed in strings
|
|
# Workaround
|
|
# - create buffer of c_wchar[] (LPCWSTR is based on this type)
|
|
# - buffer is two c_wchar characters longer (double null terminator)
|
|
# - cast the address of the buffer to a LPCWSTR
|
|
# NOTE: based on how python allocates memory for these types they should
|
|
# always be zero, if this is ever not true we can go back to explicitly
|
|
# setting the last two characters to null using buffer[index] = '\0'.
|
|
buffer = create_unicode_buffer(path, len(path)+2)
|
|
fileop.pFrom = LPCWSTR(addressof(buffer))
|
|
fileop.pTo = None
|
|
fileop.fFlags = FOF_ALLOWUNDO | FOF_NOCONFIRMATION | FOF_NOERRORUI | FOF_SILENT
|
|
fileop.fAnyOperationsAborted = 0
|
|
fileop.hNameMappings = 0
|
|
fileop.lpszProgressTitle = None
|
|
result = SHFileOperationW(byref(fileop))
|
|
if result:
|
|
raise WindowsError(None, None, path, result)
|