|
1 # Copyright 2017 Virgil Dupras |
|
2 |
|
3 # This software is licensed under the "BSD" License as described in the "LICENSE" file, |
|
4 # which should be included with this package. The terms are also available at |
|
5 # http://www.hardcoded.net/licenses/bsd_license |
|
6 |
|
7 from __future__ import unicode_literals |
|
8 |
|
9 from ctypes import (windll, Structure, byref, c_uint, |
|
10 create_unicode_buffer, addressof) |
|
11 from ctypes.wintypes import HWND, UINT, LPCWSTR, BOOL |
|
12 import os.path as op |
|
13 |
|
14 from .compat import text_type |
|
15 |
|
16 kernel32 = windll.kernel32 |
|
17 GetShortPathNameW = kernel32.GetShortPathNameW |
|
18 |
|
19 shell32 = windll.shell32 |
|
20 SHFileOperationW = shell32.SHFileOperationW |
|
21 |
|
22 |
|
23 class SHFILEOPSTRUCTW(Structure): |
|
24 _fields_ = [ |
|
25 ("hwnd", HWND), |
|
26 ("wFunc", UINT), |
|
27 ("pFrom", LPCWSTR), |
|
28 ("pTo", LPCWSTR), |
|
29 ("fFlags", c_uint), |
|
30 ("fAnyOperationsAborted", BOOL), |
|
31 ("hNameMappings", c_uint), |
|
32 ("lpszProgressTitle", LPCWSTR), |
|
33 ] |
|
34 |
|
35 |
|
36 FO_MOVE = 1 |
|
37 FO_COPY = 2 |
|
38 FO_DELETE = 3 |
|
39 FO_RENAME = 4 |
|
40 |
|
41 FOF_MULTIDESTFILES = 1 |
|
42 FOF_SILENT = 4 |
|
43 FOF_NOCONFIRMATION = 16 |
|
44 FOF_ALLOWUNDO = 64 |
|
45 FOF_NOERRORUI = 1024 |
|
46 |
|
47 |
|
48 def get_short_path_name(long_name): |
|
49 if not long_name.startswith('\\\\?\\'): |
|
50 long_name = '\\\\?\\' + long_name |
|
51 buf_size = GetShortPathNameW(long_name, None, 0) |
|
52 output = create_unicode_buffer(buf_size) |
|
53 GetShortPathNameW(long_name, output, buf_size) |
|
54 return output.value[4:] # Remove '\\?\' for SHFileOperationW |
|
55 |
|
56 |
|
57 def send2trash(path): |
|
58 if not isinstance(path, text_type): |
|
59 path = text_type(path, 'mbcs') |
|
60 if not op.isabs(path): |
|
61 path = op.abspath(path) |
|
62 path = get_short_path_name(path) |
|
63 fileop = SHFILEOPSTRUCTW() |
|
64 fileop.hwnd = 0 |
|
65 fileop.wFunc = FO_DELETE |
|
66 # FIX: https://github.com/hsoft/send2trash/issues/17 |
|
67 # Starting in python 3.6.3 it is no longer possible to use: |
|
68 # LPCWSTR(path + '\0') directly as embedded null characters are no longer |
|
69 # allowed in strings |
|
70 # Workaround |
|
71 # - create buffer of c_wchar[] (LPCWSTR is based on this type) |
|
72 # - buffer is two c_wchar characters longer (double null terminator) |
|
73 # - cast the address of the buffer to a LPCWSTR |
|
74 # NOTE: based on how python allocates memory for these types they should |
|
75 # always be zero, if this is ever not true we can go back to explicitly |
|
76 # setting the last two characters to null using buffer[index] = '\0'. |
|
77 buffer = create_unicode_buffer(path, len(path)+2) |
|
78 fileop.pFrom = LPCWSTR(addressof(buffer)) |
|
79 fileop.pTo = None |
|
80 fileop.fFlags = FOF_ALLOWUNDO | FOF_NOCONFIRMATION | FOF_NOERRORUI | FOF_SILENT |
|
81 fileop.fAnyOperationsAborted = 0 |
|
82 fileop.hNameMappings = 0 |
|
83 fileop.lpszProgressTitle = None |
|
84 result = SHFileOperationW(byref(fileop)) |
|
85 if result: |
|
86 raise WindowsError(None, None, path, result) |