如何在Windows 10上弹出USB驱动器(IOCTL_STORAGE_EJECT_MEDIA不再足够)

按惯例在Windows上弹出USB驱动器的顺序如下:

CreateFile (drive letter,with read/write rights,file share read and write)
DeviceIoControl(FSCTL_LOCK_VOLUME) 
DeviceIoControl(FSCTL_DISMOUNT_VOLUME) 
DeviceIoControl(IOCTL_STORAGE_MEDIA_REMOVAL) PreventMediaRemoval = FALSE 
DeviceIoControl(IOCTL_STORAGE_EJECT_MEDIA)

在Windows 10中的最新更改(不确定何时)之前,此方法均能正常工作。现在,驱动器仍然可以正确弹出,但是Windows会立即重新安装驱动器。

在用户卸下驱动器并将其重新放入之前,弹出驱动器需要做什么?

xiariweiyang 回答:如何在Windows 10上弹出USB驱动器(IOCTL_STORAGE_EJECT_MEDIA不再足够)

使用CM_Request_Device_EjectW API对我有用。你可以试试看。

以下是我测试过的完整代码,它来自codeproject的“如何准备用于安全移除的USB驱动器”。

(这里的“ F”是我的USB驱动器号。请使用您自己的字母替换它。)

#include <stdio.h>
#include <windows.h>
#include <Setupapi.h>
#include <winioctl.h>
#include <winioctl.h>
#include <cfgmgr32.h>

//-------------------------------------------------
DEVINST GetDrivesDevInstByDeviceNumber(long DeviceNumber,UINT DriveType,char* szDosDeviceName);
//-------------------------------------------------

//-------------------------------------------------
int main()
{
    char DriveLetter = 'F';
    DriveLetter &= ~0x20; // uppercase

    if (DriveLetter < 'A' || DriveLetter > 'Z') {
        return 1;
    }

    char szRootPath[] = "F:\\";   // "X:\"  -> for GetDriveType
    szRootPath[0] = DriveLetter;

    char szDevicePath[] = "F:";   // "X:"   -> for QueryDosDevice
    szDevicePath[0] = DriveLetter;

    char szVolumeAccessPath[] = "\\\\.\\F:";   // "\\.\X:"  -> to open the volume
    szVolumeAccessPath[4] = DriveLetter;

    long DeviceNumber = -1;

    // open the storage volume
    HANDLE hVolume = CreateFile(szVolumeAccessPath,FILE_SHARE_READ | FILE_SHARE_WRITE,NULL,OPEN_EXISTING,NULL);
    if (hVolume == INVALID_HANDLE_VALUE) {
        return 1;
    }

    // get the volume's device number
    STORAGE_DEVICE_NUMBER sdn;
    DWORD dwBytesReturned = 0;
    long res = DeviceIoControl(hVolume,IOCTL_STORAGE_GET_DEVICE_NUMBER,&sdn,sizeof(sdn),&dwBytesReturned,NULL);
    if (res) {
        DeviceNumber = sdn.DeviceNumber;
    }
    CloseHandle(hVolume);

    if (DeviceNumber == -1) {
        return 1;
    }

    // get the drive type which is required to match the device numbers correctely
    UINT DriveType = GetDriveType(szRootPath);

    // get the dos device name (like \device\floppy0) to decide if it's a floppy or not - who knows a better way?
    char szDosDeviceName[MAX_PATH];
    res = QueryDosDevice(szDevicePath,szDosDeviceName,MAX_PATH);
    if (!res) {
        return 1;
    }

    // get the device instance handle of the storage volume by means of a SetupDi enum and matching the device number
    DEVINST DevInst = GetDrivesDevInstByDeviceNumber(DeviceNumber,DriveType,szDosDeviceName);

    if (DevInst == 0) {
        return 1;
    }

    PNP_VETO_TYPE VetoType = PNP_VetoTypeUnknown;
    WCHAR VetoNameW[MAX_PATH];
    VetoNameW[0] = 0;
    bool bSuccess = false;

    // get drives's parent,e.g. the USB bridge,the SATA port,an IDE channel with two drives!
    DEVINST DevInstParent = 0;
    res = CM_Get_Parent(&DevInstParent,DevInst,0);

    for (long tries = 1; tries <= 3; tries++) { // sometimes we need some tries...

        VetoNameW[0] = 0;

        // CM_Query_And_Remove_SubTree doesn't work for restricted users
        //res = CM_Query_And_Remove_SubTreeW(DevInstParent,&VetoType,VetoNameW,MAX_PATH,CM_REMOVE_NO_RESTART); // CM_Query_And_Remove_SubTreeA is not implemented under W2K!
        //res = CM_Query_And_Remove_SubTreeW(DevInstParent,CM_REMOVE_NO_RESTART);  // with messagebox (W2K,Vista) or balloon (XP)

        res = CM_Request_Device_EjectW(DevInstParent,0);
        //res = CM_Request_Device_EjectW(DevInstParent,0); // with messagebox (W2K,Vista) or balloon (XP)

        bSuccess = (res == CR_SUCCESS && VetoType == PNP_VetoTypeUnknown);
        if (bSuccess) {
            break;
        }

        Sleep(500); // required to give the next tries a chance!
    }

    if (bSuccess) {
        printf("Success\n\n");
        return 0;
    }

    printf("failed\n");

    printf("Result=0x%2X\n",res);

    if (VetoNameW[0]) {
        printf("VetoName=%ws)\n\n",VetoNameW);
    }
    return 1;
}
//-----------------------------------------------------------



