USB设备的插入和弹出的监听以及软弹出可移动媒体(如Windows的移除USB设备) .

一、监听USB设备的插入和弹出

当USB设备插入或者弹出时,Windows会产生一条全局消息:WM_DEVICECHANGE

我们需要做的是,获得这条消息的wParam参数,如果为DBT_DEVICEARRIVAL则表示有设备插入并可用,

如果是DBT_DEVICEREMOVECOMPLETE则表示有设备已经移除。再查看lParam参数为DBT_DEVTYP_VOLUME时,

就可以取出DEV_BROADCAST_VOLUME结构的卷号dbcv_unitmask,就知道是哪个卷被插入或者弹出。

代码片段如下:

  1. using System;  
  2. using System.Runtime.InteropServices;  
  3. /// <summary>   
  4. /// 监听设备的插入和拔出   
  5. /// </summary>   
  6. public class DriveDetector  
  7. {  
  8.     /// <summary>   
  9.     /// 设备插入事件   
  10.     /// </summary>   
  11.     public event EventHandler<DriveDectctorEventArgs> DeviceArrived = null;  
  12.     /// <summary>   
  13.     /// 设备拔出事件   
  14.     /// </summary>   
  15.     public event EventHandler<DriveDectctorEventArgs> DeviceRemoved = null;  
  16.     /// <summary>   
  17.     /// 消息处理(HwndSourceHook委托的签名)   
  18.     /// </summary>   
  19.     /// <param name="hwnd"></param>   
  20.     /// <param name="msg"></param>   
  21.     /// <param name="wParam"></param>   
  22.     /// <param name="lParam"></param>   
  23.     /// <param name="handled"></param>   
  24.     /// <returns></returns>   
  25.     public IntPtr WndProc(  
  26.         IntPtr hwnd,  
  27.         int msg,  
  28.         IntPtr wParam,  
  29.         IntPtr lParam,  
  30.         ref bool handled)  
  31.     {  
  32.         if (msg == NativeConstants.WM_DEVICECHANGE)  
  33.         {  
  34. #warning USB设备检测目前只支持32位系统)   
  35.             switch (wParam.ToInt32())  
  36.             {  
  37.                 case NativeConstants.DBT_DEVICEARRIVAL:  
  38.                     {  
  39.                         var devType = Marshal.ReadInt32(lParam, 4);  
  40.                         if (devType == NativeConstants.DBT_DEVTYP_VOLUME)  
  41.                         {  
  42.                             var drive = GetDrive(lParam);  
  43.                             if (DeviceArrived != null)  
  44.                             {  
  45.                                 var args = new DriveDectctorEventArgs(drive);  
  46.                                 DeviceArrived(this, args); //触发设备插入事件   
  47.                             }  
  48.                         }  
  49.                     }  
  50.                     break;  
  51.                 case NativeConstants.DBT_DEVICEREMOVECOMPLETE:  
  52.                     {  
  53.                         var devType = Marshal.ReadInt32(lParam, 4);  
  54.                         if (devType == NativeConstants.DBT_DEVTYP_VOLUME)  
  55.                         {  
  56.                             var drive = GetDrive(lParam);  
  57.                             if (DeviceRemoved != null)  
  58.                             {  
  59.                                 var args = new DriveDectctorEventArgs(drive);  
  60.                                 DeviceRemoved(this, args);  
  61.                             }  
  62.                         }  
  63.                     }  
  64.                     break;  
  65.             }  
  66.         }  
  67.         return IntPtr.Zero;  
  68.     }  
  69.     private static string GetDrive(IntPtr lParam)  
  70.     {  
  71.         var volume = (DEV_BROADCAST_VOLUME)Marshal.PtrToStructure(lParam, typeof(DEV_BROADCAST_VOLUME));  
  72.         var letter = GetLetter(volume.dbcv_unitmask);  
  73.         return string.Format("{0}://", letter);  
  74.     }  
  75.     /// <summary>   
  76.     /// 获得盘符   
  77.     /// </summary>   
  78.     /// <param name="dbcvUnitmask">   
  79.     /// 1 = A   
  80.     /// 2 = B   
  81.     /// 4 = C...   
  82.     /// </param>   
  83.     /// <returns>结果是A~Z的任意一个字符或者为'?'</returns>   
  84.     private static char GetLetter(uint dbcvUnitmask)  
  85.     {  
  86.         const char nona = '?';  
  87.         const string drives = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";  
  88.         if (dbcvUnitmask == 0) return nona;  
  89.         var i = 0;  
  90.         var pom = dbcvUnitmask >> 1;  
  91.         while (pom != 0)  
  92.         {  
  93.             pom = pom >> 1;  
  94.             i++;  
  95.         }  
  96.         if (i < drives.Length)  
  97.             return drives[i];  
  98.         return nona;  
  99.     }  
  100.     /*   
  101.       private static void GetLetterTest() 
  102.       { 
  103.           for (int i = 0; i < 67108864; i++) 
  104.           { 
  105.               Console.WriteLine("{0} - {1}", i, GetLetter((uint)i)); 
  106.               i = i << 1; 
  107.           } 
  108. //0 - ? 
  109. //1 - A 
  110. //3 - B 
  111. //7 - C 
  112. //15 - D 
  113. //31 - E 
  114. //63 - F 
  115. //127 - G 
  116. //255 - H 
  117. //511 - I 
  118. //1023 - J 
  119. //2047 - K 
  120. //4095 - L 
  121. //8191 - M 
  122. //16383 - N 
  123. //32767 - O 
  124. //65535 - P 
  125. //131071 - Q 
  126. //262143 - R 
  127. //524287 - S 
  128. //1048575 - T 
  129. //2097151 - U 
  130. //4194303 - V 
  131. //8388607 - W 
  132. //16777215 - X 
  133. //33554431 - Y 
  134. //67108863 - Z 
  135.       }*/  
  136.     /// <summary>   
  137.     /// 设备插入或拔出事件   
  138.     /// </summary>   
  139.     public class DriveDectctorEventArgs : EventArgs  
  140.     {  
  141.         /// <summary>   
  142.         /// 获得设备卷标   
  143.         /// </summary>   
  144.         public string Drive { get; private set; }  
  145.         public DriveDectctorEventArgs(string drive)  
  146.         {  
  147.             Drive = drive ?? string.Empty;  
  148.         }  
  149.     }  
  150.     #region Win32 API   
  151.     public partial class NativeConstants  
  152.     {  
  153.         /// WM_DEVICECHANGE -> 0x0219   
  154.         public const int WM_DEVICECHANGE = 537;  
  155.         /// BROADCAST_QUERY_DENY -> 0x424D5144   
  156.         //public const int BROADCAST_QUERY_DENY = 1112363332;   
  157.         //public const int DBT_DEVTYP_DEVICEINTERFACE = 5;   
  158.         //public const int DBT_DEVTYP_HANDLE = 6;   
  159.         public const int DBT_DEVICEARRIVAL = 0x8000; // system detected a new device   
  160.         //public const int DBT_DEVICEQUERYREMOVE = 0x8001;   // Preparing to remove (any program can disable the removal)   
  161.         public const int DBT_DEVICEREMOVECOMPLETE = 0x8004; // removed    
  162.         public const int DBT_DEVTYP_VOLUME = 0x00000002; // drive type is logical volume   
  163.     }  
  164.     [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]  
  165.     public struct DEV_BROADCAST_VOLUME  
  166.     {  
  167.         /// DWORD->unsigned int   
  168.         public uint dbcv_size;  
  169.         /// DWORD->unsigned int   
  170.         public uint dbcv_devicetype;  
  171.         /// DWORD->unsigned int   
  172.         public uint dbcv_reserved;  
  173.         /// DWORD->unsigned int   
  174.         public uint dbcv_unitmask;  
  175.         /// WORD->unsigned short   
  176.         public ushort dbcv_flags;  
  177.     }  
  178.     [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]  
  179.     public struct OVERLAPPED  
  180.     {  
  181.         /// ULONG_PTR->unsigned int   
  182.         public uint Internal;  
  183.         /// ULONG_PTR->unsigned int   
  184.         public uint InternalHigh;  
  185.         /// Anonymous_7416d31a_1ce9_4e50_b1e1_0f2ad25c0196   
  186.         public Anonymous_7416d31a_1ce9_4e50_b1e1_0f2ad25c0196 Union1;  
  187.         /// HANDLE->void*   
  188.         public System.IntPtr hEvent;  
  189.     }  
  190.     [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Explicit)]  
  191.     public struct Anonymous_7416d31a_1ce9_4e50_b1e1_0f2ad25c0196  
  192.     {  
  193.         /// Anonymous_ac6e4301_4438_458f_96dd_e86faeeca2a6   
  194.         [System.Runtime.InteropServices.FieldOffsetAttribute(0)]  
  195.         public Anonymous_ac6e4301_4438_458f_96dd_e86faeeca2a6 Struct1;  
  196.         /// PVOID->void*   
  197.         [System.Runtime.InteropServices.FieldOffsetAttribute(0)]  
  198.         public System.IntPtr Pointer;  
  199.     }  
  200.     [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]  
  201.     public struct Anonymous_ac6e4301_4438_458f_96dd_e86faeeca2a6  
  202.     {  
  203.         /// DWORD->unsigned int   
  204.         public uint Offset;  
  205.         /// DWORD->unsigned int   
  206.         public uint OffsetHigh;  
  207.     }  
  208.     [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]  
  209.     public struct SECURITY_ATTRIBUTES  
  210.     {  
  211.         /// DWORD->unsigned int   
  212.         public uint nLength;  
  213.         /// LPVOID->void*   
  214.         public System.IntPtr lpSecurityDescriptor;  
  215.         /// BOOL->int   
  216.         [System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.Bool)]  
  217.         public bool bInheritHandle;  
  218.     }  
  219.     #endregion   
  220. }  

