Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I check if a disk is in a drive using python?

Tags:

python

windows

Say I want to manipulate some files on a floppy drive or a USB card reader. How do I check to see if the drive in question is ready? (That is, has a disk physically inserted.)

The drive letter exists, so os.exists() will always return True in this case. Also, at this point in the process I don't yet know any file names, so checking to see if a given file exists also won't work.

Some clarification: the issue here is exception handling. Most of the win32 API calls in question just throw an exception when you try to access a drive that isn't ready. Normally, this would work fine - look for something like the free space, and then catch the raised exception and assume that means there isn't a disk present. However, even when I catch any and all exceptions, I still get an angry exception dialog box from Windows telling me the floppy / card reader isn't ready. So, I guess the real question is - how do I suppress the windows error box?

like image 327
Electrons_Ahoy Avatar asked Dec 13 '22 04:12

Electrons_Ahoy


1 Answers

And the answer, as with so many things, turns out to be in an article about C++/Win32 programming from a decade ago.

The issue, in a nutshell, is that Windows handles floppy disk errors slightly differently than other kinds of drive errors. By default, no matter what you program does, or thinks it's doing, Windows will intercept any errors thrown by the device and present a dialog box to the user rather than letting the program handle it - the exact issue I was having.

But, as it turns out, there's a Win32 API call to solve this issue, primarily SetErrorMode()

In a nutshell (and I'm handwaving a way a lot of the details here), we can use SetErrorMode() to get Windows to stop being quite so paranoid, do our thing and let the program handle the situation, and then reset the Windows error mode back to what it was before as if we had never been there. (There's probably a Keyser Soze joke here, but I've had the wrong amount of caffeine today to be able to find it.)

Adapting the C++ sample code from the linked article, that looks about like this:

int OldMode; //a place to store the old error mode
//save the old error mode and set the new mode to let us do the work:
OldMode = SetErrorMode(SEM_FAILCRITICALERRORS); 
// Do whatever we need to do that might cause an error
SetErrorMode(OldMode); //put things back the way they were

Under C++, detecting errors the right way needs the `GetLastError()' function, which we fortunately don't need to worry about here, since this is a Python question. In our case, Python's exception handling works fine. This, then, is the function I knocked together to check a drive letter for "readiness", all ready for copy-pasting if anyone else needs it:

import win32api
def testDrive( currentLetter ):
    """
    Tests a given drive letter to see if the drive is question is ready for 
    access. This is to handle things like floppy drives and USB card readers
    which have to have physical media inserted in order to be accessed.
    Returns true if the drive is ready, false if not.
    """
    returnValue = False
    #This prevents Windows from showing an error to the user, and allows python 
    #to handle the exception on its own.
    oldError = win32api.SetErrorMode( 1 ) #note that SEM_FAILCRITICALERRORS = 1
    try:
        freeSpace = win32file.GetDiskFreeSpaceEx( letter )
    except:
        returnValue = False
    else:
        returnValue = True
    #restore the Windows error handling state to whatever it was before we
    #started messing with it:
    win32api.SetErrorMode( oldError )
    return returnValue

I've been using this quite a bit the last few days, and it's been working beautifully for both floppies and USB card readers.

A few notes: pretty much any function needing disk access will work in the try block - all we're looking for in an exception due to the media not being present.

Also, while the python win32api package exposes all the functions we need, it dones't seem to have any of the flag constants. After a trip to the ancient bowels of MSDN, it turns out that SEM_FAILCRITICALERRORS is equal to 1, which makes our life awfully easy.

I hope this helps someone else with a similar problem!

like image 159
Electrons_Ahoy Avatar answered Dec 24 '22 21:12

Electrons_Ahoy