WindowsRemovableDrivePlugin.py 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101
  1. # Copyright (c) 2015 Ultimaker B.V.
  2. # Copyright (c) 2013 David Braam
  3. # Uranium is released under the terms of the AGPLv3 or higher.
  4. from . import RemovableDrivePlugin
  5. import threading
  6. import string
  7. from ctypes import windll
  8. from ctypes import wintypes
  9. import ctypes
  10. import time
  11. import os
  12. import subprocess
  13. from UM.i18n import i18nCatalog
  14. catalog = i18nCatalog("cura")
  15. # WinAPI Constants that we need
  16. # Hardcoded here due to stupid WinDLL stuff that does not give us access to these values.
  17. DRIVE_REMOVABLE = 2
  18. GENERIC_READ = 2147483648
  19. GENERIC_WRITE = 1073741824
  20. FILE_SHARE_READ = 1
  21. FILE_SHARE_WRITE = 2
  22. IOCTL_STORAGE_EJECT_MEDIA = 2967560
  23. OPEN_EXISTING = 3
  24. ## Removable drive support for windows
  25. class WindowsRemovableDrivePlugin(RemovableDrivePlugin.RemovableDrivePlugin):
  26. def checkRemovableDrives(self):
  27. drives = {}
  28. bitmask = windll.kernel32.GetLogicalDrives()
  29. # Check possible drive letters, from A to Z
  30. # Note: using ascii_uppercase because we do not want this to change with locale!
  31. for letter in string.ascii_uppercase:
  32. drive = "{0}:/".format(letter)
  33. # Do we really want to skip A and B?
  34. # GetDriveTypeA explicitly wants a byte array of type ascii. It will accept a string, but this wont work
  35. if bitmask & 1 and windll.kernel32.GetDriveTypeA(drive.encode("ascii")) == DRIVE_REMOVABLE:
  36. volume_name = ""
  37. name_buffer = ctypes.create_unicode_buffer(1024)
  38. filesystem_buffer = ctypes.create_unicode_buffer(1024)
  39. error = windll.kernel32.GetVolumeInformationW(ctypes.c_wchar_p(drive), name_buffer, ctypes.sizeof(name_buffer), None, None, None, filesystem_buffer, ctypes.sizeof(filesystem_buffer))
  40. if error != 0:
  41. volume_name = name_buffer.value
  42. if not volume_name:
  43. volume_name = catalog.i18nc("@item:intext", "Removable Drive")
  44. # Certain readers will report themselves as a volume even when there is no card inserted, but will show an
  45. # "No volume in drive" warning when trying to call GetDiskFreeSpace. However, they will not report a valid
  46. # filesystem, so we can filter on that. In addition, this excludes other things with filesystems Windows
  47. # does not support.
  48. if filesystem_buffer.value == "":
  49. continue
  50. # Check for the free space. Some card readers show up as a drive with 0 space free when there is no card inserted.
  51. freeBytes = ctypes.c_longlong(0)
  52. if windll.kernel32.GetDiskFreeSpaceExA(drive.encode("ascii"), ctypes.byref(freeBytes), None, None) == 0:
  53. continue
  54. if freeBytes.value < 1:
  55. continue
  56. drives[drive] = "{0} ({1}:)".format(volume_name, letter)
  57. bitmask >>= 1
  58. return drives
  59. def performEjectDevice(self, device):
  60. # Magic WinAPI stuff
  61. # First, open a handle to the Device
  62. handle = windll.kernel32.CreateFileA("\\\\.\\{0}".format(device.getId()[:-1]).encode("ascii"), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, None, OPEN_EXISTING, 0, None )
  63. if handle == -1:
  64. print(windll.kernel32.GetLastError())
  65. return
  66. result = None
  67. # Then, try and tell it to eject
  68. try:
  69. if not windll.kernel32.DeviceIoControl(handle, IOCTL_STORAGE_EJECT_MEDIA, None, None, None, None, None, None):
  70. result = False
  71. else:
  72. result = True
  73. except Exception as e:
  74. result = False
  75. # Finally, close the handle
  76. windll.kernel32.CloseHandle(handle)
  77. return result