现在,你可以在你的UI线程上创建一个DriveDetector对象,监听DeviceArrived和DeviceRemoved事件。

然后通过该对象WndProc方法,传递UI线程上的消息。WPF程序可以直接用HwndSource对象AddHook,此方法的签名

与HwndSourceHook委托相同。WinForm程序也没关系,override窗口的void WndProc(ref Message m)方法,按

DriveDetector对象的WndProc签名格式,将m数据传入,或者干脆自己写个WinForm版本的。

我的演示代码效果图如下:(注,上述代码未提供显示代码,请自己编写)

另外,关于磁盘容量的监视可以使用FileSystemWatcher对象。

请参考:http://msdn.microsoft.com/zh-cn/library/cc437966.aspx

摘取代码如下:

  1. using System;  
  2. using System.IO;  
  3. using System.Security.Permissions;  
  4. public class Watcher  
  5. {  
  6.     public static void Main()  
  7.     {  
  8.         Run();  
  9.     }  
  10.     [PermissionSet(SecurityAction.Demand, Name="FullTrust")]  
  11.     public static void Run()  
  12.     {  
  13.         string[] args = System.Environment.GetCommandLineArgs();  
  14.         // If a directory is not specified, exit program.   
  15.         if(args.Length != 2)  
  16.         {  
  17.             // Display the proper way to call the program.   
  18.             Console.WriteLine("Usage: Watcher.exe (directory)");  
  19.             return;  
  20.         }  
  21.         // Create a new FileSystemWatcher and set its properties.   
  22.         FileSystemWatcher watcher = new FileSystemWatcher();  
  23.         watcher.Path = args[1];  
  24.         /* Watch for changes in LastAccess and LastWrite times, and 
  25.            the renaming of files or directories. */  
  26.         watcher.NotifyFilter = NotifyFilters.LastAccess | NotifyFilters.LastWrite  
  27.            | NotifyFilters.FileName | NotifyFilters.DirectoryName;  
  28.         // Only watch text files.   
  29.         //watcher.Filter = "*.txt";   
  30.         watcher.IncludeSubdirectories=true;  
  31.         // Add event handlers.   
  32.         watcher.Changed += new FileSystemEventHandler(OnChanged);  
  33.         watcher.Created += new FileSystemEventHandler(OnChanged);  
  34.         watcher.Deleted += new FileSystemEventHandler(OnChanged);  
  35.         watcher.Renamed += new RenamedEventHandler(OnRenamed);  
  36.         // Begin watching.   
  37.         watcher.EnableRaisingEvents = true;  
  38.         // Wait for the user to quit the program.   
  39.         Console.WriteLine("Press /'q/' to quit the sample.");  
  40.         while(Console.Read()!='q');  
  41.     }  
  42.     // Define the event handlers.   
  43.     private static void OnChanged(object source, FileSystemEventArgs e)  
  44.     {  
  45.         // Specify what is done when a file is changed, created, or deleted.   
  46.        Console.WriteLine("File: " +  e.FullPath + " " + e.ChangeType);  
  47.     }  
  48.     private static void OnRenamed(object source, RenamedEventArgs e)  
  49.     {  
  50.         // Specify what is done when a file is renamed.   
  51.         Console.WriteLine("File: {0} renamed to {1}", e.OldFullPath, e.FullPath);  
  52.     }  
  53. }  

