Browse Source

CURA-1104: Fix the incorrect "failed to eject drive" message for windows.

The C windows API call was wrong. The lpBytesReturned parameter was set to
NULL. While the docmentation at:
https://msdn.microsoft.com/en-us/library/windows/desktop/aa363406(v=vs.85).aspx
states that it CANNOT be NULL if lpOverlapped is NULL. Overlapped is for
async operations. So the easiest way to fix this is to supply a proper
pointer.

I've also removed unused includes. And supplied the DeviceIoControl with
proper ctypes calling information to prevent other python->c problems.
daid 9 years ago
parent
commit
8980600292

+ 1 - 1
plugins/RemovableDriveOutputDevice/RemovableDrivePlugin.py

@@ -4,9 +4,9 @@
 import threading
 import time
 
-from UM.Signal import Signal
 from UM.Message import Message
 from UM.OutputDevice.OutputDevicePlugin import OutputDevicePlugin
+from UM.Logger import Logger
 
 from . import RemovableDriveOutputDevice
 from UM.Logger import Logger

+ 38 - 12
plugins/RemovableDriveOutputDevice/WindowsRemovableDrivePlugin.py

@@ -4,16 +4,12 @@
 
 from . import RemovableDrivePlugin
 
-import threading
 import string
 
 from ctypes import windll
 from ctypes import wintypes
 
 import ctypes
-import time
-import os
-import subprocess
 
 from UM.i18n import i18nCatalog
 catalog = i18nCatalog("cura")
@@ -32,6 +28,21 @@ IOCTL_STORAGE_EJECT_MEDIA = 2967560 # [CodeStyle: Windows Enum value]
 
 OPEN_EXISTING = 3 # [CodeStyle: Windows Enum value]
 
+# Setup the DeviceIoControl function arguments and return type.
+# See ctypes documentation for details on how to call C functions from python, and why this is important.
+windll.kernel32.DeviceIoControl.argtypes = [
+    wintypes.HANDLE,                    # _In_          HANDLE hDevice
+    wintypes.DWORD,                     # _In_          DWORD dwIoControlCode
+    wintypes.LPVOID,                    # _In_opt_      LPVOID lpInBuffer
+    wintypes.DWORD,                     # _In_          DWORD nInBufferSize
+    wintypes.LPVOID,                    # _Out_opt_     LPVOID lpOutBuffer
+    wintypes.DWORD,                     # _In_          DWORD nOutBufferSize
+    ctypes.POINTER(wintypes.DWORD),     # _Out_opt_     LPDWORD lpBytesReturned
+    wintypes.LPVOID                     # _Inout_opt_   LPOVERLAPPED lpOverlapped
+]
+windll.kernel32.DeviceIoControl.restype = wintypes.BOOL
+
+
 ## Removable drive support for windows
 class WindowsRemovableDrivePlugin(RemovableDrivePlugin.RemovableDrivePlugin):
     def checkRemovableDrives(self):
@@ -83,16 +94,31 @@ class WindowsRemovableDrivePlugin(RemovableDrivePlugin.RemovableDrivePlugin):
         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 )
 
         if handle == -1:
-            print(windll.kernel32.GetLastError())
-            return
+            # ctypes.WinError sets up an GetLastError API call for windows as an Python OSError exception.
+            # So we use this to raise the error to our caller.
+            raise ctypes.WinError()
+
+        # The DeviceIoControl requires a bytes_returned pointer to be a valid pointer.
+        # So create a ctypes DWORD to reference. (Without this pointer the DeviceIoControl function will crash with an access violation after doing it's job.
+        bytes_returned = wintypes.DWORD(0)
+
+        error = None
 
-        result = None
         # Then, try and tell it to eject
-        if not windll.kernel32.DeviceIoControl(handle, IOCTL_STORAGE_EJECT_MEDIA, None, None, None, None, None, None):
-            result = False
-        else:
-            result = True
+        return_code = windll.kernel32.DeviceIoControl(handle, IOCTL_STORAGE_EJECT_MEDIA, None, 0, None, 0, ctypes.pointer(bytes_returned), None)
+        # DeviceIoControl with IOCTL_STORAGE_EJECT_MEDIA return 0 on error.
+        if return_code == 0:
+            # ctypes.WinError sets up an GetLastError API call for windows as an Python OSError exception.
+            # So we use this to raise the error to our caller.
+            error = ctypes.WinError()
+            # Do not raise an error here yet, so we can properly close the handle.
 
         # Finally, close the handle
         windll.kernel32.CloseHandle(handle)
-        return result
+
+        # If an error happened in the DeviceIoControl, raise it now.
+        if error:
+            raise error
+
+        # Return success
+        return True