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

2023-12-29

在 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 会立即重新安装驱动器。

在用户将其卸下并再次放入之前,需要执行哪些操作才能弹出驱动器?


Using CM_Request_Device_EjectW https://learn.microsoft.com/en-us/windows/win32/api/cfgmgr32/nf-cfgmgr32-cm_request_device_ejectwAPI 对我有用。你可以尝试一下。

以下是我测试的完整代码,来自 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, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, NULL, 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, NULL, 0, &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, NULL, NULL, 0, CM_REMOVE_NO_RESTART);  // with messagebox (W2K, Vista) or balloon (XP)

        res = CM_Request_Device_EjectW(DevInstParent, &VetoType, VetoNameW, MAX_PATH, 0);
        //res = CM_Request_Device_EjectW(DevInstParent, NULL, NULL, 0, 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, UINT DriveType, 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, NULL, NULL, 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, NULL, guid, dwIndex, &spdid);
        if (!res) {
            break;
        }

        dwSize = 0;
        SetupDiGetDeviceInterfaceDetail(hDevInfo, &spdid, NULL, 0, &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, &spdid, pspdidd, dwSize, &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, MAX_PATH, 0);
                printf("DeviceId=%s\n", szDeviceIdString);
                */

                // open the disk or cdrom or floppy
                HANDLE hDrive = CreateFile(pspdidd->DevicePath, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
                if (hDrive != INVALID_HANDLE_VALUE) {
                    // get its device number
                    STORAGE_DEVICE_NUMBER sdn;
                    DWORD dwBytesReturned = 0;
                    res = DeviceIoControl(hDrive, IOCTL_STORAGE_GET_DEVICE_NUMBER, NULL, 0, &sdn, sizeof(sdn), &dwBytesReturned, 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;
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何在 Windows 10 上弹出 USB 驱动器(IOCTL_STORAGE_EJECT_MEDIA 不再足够) 的相关文章

  • 具有自定义镶边的 WPF 窗口在右侧和底部有不需要的轮廓

    我使用 Microsoft Windows Shell dll 创建了带有自定义镶边的 WPF 窗口 这是代码
  • Unix 命令“host” - Windows 中有等效命令吗? [关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 我在一台 Windows 7 机器上 按照本文指示我使用 Unix 命令 host https devcenter heroku com
  • 我如何移动粘性/捕捉 wpf 窗口

    当我移动 主 窗口时 我想移动两个或更多粘性窗口 我想做这样的事情 private void MainWindow PreviewMouseMove object sender MouseEventArgs e if e LeftButto
  • 检查Windows电源管理是否关闭了显示器

    如何在 Windows 7 和 XP 中以编程方式检查 Windows 电源管理 是否已关闭显示屏 如果能收到一个活动就更好了 我不认为 XP 可以做到这一点 Windows 7 中有各种与电源管理相关的好东西 这Windows API 代
  • 64 位大型 malloc

    malloc 失败的原因是什么 尤其是在 64 位中 我的具体问题是尝试在 64 位系统上分配一大块 10GB RAM 该机器有 12GB RAM 和 32GB 交换空间 是的 malloc 是极端的 但是为什么它会成为一个问题呢 这是在带
  • 在 C# 中使用反射列出枚举中的值

    我正在尝试使用反射来列出一个 Visual Studio 解决方案内的各个项目中的一些类的公共成员和方法 我尝试访问的所有类都是 C 的 并且它们都是从 C 类访问的 我用来进行这些调用的代码如下 public void PopulateE
  • 使用 Windows 任务计划程序安排 [Virtualenv 相关] Python 脚本

    I want to schedule a python script to start at 3AM and break at 5PM every weekday However the problem arises when I need
  • windows关闭tomcat后保持端口锁定

    我遇到了一个问题 该问题发生在不同站点的 3 台不同服务器上 问题是 当我关闭 Windows 服务器上的 Tomcat 7 和 8 5 版本 服务并尝试重新启动该服务后 该服务将无法启动 因为 tomcat 认为端口仍在使用中 以下是错误
  • 如何保证对象只有一个线程

    我有以下代码 class Service public void start creates thread which creates window and goes to message loop void stop sends WM C
  • Boost + Visual Studio 2010 + Windows 平台 SDK 7.1

    有人可以告诉我 bjam 的命令行开关或其他可以使用新的 Windows Platform SDK 7 1 工具链使用 VS2010 进行 boost 编译的东西吗 您可以在普通的视觉工作室项目中设置该选项 默认值是 v100 是平台 7
  • 使用 BitmapEncoder 生成时如何使 GIF 循环重复

    我能够使用 BitmapEncoder C WinRT 创建动画 gif 但是 我一直无法弄清楚如何让GIF循环回来并从头开始 没有尝试太多 因为我不确定要尝试什么 搜索了更多要在 GIF 上设置的属性 但找不到任何相关内容 好吧 终于能弄
  • Windows 睡眠功能极慢

    我正在通过 Windows h 使用 Sleep 命令制作一个程序 并且在 Windows 10 而不是 Windows 7 上运行我的程序时遇到了令人沮丧的差异 我将我的程序简化为下面的程序 它与我的更复杂的程序表现出相同的行为 在 Wi
  • PostgreSql“运行安装后步骤...数据库集群初始化失败”

    我是一名 Windows 用户 我花了几个小时不断地安装和卸载 然后才使其正常工作 前 10 次左右才看到标题中的错误消息 我将其作为一个自我回答的问题放在这里 以防止其他人在安装时可能遇到同样的问题 并为像我这样第一次使用 Postgre
  • 如何使用 python 在 Windows 中禁用/启用特定 USB 端口? [关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 我想在图形窗口中创建一个切换开关 可以使用 python 禁用 启用 Windows 中的特定 USB 端口 我可以使用哪个外部命令或
  • 如何查明 .exe 是否正在 C++ 中运行?

    给定进程名称 例如 程序 exe C 标准库没有这样的支持 您需要一个操作系统 API 来执行此操作 如果这是 Windows 那么您将使用 CreateToolhelp32Snapshot 然后使用 Process32First 和 Pr
  • 开发者可以在 Windows 应用程序中使用 iCloud 吗?

    开发人员可以使用 Apple 的 iCloud API 在 Mac OS X 和 iOS 上的不同版本的应用程序之间同步应用程序数据 如果开发人员拥有 Windows 版本的应用程序 该版本是否也可以使用 iCloud 将应用程序数据与 M
  • win32上的64位Anaconda使用32位还是64位?

    我猜答案是 32 位 但我有点困惑为什么我什至可以在 win32 中安装 Anaconda 64 我曾经在 Anaconda 64 位上工作 但我刚刚意识到我的系统是 win32 这有时会产生一些异常 例如 请参阅我为 scipy 打开的这
  • 将文件夹中的所有文件及其所有子文件夹移动到一个大文件夹中 - windows xp

    我有一个文件夹 c downloads ffme 里面有很多子文件夹 每个子文件夹中都有不同数量的文件 我想将所有这些单独的文件合并到一个大文件夹中 同时将它们从子文件夹中删除 我希望最终得到一个包含大量文件的文件夹 但没有子文件夹 我怎样
  • 从 Python 下载/安装 Windows 更新

    我正在编写一个脚本来自动安装 Windows 更新 我可以将其部署在多台计算机上 这样我就不必担心手动更新它们 我想用 Python 编写这个 但找不到任何关于如何完成此操作的信息 我需要知道如何搜索更新 下载更新并从 python 脚本安
  • Delphi中使用FindVCLWindow调用WinHelp32(WinXP Pro SP3 32bit)

    有什么问题吗 procedure TForm1 VCLHelpClick Sender TObject var Ctrl TWinControl begin Ctrl FindVCLWindow Mouse CursorPos if Ctr

随机推荐