5 # http://www.hardcoded.net/licenses/bsd_license |
5 # http://www.hardcoded.net/licenses/bsd_license |
6 |
6 |
7 from __future__ import unicode_literals |
7 from __future__ import unicode_literals |
8 |
8 |
9 from ctypes import (windll, Structure, byref, c_uint, |
9 from ctypes import (windll, Structure, byref, c_uint, |
10 create_unicode_buffer, sizeof, addressof) |
10 create_unicode_buffer, addressof) |
11 from ctypes.wintypes import HWND, UINT, LPCWSTR, BOOL |
11 from ctypes.wintypes import HWND, UINT, LPCWSTR, BOOL |
12 import os.path as op |
12 import os.path as op |
13 |
13 |
14 from .compat import text_type |
14 from .compat import text_type |
15 |
15 |
|
16 kernel32 = windll.kernel32 |
|
17 GetShortPathNameW = kernel32.GetShortPathNameW |
|
18 |
16 shell32 = windll.shell32 |
19 shell32 = windll.shell32 |
17 SHFileOperationW = shell32.SHFileOperationW |
20 SHFileOperationW = shell32.SHFileOperationW |
|
21 |
18 |
22 |
19 class SHFILEOPSTRUCTW(Structure): |
23 class SHFILEOPSTRUCTW(Structure): |
20 _fields_ = [ |
24 _fields_ = [ |
21 ("hwnd", HWND), |
25 ("hwnd", HWND), |
22 ("wFunc", UINT), |
26 ("wFunc", UINT), |
26 ("fAnyOperationsAborted", BOOL), |
30 ("fAnyOperationsAborted", BOOL), |
27 ("hNameMappings", c_uint), |
31 ("hNameMappings", c_uint), |
28 ("lpszProgressTitle", LPCWSTR), |
32 ("lpszProgressTitle", LPCWSTR), |
29 ] |
33 ] |
30 |
34 |
|
35 |
31 FO_MOVE = 1 |
36 FO_MOVE = 1 |
32 FO_COPY = 2 |
37 FO_COPY = 2 |
33 FO_DELETE = 3 |
38 FO_DELETE = 3 |
34 FO_RENAME = 4 |
39 FO_RENAME = 4 |
35 |
40 |
37 FOF_SILENT = 4 |
42 FOF_SILENT = 4 |
38 FOF_NOCONFIRMATION = 16 |
43 FOF_NOCONFIRMATION = 16 |
39 FOF_ALLOWUNDO = 64 |
44 FOF_ALLOWUNDO = 64 |
40 FOF_NOERRORUI = 1024 |
45 FOF_NOERRORUI = 1024 |
41 |
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 |
42 def send2trash(path): |
57 def send2trash(path): |
43 if not isinstance(path, text_type): |
58 if not isinstance(path, text_type): |
44 path = text_type(path, 'mbcs') |
59 path = text_type(path, 'mbcs') |
45 if not op.isabs(path): |
60 if not op.isabs(path): |
46 path = op.abspath(path) |
61 path = op.abspath(path) |
|
62 path = get_short_path_name(path) |
47 fileop = SHFILEOPSTRUCTW() |
63 fileop = SHFILEOPSTRUCTW() |
48 fileop.hwnd = 0 |
64 fileop.hwnd = 0 |
49 fileop.wFunc = FO_DELETE |
65 fileop.wFunc = FO_DELETE |
50 # FIX: https://github.com/hsoft/send2trash/issues/17 |
66 # FIX: https://github.com/hsoft/send2trash/issues/17 |
51 # Starting in python 3.6.3 it is no longer possible to use: |
67 # Starting in python 3.6.3 it is no longer possible to use: |
52 # LPCWSTR(path + '\0') directly as embedded null characters are no longer |
68 # LPCWSTR(path + '\0') directly as embedded null characters are no longer |
53 # allowed in strings |
69 # allowed in strings |
54 # Workaround |
70 # Workaround |
55 # - create buffer of c_wchar[] (LPCWSTR is based on this type) |
71 # - create buffer of c_wchar[] (LPCWSTR is based on this type) |
56 # - buffer is two c_wchar characters longer (double null terminator) |
72 # - buffer is two c_wchar characters longer (double null terminator) |
57 # - cast the address of the buffer to a LPCWSTR |
73 # - cast the address of the buffer to a LPCWSTR |
58 # NOTE: based on how python allocates memory for these types they should |
74 # NOTE: based on how python allocates memory for these types they should |
59 # always be zero, if this is ever not true we can go back to explicitly |
75 # always be zero, if this is ever not true we can go back to explicitly |