python-3.xwinapictypesiso

Mount .iso file with python


I am trying to mount iso file with python on Windows 10. Here is the code:

import ctypes
from ctypes import wintypes

ByteArray8 = wintypes.BYTE * 8

src = r'F:\Backup\ubuntu.iso'


class GUID(ctypes.Structure):
    _fields_ = [
        ("Data1", ctypes.c_long),
        ("Data2", ctypes.c_short),
        ("Data3", ctypes.c_short),
        ("Data4", ByteArray8)
    ]


guid = GUID(0xec984aec, 0xa0f9, 0x47e9, ByteArray8(0x90, 0x1f, 0x71, 0x41, 0x5a, 0x66, 0x34, 0x5b))


class VIRTUAL_STORAGE_TYPE(ctypes.Structure):
    _fields_ = [
        ('DeviceId', ctypes.c_ulong),
        ('VendorId', GUID)
    ]


virtual_storage_type = VIRTUAL_STORAGE_TYPE(1, guid)
handle = wintypes.HANDLE()

Path = ctypes.c_wchar_p(src)

print(
    ctypes.windll.virtdisk.OpenVirtualDisk(
            ctypes.byref(virtual_storage_type),
            Path,
            0x000d0000,
            0x00000000,
            None,
            ctypes.byref(handle)
        )
)

print(
    ctypes.windll.virtdisk.AttachVirtualDisk(
        handle,
        None,
        0x00000001,
        0,
        None,
        None
    )
)

It shows two 0 after run, which means the open and attach operation succeed. But no new driver shown in explorer.

I want to know the reason and how to mount .iso file correctly. Here is reference: https://learn.microsoft.com/en-us/windows/win32/api/virtdisk/nf-virtdisk-openvirtualdisk https://learn.microsoft.com/en-us/windows/win32/api/virtdisk/nf-virtdisk-attachvirtualdisk