二、软件弹出可移动媒体

网上有一部分关于这方法的代码,我的代码是从MSDN获取的VC++版本,需要调用多个API函数,转换为C#代码如下:

参考:http://support.microsoft.com/kb/165721

  1. using System;  
  2. using System.Runtime.InteropServices;  
  3. using System.Threading;  
  4. /// <summary>   
  5. /// 弹出可移动媒体   
  6. /// </summary>   
  7. /// <see cref="http://support.microsoft.com/kb/165721"/>   
  8. public static class Eject  
  9. {  
  10.     private static void ReportError(string szMsg)  
  11.     {  
  12.         const string szErrorFormat = "Error {0}: {1}";  
  13.         var error = string.Format(szErrorFormat, GetLastError(), szMsg);  
  14.         Console.Error.WriteLine(error);  
  15.     }  
  16.     private static IntPtr OpenVolume(char driveLetter)  
  17.     {  
  18.         const string volumeFormat = "////.//{0}:";  
  19.         const string rootFormat = "{0}://";  
  20.         int accessFlags;  
  21.         var rootName = string.Format(rootFormat, driveLetter);  
  22.         var driveType = GetDriveTypeW(rootName);  
  23.         switch (driveType)  
  24.         {  
  25.             case DRIVE_REMOVABLE:  
  26.                 accessFlags = GENERIC_READ | GENERIC_WRITE;  
  27.                 break;  
  28.             case DRIVE_CDROM:  
  29.                 accessFlags = GENERIC_READ;  
  30.                 break;  
  31.             default:  
  32.                 Console.Error.WriteLine("Cannot eject. Drive type is incorrect.");  
  33.                 return new IntPtr(INVALID_HANDLE_VALUE);  
  34.         }  
  35.         var volumeName = string.Format(volumeFormat, driveLetter);  
  36.         var hVolume = CreateFileW(  
  37.             volumeName,  
  38.             accessFlags,  
  39.             FILE_SHARE_READ | FILE_SHARE_WRITE,  
  40.             IntPtr.Zero,  
  41.             OPEN_EXISTING,  
  42.             0,  
  43.             IntPtr.Zero);  
  44.         if (hVolume == new IntPtr(INVALID_HANDLE_VALUE))  
  45.             ReportError("CreateFile");  
  46.         return hVolume;  
  47.     }  
  48.     private static bool CloseVolume(IntPtr hVolume)  
  49.     {  
  50.         return CloseHandle(hVolume);  
  51.     }  
  52.     private static bool LockVolume(IntPtr hVolume)  
  53.     {  
  54.         const int LOCK_TIMEOUT = 10000; //10 Seconds   
  55.         const int LOCK_RETRIES = 20;  
  56.         var sleepAmount = LOCK_TIMEOUT / LOCK_RETRIES;  
  57.         //Do this in a loop until a timeout period has expired   
  58.         for (int tryCount = 0; tryCount < LOCK_RETRIES; tryCount++)  
  59.         {  
  60.             int dwBytesReturned;  
  61.             if (DeviceIoControl(  
  62.                 hVolume,  
  63.                 FSCTL_LOCK_VOLUME,  
  64.                 IntPtr.Zero, 0,  
  65.                 IntPtr.Zero, 0,  
  66.                 out dwBytesReturned,  
  67.                 IntPtr.Zero))  
  68.                 return true; //return   
  69.             Thread.Sleep(sleepAmount);  
  70.         }  
  71.         return false;  
  72.     }  
  73.     private static bool DismountVolume(IntPtr hVolume)  
  74.     {  
  75.         int dwBytesReturned;  
  76.         return DeviceIoControl(  
  77.             hVolume,  
  78.             FSCTL_DISMOUNT_VOLUME,  
  79.             IntPtr.Zero, 0,  
  80.             IntPtr.Zero, 0,  
  81.             out dwBytesReturned,  
  82.             IntPtr.Zero);  
  83.     }  
  84.     private static bool PresentRemovalOfVolume(IntPtr hVolume,bool preventRemoval)  
  85.     {  
  86.         PREVENT_MEDIA_REMOVAL pmrBuffer;  
  87.         pmrBuffer.PreventMediaRemoval = preventRemoval;  
  88.           
  89.         var size = Marshal.SizeOf(pmrBuffer);  
  90.         IntPtr ptr = Marshal.AllocHGlobal(size);  
  91.         try  
  92.         {  
  93.             Marshal.StructureToPtr(pmrBuffer, ptr, false);  
  94.             int dwBytesReturned;  
  95.             return DeviceIoControl(  
  96.                 hVolume,  
  97.                 IOCTL_STORAGE_MEDIA_REMOVAL,  
  98.                 ptr, (uint) size,  
  99.                 IntPtr.Zero, 0,  
  100.                 out dwBytesReturned,  
  101.                 IntPtr.Zero  
  102.                 );  
  103.         }  
  104.         finally  
  105.         {  
  106.             Marshal.DestroyStructure(ptr, pmrBuffer.GetType());  
  107.         }  
  108.     }  
  109.     private static bool AutoEjectVolume(IntPtr hVolume)  
  110.     {  
  111.         int dwBytesReturned;  
  112.         return DeviceIoControl(  
  113.             hVolume,  
  114.             IOCTL_STORAGE_EJECT_MEDIA,  
  115.             IntPtr.Zero, 0,  
  116.             IntPtr.Zero, 0,  
  117.             out dwBytesReturned,  
  118.             IntPtr.Zero);  
  119.     }  
  120.     private static bool RemoveVolumeDefinition(string deviceName)  
  121.     {  
  122.         return DefineDosDeviceW(DDD_REMOVE_DEFINITION, deviceName, null);  
  123.     }  
  124.       
  125.     public static bool EjectVolume(char driveLetter, bool removeVolumeDefinition)  
  126.     {  
  127.         var removeSafely = false;  
  128.         var autoEject = false;  
  129.         //Open the volume.   
  130.         var hVolume = OpenVolume(driveLetter);  
  131.         if (hVolume == new IntPtr(INVALID_HANDLE_VALUE))  
  132.             return false;  
  133.         //Lock and dismount the volume.   
  134.         if(LockVolume(hVolume)&&DismountVolume(hVolume))  
  135.         {  
  136.             removeSafely = true;  
  137.             //Set prevent removal to false and eject the volume.   
  138.             if (PresentRemovalOfVolume(hVolume, false) && AutoEjectVolume(hVolume))  
  139.                 autoEject = true;  
  140.         }  
  141.         //Close the volume so other processes can use the drive.   
  142.         if (!CloseVolume(hVolume))  
  143.             return false;  
  144.         if(autoEject)  
  145.         {  
  146.             Console.Out.WriteLine("Media in Drive {0} has been ejected safely.",driveLetter);  
  147.         }  
  148.         else  
  149.         {  
  150.             if (removeSafely)  
  151.             {  
  152.                 Console.Out.WriteLine("Media in Drive {0} can be safely removed.", driveLetter);  
  153.             }  
  154.             else  
  155.             {  
  156.                 Console.Error.WriteLine("Media in Drive {0} is working, and can't be safely removed.", driveLetter);  
  157.                 return false;  
  158.             }  
  159.         }  
  160.         if(removeVolumeDefinition) RemoveVolumeDefinition(string.Format("{0}:", driveLetter));  
  161.         return true;  
  162.     }  
  163.     public static void Usage()  
  164.     {  
  165.         Console.Out.WriteLine("Usage: Eject <drive letter>");  
  166.         Console.Out.WriteLine();  
  167.     }  
  168.     static void Main(string[] args)  
  169.     {  
  170.         if(args.Length != 1)  
  171.         {  
  172.             Usage();  
  173.             return;  
  174.         }  
  175.           
  176.         if(!EjectVolume(args[0][0], true))  
  177.         {  
  178.             Console.Error.WriteLine("Failure ejecting drive {0}.",args[0][0]);  
  179.         }  
  180.     }  
  181.     #region WIN32 API   
  182.     /// Return Type: DWORD->unsigned int   
  183.     [System.Runtime.InteropServices.DllImportAttribute("kernel32.dll", EntryPoint = "GetLastError")]  
  184.     public static extern uint GetLastError();  
  185.     /// Return Type: UINT->unsigned int   
  186.     ///lpRootPathName: LPCWSTR->WCHAR*   
  187.     [System.Runtime.InteropServices.DllImportAttribute("kernel32.dll", EntryPoint = "GetDriveTypeW")]  
  188.     public static extern uint GetDriveTypeW([System.Runtime.InteropServices.InAttribute()] [System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.LPWStr)] string lpRootPathName);  
  189.     /// Return Type: HANDLE->void*   
  190.     ///lpFileName: LPCWSTR->WCHAR*   
  191.     ///dwDesiredAccess: DWORD->unsigned int   
  192.     ///dwShareMode: DWORD->unsigned int   
  193.     ///lpSecurityAttributes: LPSECURITY_ATTRIBUTES->_SECURITY_ATTRIBUTES*   
  194.     ///dwCreationDisposition: DWORD->unsigned int   
  195.     ///dwFlagsAndAttributes: DWORD->unsigned int   
  196.     ///hTemplateFile: HANDLE->void*   
  197.     [System.Runtime.InteropServices.DllImportAttribute("kernel32.dll", EntryPoint = "CreateFileW")]  
  198.     public static extern System.IntPtr CreateFileW([System.Runtime.InteropServices.InAttribute()] [System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.LPWStr)] string lpFileName, int dwDesiredAccess, uint dwShareMode, [System.Runtime.InteropServices.InAttribute()] System.IntPtr lpSecurityAttributes, uint dwCreationDisposition, uint dwFlagsAndAttributes, [System.Runtime.InteropServices.InAttribute()] System.IntPtr hTemplateFile);  
  199.     /// Return Type: BOOL->int   
  200.     ///hObject: HANDLE->void*   
  201.     [System.Runtime.InteropServices.DllImportAttribute("kernel32.dll", EntryPoint = "CloseHandle")]  
  202.     [return: System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.Bool)]  
  203.     public static extern bool CloseHandle([System.Runtime.InteropServices.InAttribute()] System.IntPtr hObject);  
  204.     /// Return Type: BOOL->int   
  205.     ///hDevice: HANDLE->void*   
  206.     ///dwIoControlCode: DWORD->unsigned int   
  207.     ///lpInBuffer: LPVOID->void*   
  208.     ///nInBufferSize: DWORD->unsigned int   
  209.     ///lpOutBuffer: LPVOID->void*   
  210.     ///nOutBufferSize: DWORD->unsigned int   
  211.     ///lpBytesReturned: LPDWORD->DWORD*   
  212.     ///lpOverlapped: LPOVERLAPPED->_OVERLAPPED*   
  213.     [System.Runtime.InteropServices.DllImportAttribute("kernel32.dll", EntryPoint = "DeviceIoControl")]  
  214.     [return: System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.Bool)]  
  215.     public static extern bool DeviceIoControl([System.Runtime.InteropServices.InAttribute()] System.IntPtr hDevice, uint dwIoControlCode, [System.Runtime.InteropServices.InAttribute()] System.IntPtr lpInBuffer, uint nInBufferSize, System.IntPtr lpOutBuffer, uint nOutBufferSize, out int lpBytesReturned, System.IntPtr lpOverlapped);  
  216.   
  217.     /// DRIVE_REMOVABLE -> 2   
  218.     public const int DRIVE_REMOVABLE = 2;  
  219.     /// DRIVE_CDROM -> 5   
  220.     public const int DRIVE_CDROM = 5;  
  221.     /// INVALID_HANDLE_VALUE -> -1   
  222.     public const int INVALID_HANDLE_VALUE = -1;  
  223.     /// GENERIC_READ -> (0x80000000L)   
  224.     public const int GENERIC_READ = -2147483648;  
  225.     /// GENERIC_WRITE -> (0x40000000L)   
  226.     public const int GENERIC_WRITE = 1073741824;  
  227.     /// FILE_SHARE_READ -> 0x00000001   
  228.     public const int FILE_SHARE_READ = 1;  
  229.     /// FILE_SHARE_WRITE -> 0x00000002   
  230.     public const int FILE_SHARE_WRITE = 2;  
  231.     /// OPEN_EXISTING -> 3   
  232.     public const int OPEN_EXISTING = 3;  
  233.     //WinIoCtl.h   
  234.     //   
  235.     //#define CTL_CODE( DeviceType, Function, Method, Access ) (                 /   
  236.     //  ((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method) /   
  237.     //)   
  238.     private const int FILE_DEVICE_FILE_SYSTEM = 0x00000009;  
  239.     private const int METHOD_BUFFERED = 0;  
  240.     private const int FILE_ANY_ACCESS = 0;  
  241.     //#define FSCTL_LOCK_VOLUME               CTL_CODE(FILE_DEVICE_FILE_SYSTEM,  6, METHOD_BUFFERED, FILE_ANY_ACCESS)   
  242.     public const int FSCTL_LOCK_VOLUME = ((FILE_DEVICE_FILE_SYSTEM) << 16) | ((FILE_ANY_ACCESS) << 14) | ((6) << 2) | (METHOD_BUFFERED);  
  243.     public const int FSCTL_UNLOCK_VOLUME = ((FILE_DEVICE_FILE_SYSTEM) << 16) | ((FILE_ANY_ACCESS) << 14) | ((7) << 2) | (METHOD_BUFFERED);  
  244.     public const int FSCTL_DISMOUNT_VOLUME = ((FILE_DEVICE_FILE_SYSTEM) << 16) | ((FILE_ANY_ACCESS) << 14) | ((8) << 2) | (METHOD_BUFFERED);  
  245.     //#define IOCTL_STORAGE_BASE FILE_DEVICE_MASS_STORAGE   
  246.     private const int FILE_DEVICE_MASS_STORAGE = 0x0000002d;  
  247.     private const int IOCTL_STORAGE_BASE = FILE_DEVICE_MASS_STORAGE;  
  248.     //#define FILE_READ_ACCESS          ( 0x0001 )    // file & pipe   
  249.     private const int FILE_READ_ACCESS = 0x0001;  
  250.       
  251.     //#define IOCTL_STORAGE_MEDIA_REMOVAL           CTL_CODE(IOCTL_STORAGE_BASE, 0x0201, METHOD_BUFFERED, FILE_READ_ACCESS)   
  252.     public const int IOCTL_STORAGE_MEDIA_REMOVAL =  
  253.         ((IOCTL_STORAGE_BASE) << 16) | ((FILE_READ_ACCESS) << 14) | ((0x0201) << 2) | (METHOD_BUFFERED);  
  254.     //#define IOCTL_STORAGE_EJECT_MEDIA             CTL_CODE(IOCTL_STORAGE_BASE, 0x0202, METHOD_BUFFERED, FILE_READ_ACCESS)   
  255.     public const int IOCTL_STORAGE_EJECT_MEDIA =  
  256.         ((IOCTL_STORAGE_BASE) << 16) | ((FILE_READ_ACCESS) << 14) | ((0x0202) << 2) | (METHOD_BUFFERED);  
  257.   
  258.     [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]  
  259.     public struct PREVENT_MEDIA_REMOVAL  
  260.     {  
  261.         /// BOOLEAN->BYTE->unsigned char   
  262.         [MarshalAs(UnmanagedType.I1)]  
  263.         public bool PreventMediaRemoval;  
  264.     }  
  265.      
  266.     #region Remove Volume Definition   
  267.     /// DDD_REMOVE_DEFINITION -> 0x00000002   
  268.     public const int DDD_REMOVE_DEFINITION = 2;  
  269.           
  270.     /// Return Type: BOOL->int   
  271.     ///dwFlags: DWORD->unsigned int   
  272.     ///lpDeviceName: LPCWSTR->WCHAR*   
  273.     ///lpTargetPath: LPCWSTR->WCHAR*   
  274.     [System.Runtime.InteropServices.DllImportAttribute("kernel32.dll", EntryPoint="DefineDosDeviceW")]  
  275.     [return: System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.Bool)]  
  276.     public static extern  bool DefineDosDeviceW(uint dwFlags, [System.Runtime.InteropServices.InAttribute()] [System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.LPWStr)] string lpDeviceName, [System.Runtime.InteropServices.InAttribute()] [System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.LPWStr)] string lpTargetPath) ;  
  277.     #endregion  
  278.      
  279.     #endregion   
  280. }  

预览:(注,弹出可移动磁盘,并不会删除驱动器号,但设备已经被弹出,如图中的h盘)

注:已改进,安全弹出后,通过DDD_REMOVE_DEFINITION移除h盘。

原文地址:https://www.cnblogs.com/soundcode/p/9376864.html