From 898060029256ef461338ef2137b01249e66dadd6 Mon Sep 17 00:00:00 2001 From: daid Date: Fri, 11 Mar 2016 15:13:09 +0100 Subject: [PATCH] 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. --- .../RemovableDrivePlugin.py | 2 +- .../WindowsRemovableDrivePlugin.py | 50 ++++++++++++++----- 2 files changed, 39 insertions(+), 13 deletions(-) diff --git a/plugins/RemovableDriveOutputDevice/RemovableDrivePlugin.py b/plugins/RemovableDriveOutputDevice/RemovableDrivePlugin.py index ee651afc38..4e748230e9 100644 --- a/plugins/RemovableDriveOutputDevice/RemovableDrivePlugin.py +++ b/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 diff --git a/plugins/RemovableDriveOutputDevice/WindowsRemovableDrivePlugin.py b/plugins/RemovableDriveOutputDevice/WindowsRemovableDrivePlugin.py index b47a37d35c..569352a8af 100644 --- a/plugins/RemovableDriveOutputDevice/WindowsRemovableDrivePlugin.py +++ b/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