//----------------------------------------------------------------------
// returns the device instance handle of a storage volume or 0 on error
//----------------------------------------------------------------------
DEVINST GetDrivesDevInstByDeviceNumber(long DeviceNumber,char* szDosDeviceName)
{
    bool IsFloppy = (strstr(szDosDeviceName,"\\Floppy") != NULL); // who knows a better way?

    GUID* guid;

    switch (DriveType) {
    case DRIVE_REMOVABLE:
        if (IsFloppy) {
            guid = (GUID*)&GUID_DEVINTERFACE_FLOPPY;
        }
        else {
            guid = (GUID*)&GUID_DEVINTERFACE_DISK;
        }
        break;
    case DRIVE_FIXED:
        guid = (GUID*)&GUID_DEVINTERFACE_DISK;
        break;
    case DRIVE_CDROM:
        guid = (GUID*)&GUID_DEVINTERFACE_CDROM;
        break;
    default:
        return 0;
    }

    // Get device interface info set handle for all devices attached to system
    HDEVINFO hDevInfo = SetupDiGetClassDevs(guid,DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);

    if (hDevInfo == INVALID_HANDLE_VALUE) {
        return 0;
    }

    // Retrieve a context structure for a device interface of a device information set
    DWORD dwIndex = 0;
    long res;

    BYTE Buf[1024];
    PSP_DEVICE_INTERFACE_DETAIL_DATA pspdidd = (PSP_DEVICE_INTERFACE_DETAIL_DATA)Buf;
    SP_DEVICE_INTERFACE_DATA         spdid;
    SP_DEVINFO_DATA                  spdd;
    DWORD                            dwSize;

    spdid.cbSize = sizeof(spdid);

    while (true) {
        res = SetupDiEnumDeviceInterfaces(hDevInfo,guid,dwIndex,&spdid);
        if (!res) {
            break;
        }

        dwSize = 0;
        SetupDiGetDeviceInterfaceDetail(hDevInfo,&spdid,&dwSize,NULL); // check the buffer size

        if (dwSize != 0 && dwSize <= sizeof(Buf)) {

            pspdidd->cbSize = sizeof(*pspdidd); // 5 Bytes!

            ZeroMemory(&spdd,sizeof(spdd));
            spdd.cbSize = sizeof(spdd);

            long res = SetupDiGetDeviceInterfaceDetail(hDevInfo,pspdidd,dwSize,&spdd);
            if (res) {

                // in case you are interested in the USB serial number:
                // the device id string contains the serial number if the device has one,// otherwise a generated id that contains the '&' char...
                /*
                DEVINST DevInstParent = 0;
                CM_Get_Parent(&DevInstParent,spdd.DevInst,0);
                char szDeviceIdString[MAX_PATH];
                CM_Get_Device_ID(DevInstParent,szDeviceIdString,0);
                printf("DeviceId=%s\n",szDeviceIdString);
                */

                // open the disk or cdrom or floppy
                HANDLE hDrive = CreateFile(pspdidd->DevicePath,NULL);
                if (hDrive != INVALID_HANDLE_VALUE) {
                    // get its device number
                    STORAGE_DEVICE_NUMBER sdn;
                    DWORD dwBytesReturned = 0;
                    res = DeviceIoControl(hDrive,NULL);
                    if (res) {
                        if (DeviceNumber == (long)sdn.DeviceNumber) {  // match the given device number with the one of the current device
                            CloseHandle(hDrive);
                            SetupDiDestroyDeviceInfoList(hDevInfo);
                            return spdd.DevInst;
                        }
                    }
                    CloseHandle(hDrive);
                }
            }
        }
        dwIndex++;
    }

    SetupDiDestroyDeviceInfoList(hDevInfo);

    return 0;
}
本文链接:https://www.f2er.com/3149956.html

大家都在问