簡體   English   中英

c#彈出USB-LockVolume,DismountVolume和PrepareRemovalOfVolume的代碼

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

我正在努力創建一個能夠彈出任何USB大容量存儲設備的應用程序。 經過多次嘗試,我終於在一些USB設備上使用了在codeproject https://www.codeproject.com/Articles/375916/How-to-Prepare-a-USB-Drive-for-Safe-Removal BUT中找到的一些代碼出現錯誤“此設備正在使用中。請關閉所有可能正在使用該設備的程序或窗口,然后重試。”。 我真的不明白為什么某些設備會發生這種情況,而其他設備卻沒有...

顯示錯誤窗口,並且基本上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;
    }

閱讀更多答案我從讀卡器C#中找到了一張此彈出存儲卡

在提到方法CM_Request_Device_Eject_NoUi之前,我必須使用從CreateFile返回的hVolume調用“ LockVolume,DismountVolume和PrepareRemovalOfVolume”。

不幸的是,這些功能僅由Microsoft用C ++提供。 如何在窗口中彈出可移動媒體

我想嘗試這種解決方案,但我真的不知道如何使用c#實現這些方法。

有什么可以讓我對正在發生的事情有任何了解嗎? 我嘗試簡單地關閉文件處理程序

CloseHandle(hVolume);

在調用方法CM_Request_Device_Eject_NoUi之前,它創建了另一個SEHException異常(外部組件已引發異常)。

我設法在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);
    }

更新:我捕獲了使用代碼在DeviceIoControl調用中產生的錯誤

int error = Marshal.GetLastWin32Error();

我得到錯誤6,意思是ERROR_INVALID_HANDLE。 :O

現在,我變得更加困惑,因為該處理程序在其余的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