[python-win32] DeviceIOControl using IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS fails with 'Incorrect function.'

Doug Campbell wdouglascampbell at hotmail.com
Wed Feb 10 16:04:48 EST 2021


Thanks again, Eryk.

I was able to play around today with the code you provided below and was able to tweak it to list the volumes, find the one that matched the disk/partition combination I was searching for and return its offset on the disk.  Very helpful!

________________________________
From: Eryk Sun <eryksun at gmail.com>
Sent: Tuesday, February 9, 2021 11:32 PM
To: python-win32 at python.org <python-win32 at python.org>
Cc: Doug Campbell <wdouglascampbell at hotmail.com>
Subject: Re: [python-win32] DeviceIOControl using IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS fails with 'Incorrect function.'

On 2/9/21, Doug Campbell <wdouglascampbell at hotmail.com> wrote:
>
> Again, you expand my knowledge!  It seems so obvious now after reading what
> you wrote that I would not be able to get volume disk extents for a physical
> partition but yet this is what I wanted to do because I was attempting to
> find out the partition's offset on the disk.

In the case of a basic disk device, it can have multiple partitions
that each correspond to a volume device, \Device\HarddiskVolume<N>. In
this case, each volume device consists of a single extent on a single
disk.

> The only way I can find to accomplish this is to iterate through each volume

You can get the list of volume GUID names of volume devices that are
registered with the mountpoint manager via FindFirstVolumeW /
FindNextVolumeW. Unfortunately, PyWin32 doesn't wrap the volume-find
functions. They can be called using ctypes. For example:

import ctypes
kernel32 = ctypes.WinDLL('kernel32', use_last_error=True)

class HANDLE(ctypes.c_void_p):
    def __eq__(self, other):
        if hasattr(other, 'value'):
            return self.value == other.value
        return False

INVALID_HANDLE_VALUE = HANDLE(-1)
ERROR_NO_MORE_FILES = 18

kernel32.FindFirstVolumeW.restype = HANDLE

def list_volumes():
    volumes = []
    buf = (ctypes.c_wchar * 260)()
    hfind = kernel32.FindFirstVolumeW(buf, len(buf))
    if hfind == INVALID_HANDLE_VALUE:
        raise ctypes.WinError(ctypes.get_last_error())
    try:
        while True:
            # Strip the trailing backslash that FindNextVolumeW appends.
            # The trailing backslash is the mountpoint of the filesystem
            # that mounts the volume device, but we only want the GUID
            # device name.
            volumes.append(buf.value.rstrip('\\'))
            if not kernel32.FindNextVolumeW(hfind, buf, len(buf)):
                error = ctypes.get_last_error()
                if error != ERROR_NO_MORE_FILES:
                    raise ctypes.WinError(error)
                break
    finally:
        kernel32.FindVolumeClose(hfind)
    return volumes

I subclassed c_void_p to create the HANDLE type in order to avoid the
otherwise automatic conversion of the return value to a builtin Python
type. This is a simple way around needing to declare argtypes for the
functions that take the find handle (actually a pointer to memory) as
an argument.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.python.org/pipermail/python-win32/attachments/20210210/1d475b4a/attachment.html>


More information about the python-win32 mailing list