简体   繁体   English

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

[英]c# Eject USB - code for LockVolume, DismountVolume, and PrepareRemovalOfVolume

I am struggling trying to create an application capable to eject any USB mass storage device. 我正在努力创建一个能够弹出任何USB大容量存储设备的应用程序。 After many tries, I am finally using some code found in codeproject https://www.codeproject.com/Articles/375916/How-to-Prepare-a-USB-Drive-for-Safe-Removal BUT with some USB devices I get an error "This device is currently in use. Close any programs or windows that might be using the device, and then try again.". 经过多次尝试,我终于在一些USB设备上使用了在codeproject https://www.codeproject.com/Articles/375916/How-to-Prepare-a-USB-Drive-for-Safe-Removal BUT中找到的一些代码出现错误“此设备正在使用中。请关闭所有可能正在使用该设备的程序或窗口,然后重试。”。 I dont really understand why it happens for some devices and not for others... 我真的不明白为什么某些设备会发生这种情况,而其他设备却没有...

The error window is shown and basically removalDrive() method returns false, USB not ejected. 显示错误窗口,并且基本上removingDrive()方法返回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;
    }

Reading more answers I found this one Eject Memory card from Card Reader C# 阅读更多答案我从读卡器C#中找到了一张此弹出存储卡

Where it is mentioned that before invoking the method CM_Request_Device_Eject_NoUi, I have to call "LockVolume, DismountVolume, and PrepareRemovalOfVolume" using the hVolume returned from CreateFile. 在提到方法CM_Request_Device_Eject_NoUi之前,我必须使用从CreateFile返回的hVolume调用“ LockVolume,DismountVolume和PrepareRemovalOfVolume”。

Unfortunately these functions are only provided by Microsoft in C++. 不幸的是,这些功能仅由Microsoft用C ++提供。 how-to-ejecting-removable-media-in-windows 如何在窗口中弹出可移动媒体

I wanted to try this solution but I dont really know how to implement those methods using c#. 我想尝试这种解决方案,但我真的不知道如何使用c#实现这些方法。

Any could give me any idea about what might be happening? 有什么可以让我对正在发生的事情有任何了解吗? I tried with simply closing the file handler 我尝试简单地关闭文件处理程序

CloseHandle(hVolume);

before calling the method CM_Request_Device_Eject_NoUi but it created other SEHException exception (External component has thrown an exception). 在调用方法CM_Request_Device_Eject_NoUi之前,它创建了另一个SEHException异常(外部组件已引发异常)。

I managed to implemented in C# the 3 methods but the methods LockVolume and DismountVolume always return a false value. 我设法在C#中实现了这3种方法,但是LockVolume和DismountVolume方法始终返回错误值。

    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);
    }

UPDATE: I captured the error produced in the DeviceIoControl call using the code 更新:我捕获了使用代码在DeviceIoControl调用中产生的错误

int error = Marshal.GetLastWin32Error();

and I got error 6, meaning ERROR_INVALID_HANDLE. 我得到错误6,意思是ERROR_INVALID_HANDLE。 :O :O

Now I am even more confused as that handler works fine in the rest of the RemoveDrive method, including the CloseVolume(handler) call. 现在,我变得更加困惑,因为该处理程序在其余的RemoveDrive方法中都可以正常工作,包括CloseVolume(handler)调用。

        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);

        // Lock and dismount the volume.
        bool e = LockVolume(hVolume);
        int error = Marshal.GetLastWin32Error();
        bool f = DismountVolume(hVolume);
        int error2 = Marshal.GetLastWin32Error();

        // Close the volume so other processes can use the drive.
        if (!CloseVolume(hVolume))
            return false;

        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;
    }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM