wastebasket_windows.go 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  1. package wastebasket
  2. import (
  3. "fmt"
  4. "os"
  5. "unsafe"
  6. "golang.org/x/sys/windows"
  7. )
  8. var (
  9. shell32DLL = windows.NewLazyDLL("shell32.dll")
  10. shFileOperationW = shell32DLL.NewProc("SHFileOperationW")
  11. shEmptyRecycleBinW = shell32DLL.NewProc("SHEmptyRecycleBinW")
  12. )
  13. /*
  14. #define FO_DELETE 0x0003
  15. #define FOF_SILENT 0x0004
  16. #define FOF_NOCONFIRMATION 0x0010
  17. #define FOF_ALLOWUNDO 0x0040
  18. #define FOF_NOCONFIRMMKDIR 0x0200
  19. #define FOF_NOERRORUI 0x0400
  20. #define FOF_NO_UI (FOF_SILENT | FOF_NOCONFIRMATION | FOF_NOERRORUI | FOF_NOCONFIRMMKDIR)
  21. typedef struct _SHFILEOPSTRUCTW {
  22. HWND hwnd;
  23. UINT wFunc;
  24. PCZZWSTR pFrom;
  25. PCZZWSTR pTo;
  26. FILEOP_FLAGS fFlags;
  27. BOOL fAnyOperationsAborted;
  28. LPVOID hNameMappings;
  29. PCWSTR lpszProgressTitle;
  30. } SHFILEOPSTRUCTW, *LPSHFILEOPSTRUCTW;
  31. */
  32. const (
  33. FO_DELETE = 0x0003
  34. FOF_SILENT = 0x0004
  35. FOF_NOCONFIRMATION = 0x0010
  36. FOF_ALLOWUNDO = 0x0040
  37. FOF_NOCONFIRMMKDIR = 0x0200
  38. FOF_NOERRORUI = 0x0400
  39. FOF_NO_UI = FOF_SILENT | FOF_NOCONFIRMATION | FOF_NOERRORUI | FOF_NOCONFIRMMKDIR
  40. )
  41. type SHFileOpStructW struct {
  42. // Irrelevant, as its for UI related things
  43. hwnd windows.HWND
  44. wFunc uint32
  45. pFrom *uint16
  46. // This isn't necessary as it is only needed for copy / move actions.
  47. pTo *uint16
  48. fFlags uint16
  49. // FIXME Why isn't this relevant?
  50. fAnyOperationsAborted int32
  51. // Irrelevant, as its for move operations
  52. hNameMappings uintptr
  53. // Irrelevant, as its for UI related things
  54. lpszProgressTitle *uint16
  55. }
  56. // Trash moves a file or folder including its content into the systems trashbin.
  57. func Trash(paths ...string) error {
  58. existingPaths := make([]string, 0, len(paths))
  59. for _, path := range paths {
  60. // The API will return error code "2 - Operation completed successfully"
  61. // when attempting to delete a non-existent file.
  62. if _, err := os.Stat(path); os.IsNotExist(err) {
  63. continue
  64. } else if err != nil {
  65. return err
  66. }
  67. existingPaths = append(existingPaths, path)
  68. }
  69. filesParameter, err := makeDoubleNullTerminatedLpstr(existingPaths...)
  70. if err != nil {
  71. return fmt.Errorf("error creating utf16ptr for passed path: %w", err)
  72. }
  73. ret, _, err := shFileOperationW.Call(uintptr(unsafe.Pointer(&SHFileOpStructW{
  74. hwnd: windows.HWND(0),
  75. wFunc: FO_DELETE,
  76. pFrom: filesParameter,
  77. pTo: nil,
  78. fFlags: FOF_NO_UI | FOF_ALLOWUNDO,
  79. fAnyOperationsAborted: 0,
  80. hNameMappings: 0,
  81. lpszProgressTitle: nil,
  82. })))
  83. if ret != 0 {
  84. return fmt.Errorf("windows error: %w", err)
  85. }
  86. return nil
  87. }
  88. func makeDoubleNullTerminatedLpstr(items ...string) (*uint16, error) {
  89. chars := []uint16{}
  90. for _, s := range items {
  91. converted, err := windows.UTF16FromString(s)
  92. if err != nil {
  93. return nil, err
  94. }
  95. chars = append(chars, converted...)
  96. }
  97. chars = append(chars, 0)
  98. return &chars[0], nil
  99. }
  100. const (
  101. SHERB_NOCONFIRMATION = 1
  102. SHERB_NOPROGRESSUI = 2
  103. SHERB_NOSOUND = 4
  104. )
  105. // Empty clears the platforms trashbin.
  106. func Empty() error {
  107. flags := SHERB_NOCONFIRMATION | SHERB_NOPROGRESSUI | SHERB_NOSOUND
  108. ret, _, err := shEmptyRecycleBinW.Call(uintptr(unsafe.Pointer(nil)), uintptr(unsafe.Pointer(nil)), uintptr(flags))
  109. if ret != 0 {
  110. // Weird edge case, where windows reports that it couldnt load the DLL
  111. // if the trash bin is empty.
  112. if err.(windows.Errno) == 126 {
  113. return nil
  114. }
  115. return fmt.Errorf("windows error: %w", err)
  116. }
  117. return nil
  118. }