Solution

  • 2 problems with the code:

    1. Functional. By default, disk is closed when its corresponding handle is (which happens automatically when the program ends). So, the disk was opened (and shown in Explorer), but only for a very short period of time (after the AttachVirtualDisk call till program end), so you were not able to see it. To be able to use the disk, either:

      • Don't stop the program until you're done using the disk (add an input statement at the very end)

      • Detach the disk lifetime from its handle's one (use ATTACH_VIRTUAL_DISK_FLAG_PERMANENT_LIFETIME flag from [MS.Learn]: ATTACH_VIRTUAL_DISK_FLAG enumeration (virtdisk.h)).
        Needless to say that now, you'll have to detach the disk yourself, by either:

        • Eject it from Explorer

        • Enhancing code to call DetachVirtualDisk

        Also, not sure what are the implications of repeatedly attaching the disk (without detaching it)

    2. Coding - Undefined Behavior generator. Check [SO]: C function called from Python via ctypes returns incorrect value (@CristiFati's answer) for a common pitfall when working with CTypes (calling functions)

    code00.py:

    #!/usr/bin/env python
    
    import ctypes as cts
    import sys
    from ctypes import wintypes as wts
    
    
    class GUID(cts.Structure):
        _fields_ = (
            ("Data1", cts.c_ulong),
            ("Data2", cts.c_ushort),
            ("Data3", cts.c_ushort),
            ("Data4", cts.c_ubyte * 8),
        )
    
    
    class VIRTUAL_STORAGE_TYPE(cts.Structure):
        _fields_ = (
            ("DeviceId", wts.ULONG),
            ("VendorId", GUID),
        )
    
    
    ERROR_SUCCESS = 0
    VIRTUAL_STORAGE_TYPE_DEVICE_ISO = 1
    VIRTUAL_DISK_ACCESS_READ = 0x000D0000
    OPEN_VIRTUAL_DISK_FLAG_NONE = 0x00000000
    ATTACH_VIRTUAL_DISK_FLAG_READ_ONLY = 0x00000001
    ATTACH_VIRTUAL_DISK_FLAG_PERMANENT_LIFETIME = 0x00000004
    VIRTUAL_STORAGE_TYPE_VENDOR_MICROSOFT = GUID(0xEC984AEC, 0xA0F9, 0x47E9, (0x90, 0x1F, 0x71, 0x41, 0x5A, 0x66, 0x34, 0x5B))
    
    
    def main(*argv):
        path = r"l:\Kit\Linux\Ubuntu\pc064\20\ubuntu-20.04.1-desktop-amd64.iso"
    
        virtdisk = cts.WinDLL("VirtDisk")
        OpenVirtualDisk = virtdisk.OpenVirtualDisk
        OpenVirtualDisk.argtypes = (cts.POINTER(VIRTUAL_STORAGE_TYPE), cts.c_wchar_p, cts.c_int, cts.c_int, cts.c_void_p, wts.HANDLE)
        OpenVirtualDisk.restype = wts.DWORD
        AttachVirtualDisk = virtdisk.AttachVirtualDisk
        AttachVirtualDisk.argtypes = (wts.HANDLE, cts.c_void_p, cts.c_int, wts.ULONG, cts.c_void_p, cts.c_void_p)
        AttachVirtualDisk.restype = wts.DWORD
        kernel32 = cts.WinDLL("Kernel32.dll")
        GetLastError = kernel32.GetLastError
        GetLastError.argtypes = ()
        GetLastError.restype = wts.DWORD
        CloseHandle = kernel32.CloseHandle
        CloseHandle.argtypes = (wts.HANDLE,)
        CloseHandle.restype = wts.BOOL
    
        vts = VIRTUAL_STORAGE_TYPE(VIRTUAL_STORAGE_TYPE_DEVICE_ISO, VIRTUAL_STORAGE_TYPE_VENDOR_MICROSOFT)
        handle = wts.HANDLE()
    
        res = OpenVirtualDisk(cts.byref(vts), path, VIRTUAL_DISK_ACCESS_READ, OPEN_VIRTUAL_DISK_FLAG_NONE, None, cts.byref(handle))
        if res != ERROR_SUCCESS:
            print(f"OpenVirtualDisk error: {GetLastError()}")
            return -1
    
        attach_flags = ATTACH_VIRTUAL_DISK_FLAG_READ_ONLY
        permanent = bool(argv)  # Any argument was passed
        if permanent:
            attach_flags |= ATTACH_VIRTUAL_DISK_FLAG_PERMANENT_LIFETIME
    
        res = AttachVirtualDisk(handle, None, attach_flags, 0, None, None)
        if res != ERROR_SUCCESS:
            print(f"AttachVirtualDisk error: {GetLastError()}")
            CloseHandle(handle)
            return -2
    
        input(f"Press <Enter> to continue{'' if permanent else ' (this also closes the ISO drive)'} ...")
        CloseHandle(handle)  # Performed automatically
    
    
    if __name__ == "__main__":
        print(
            "Python {:s} {:03d}bit on {:s}\n".format(
                " ".join(elem.strip() for elem in sys.version.split("\n")),
                64 if sys.maxsize > 0x100000000 else 32,
                sys.platform,
            )
        )
        rc = main(*sys.argv[1:])
        print("\nDone.\n")
        sys.exit(rc)
    

    Output:

    [cfati@CFATI-5510-0:e:\Work\Dev\StackExchange\StackOverflow\q078246936]> sopr.bat
    ### Set shorter prompt to better fit when pasted in StackOverflow (or other) pages ###
    
    [prompt]> "e:\Work\Dev\VEnvs\py_pc064_03.10_test0\Scripts\python.exe" ./code00.py
    Python 3.10.11 (tags/v3.10.11:7d4cc5a, Apr  5 2023, 00:38:17) [MSC v.1929 64 bit (AMD64)] 064bit on win32
    
    Press <Enter> to continue (this also closes the ISO drive) ...
    
    Done.
    
    
    [prompt]>
    [prompt]> "e:\Work\Dev\VEnvs\py_pc064_03.10_test0\Scripts\python.exe" ./code00.py perm
    Python 3.10.11 (tags/v3.10.11:7d4cc5a, Apr  5 2023, 00:38:17) [MSC v.1929 64 bit (AMD64)] 064bit on win32
    
    Press <Enter> to continue ...
    
    Done.
    

    In each of the 2 runs above, the effect is visible in Explorer (according to explanations from the beginning):

    Img00