c#弹出USB - LockVolume,DismountVolume和PrepareRemovalOfVolume的代码

时间:2018-02-14 17:50:40

标签: c# file usb

我正在努力创建一个能够弹出任何USB大容量存储设备的应用程序。 经过多次尝试,我终于使用了codeproject中的一些代码 https://www.codeproject.com/Articles/375916/How-to-Prepare-a-USB-Drive-for-Safe-Removal 但是对于某些USB设备,我收到错误“此设备当前正在使用中。关闭可能正在使用该设备的所有程序或窗口,然后再试一次。”我真的不明白为什么它会发生在一些设备而不是其他设备......

显示错误窗口,基本上removeDrive()方法返回false,USB不弹出。

        public static bool RemoveDrive( string driveCharWithColon )
    {
        // open the storage volume
        IntPtr hVolume = CreateFile( @"\\.\" + driveCharWithColon, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, IntPtr.Zero, OPEN_EXISTING, 0, IntPtr.Zero );
        if ( hVolume.ToInt32( ) == -1 ) return false;

        // get the volume's device number
        long DeviceNumber = GetDeviceNumber( hVolume );
        if ( DeviceNumber == -1 ) return false;

        // get the drive type which is required to match the device numbers correctely
        string rootPath = driveCharWithColon + "\\";        
        DriveType driveType = GetDriveType( rootPath );

        // get the dos device name (like \device\floppy0) to decide if it's a floppy or not - who knows a better way?
        StringBuilder pathInformation = new StringBuilder( 250 );
        uint res = QueryDosDevice( driveCharWithColon, pathInformation, 250 );
        if ( res == 0 ) return false;

        // get the device instance handle of the storage volume by means of a SetupDi enum and matching the device number
        long DevInst = GetDrivesDevInstByDeviceNumber( DeviceNumber, driveType, pathInformation.ToString( ) );
        if ( DevInst == 0 ) return false;

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

        for ( int tries=1; tries <= 3; tries++ )  // sometimes we need some tries...
        {
            int r = CM_Request_Device_Eject_NoUi( DevInstParent, IntPtr.Zero, null, 0, 0 );
            if ( r == 0 ) return true;
            Thread.Sleep( 500 );
        }
        return false;
    }

阅读更多答案我发现了这个 Eject Memory card from Card Reader C#

如果提到在调用方法CM_Request_Device_Eject_NoUi之前,我必须使用从CreateFile返回的hVolume调用“LockVolume,DismountVolume和PrepareRemovalOfVolume”。

不幸的是,这些功能仅由Microsoft在C ++中提供。 how-to-ejecting-removable-media-in-windows

我想尝试这个解决方案,但我真的不知道如何使用c#实现这些方法。

任何人都可以告诉我可能发生的事情吗? 我试着简单地关闭文件处理程序

CloseHandle(hVolume);

在调用CM_Request_Device_Eject_NoUi方法之前,但它创建了其他SEHException异常(外部组件抛出异常)。

1 个答案:

答案 0 :(得分:0)

我设法在C#中实现了3种方法,但LockVolume和DismountVolume方法总是返回false值。

eval(x.value)

更新: 我使用代码

捕获了DeviceIoControl调用中产生的错误
    const int LOCK_TIMEOUT = 10000;       // 10 Seconds
    const int LOCK_RETRIES = 20; 
    public static bool LockVolume(IntPtr hVolume)
    {
        int dwBytesReturned;
        int dwSleepAmount;
        int nTryCount;
        dwSleepAmount = LOCK_TIMEOUT / LOCK_RETRIES;
        // Do this in a loop until a timeout period has expired
        for (nTryCount = 0; nTryCount < LOCK_RETRIES; nTryCount++)
        {
            if (DeviceIoControl(hVolume,
                                FSCTL_LOCK_VOLUME,
                                IntPtr.Zero, 0,
                                IntPtr.Zero, 0,
                                out dwBytesReturned,
                                IntPtr.Zero))
                return true;
            Thread.Sleep(dwSleepAmount);
        }
        return false;
    }

    public static bool PreventRemovalOfVolume(IntPtr hVolume, bool fPreventRemoval)
    {
        int retVal;
        IntPtr buffer = new IntPtr((fPreventRemoval) ? 1 : 0);
        return DeviceIoControl(hVolume, IOCTL_STORAGE_MEDIA_REMOVAL, buffer, 1, IntPtr.Zero, 0, out retVal, IntPtr.Zero);
    }
    public static bool DismountVolume(IntPtr hVolume)
    {
        int dwBytesReturned;

        return DeviceIoControl(hVolume,
                                FSCTL_DISMOUNT_VOLUME,
                                IntPtr.Zero, 0,
                                IntPtr.Zero, 0,
                                out dwBytesReturned,
                                IntPtr.Zero);
    }

    public static bool  AutoEjectVolume(IntPtr hVolume)
    {
        int  dwBytesReturned;

        return DeviceIoControl(hVolume,
                                IOCTL_STORAGE_EJECT_MEDIA,
                                IntPtr.Zero, 0,
                                IntPtr.Zero, 0,
                                out dwBytesReturned,
                                IntPtr.Zero);
    }

    public static bool CloseVolume(IntPtr hVolume)
    {
        return CloseHandle(hVolume);
    }

我收到错误6,意思是ERROR_INVALID_HANDLE。 :o

现在我更加困惑,因为该处理程序在RemoveDrive方法的其余部分中运行良好,包括CloseVolume(处理程序)调用。

int error = Marshal.GetLastWin32Error();