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

时间:2023-03-09 01:44:47
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版本的。

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

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

另外,关于磁盘容量的监视可以使用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. var size = Marshal.SizeOf(pmrBuffer);
  89. IntPtr ptr = Marshal.AllocHGlobal(size);
  90. try
  91. {
  92. Marshal.StructureToPtr(pmrBuffer, ptr, false);
  93. int dwBytesReturned;
  94. return DeviceIoControl(
  95. hVolume,
  96. IOCTL_STORAGE_MEDIA_REMOVAL,
  97. ptr, (uint) size,
  98. IntPtr.Zero, 0,
  99. out dwBytesReturned,
  100. IntPtr.Zero
  101. );
  102. }
  103. finally
  104. {
  105. Marshal.DestroyStructure(ptr, pmrBuffer.GetType());
  106. }
  107. }
  108. private static bool AutoEjectVolume(IntPtr hVolume)
  109. {
  110. int dwBytesReturned;
  111. return DeviceIoControl(
  112. hVolume,
  113. IOCTL_STORAGE_EJECT_MEDIA,
  114. IntPtr.Zero, 0,
  115. IntPtr.Zero, 0,
  116. out dwBytesReturned,
  117. IntPtr.Zero);
  118. }
  119. private static bool RemoveVolumeDefinition(string deviceName)
  120. {
  121. return DefineDosDeviceW(DDD_REMOVE_DEFINITION, deviceName, null);
  122. }
  123. public static bool EjectVolume(char driveLetter, bool removeVolumeDefinition)
  124. {
  125. var removeSafely = false;
  126. var autoEject = false;
  127. //Open the volume.
  128. var hVolume = OpenVolume(driveLetter);
  129. if (hVolume == new IntPtr(INVALID_HANDLE_VALUE))
  130. return false;
  131. //Lock and dismount the volume.
  132. if(LockVolume(hVolume)&&DismountVolume(hVolume))
  133. {
  134. removeSafely = true;
  135. //Set prevent removal to false and eject the volume.
  136. if (PresentRemovalOfVolume(hVolume, false) && AutoEjectVolume(hVolume))
  137. autoEject = true;
  138. }
  139. //Close the volume so other processes can use the drive.
  140. if (!CloseVolume(hVolume))
  141. return false;
  142. if(autoEject)
  143. {
  144. Console.Out.WriteLine("Media in Drive {0} has been ejected safely.",driveLetter);
  145. }
  146. else
  147. {
  148. if (removeSafely)
  149. {
  150. Console.Out.WriteLine("Media in Drive {0} can be safely removed.", driveLetter);
  151. }
  152. else
  153. {
  154. Console.Error.WriteLine("Media in Drive {0} is working, and can't be safely removed.", driveLetter);
  155. return false;
  156. }
  157. }
  158. if(removeVolumeDefinition) RemoveVolumeDefinition(string.Format("{0}:", driveLetter));
  159. return true;
  160. }
  161. public static void Usage()
  162. {
  163. Console.Out.WriteLine("Usage: Eject <drive letter>");
  164. Console.Out.WriteLine();
  165. }
  166. static void Main(string[] args)
  167. {
  168. if(args.Length != 1)
  169. {
  170. Usage();
  171. return;
  172. }
  173. if(!EjectVolume(args[0][0], true))
  174. {
  175. Console.Error.WriteLine("Failure ejecting drive {0}.",args[0][0]);
  176. }
  177. }
  178. #region WIN32 API
  179. /// Return Type: DWORD->unsigned int
  180. [System.Runtime.InteropServices.DllImportAttribute("kernel32.dll", EntryPoint = "GetLastError")]
  181. public static extern uint GetLastError();
  182. /// Return Type: UINT->unsigned int
  183. ///lpRootPathName: LPCWSTR->WCHAR*
  184. [System.Runtime.InteropServices.DllImportAttribute("kernel32.dll", EntryPoint = "GetDriveTypeW")]
  185. public static extern uint GetDriveTypeW([System.Runtime.InteropServices.InAttribute()] [System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.LPWStr)] string lpRootPathName);
  186. /// Return Type: HANDLE->void*
  187. ///lpFileName: LPCWSTR->WCHAR*
  188. ///dwDesiredAccess: DWORD->unsigned int
  189. ///dwShareMode: DWORD->unsigned int
  190. ///lpSecurityAttributes: LPSECURITY_ATTRIBUTES->_SECURITY_ATTRIBUTES*
  191. ///dwCreationDisposition: DWORD->unsigned int
  192. ///dwFlagsAndAttributes: DWORD->unsigned int
  193. ///hTemplateFile: HANDLE->void*
  194. [System.Runtime.InteropServices.DllImportAttribute("kernel32.dll", EntryPoint = "CreateFileW")]
  195. 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);
  196. /// Return Type: BOOL->int
  197. ///hObject: HANDLE->void*
  198. [System.Runtime.InteropServices.DllImportAttribute("kernel32.dll", EntryPoint = "CloseHandle")]
  199. [return: System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.Bool)]
  200. public static extern bool CloseHandle([System.Runtime.InteropServices.InAttribute()] System.IntPtr hObject);
  201. /// Return Type: BOOL->int
  202. ///hDevice: HANDLE->void*
  203. ///dwIoControlCode: DWORD->unsigned int
  204. ///lpInBuffer: LPVOID->void*
  205. ///nInBufferSize: DWORD->unsigned int
  206. ///lpOutBuffer: LPVOID->void*
  207. ///nOutBufferSize: DWORD->unsigned int
  208. ///lpBytesReturned: LPDWORD->DWORD*
  209. ///lpOverlapped: LPOVERLAPPED->_OVERLAPPED*
  210. [System.Runtime.InteropServices.DllImportAttribute("kernel32.dll", EntryPoint = "DeviceIoControl")]
  211. [return: System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.Bool)]
  212. 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);
  213. /// DRIVE_REMOVABLE -> 2
  214. public const int DRIVE_REMOVABLE = 2;
  215. /// DRIVE_CDROM -> 5
  216. public const int DRIVE_CDROM = 5;
  217. /// INVALID_HANDLE_VALUE -> -1
  218. public const int INVALID_HANDLE_VALUE = -1;
  219. /// GENERIC_READ -> (0x80000000L)
  220. public const int GENERIC_READ = -2147483648;
  221. /// GENERIC_WRITE -> (0x40000000L)
  222. public const int GENERIC_WRITE = 1073741824;
  223. /// FILE_SHARE_READ -> 0x00000001
  224. public const int FILE_SHARE_READ = 1;
  225. /// FILE_SHARE_WRITE -> 0x00000002
  226. public const int FILE_SHARE_WRITE = 2;
  227. /// OPEN_EXISTING -> 3
  228. public const int OPEN_EXISTING = 3;
  229. //WinIoCtl.h
  230. //
  231. //#define CTL_CODE( DeviceType, Function, Method, Access ) (                 /
  232. //  ((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method) /
  233. //)
  234. private const int FILE_DEVICE_FILE_SYSTEM = 0x00000009;
  235. private const int METHOD_BUFFERED = 0;
  236. private const int FILE_ANY_ACCESS = 0;
  237. //#define FSCTL_LOCK_VOLUME               CTL_CODE(FILE_DEVICE_FILE_SYSTEM,  6, METHOD_BUFFERED, FILE_ANY_ACCESS)
  238. public const int FSCTL_LOCK_VOLUME = ((FILE_DEVICE_FILE_SYSTEM) << 16) | ((FILE_ANY_ACCESS) << 14) | ((6) << 2) | (METHOD_BUFFERED);
  239. public const int FSCTL_UNLOCK_VOLUME = ((FILE_DEVICE_FILE_SYSTEM) << 16) | ((FILE_ANY_ACCESS) << 14) | ((7) << 2) | (METHOD_BUFFERED);
  240. public const int FSCTL_DISMOUNT_VOLUME = ((FILE_DEVICE_FILE_SYSTEM) << 16) | ((FILE_ANY_ACCESS) << 14) | ((8) << 2) | (METHOD_BUFFERED);
  241. //#define IOCTL_STORAGE_BASE FILE_DEVICE_MASS_STORAGE
  242. private const int FILE_DEVICE_MASS_STORAGE = 0x0000002d;
  243. private const int IOCTL_STORAGE_BASE = FILE_DEVICE_MASS_STORAGE;
  244. //#define FILE_READ_ACCESS          ( 0x0001 )    // file & pipe
  245. private const int FILE_READ_ACCESS = 0x0001;
  246. //#define IOCTL_STORAGE_MEDIA_REMOVAL           CTL_CODE(IOCTL_STORAGE_BASE, 0x0201, METHOD_BUFFERED, FILE_READ_ACCESS)
  247. public const int IOCTL_STORAGE_MEDIA_REMOVAL =
  248. ((IOCTL_STORAGE_BASE) << 16) | ((FILE_READ_ACCESS) << 14) | ((0x0201) << 2) | (METHOD_BUFFERED);
  249. //#define IOCTL_STORAGE_EJECT_MEDIA             CTL_CODE(IOCTL_STORAGE_BASE, 0x0202, METHOD_BUFFERED, FILE_READ_ACCESS)
  250. public const int IOCTL_STORAGE_EJECT_MEDIA =
  251. ((IOCTL_STORAGE_BASE) << 16) | ((FILE_READ_ACCESS) << 14) | ((0x0202) << 2) | (METHOD_BUFFERED);
  252. [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
  253. public struct PREVENT_MEDIA_REMOVAL
  254. {
  255. /// BOOLEAN->BYTE->unsigned char
  256. [MarshalAs(UnmanagedType.I1)]
  257. public bool PreventMediaRemoval;
  258. }
  259. #region Remove Volume Definition
  260. /// DDD_REMOVE_DEFINITION -> 0x00000002
  261. public const int DDD_REMOVE_DEFINITION = 2;
  262. /// Return Type: BOOL->int
  263. ///dwFlags: DWORD->unsigned int
  264. ///lpDeviceName: LPCWSTR->WCHAR*
  265. ///lpTargetPath: LPCWSTR->WCHAR*
  266. [System.Runtime.InteropServices.DllImportAttribute("kernel32.dll", EntryPoint="DefineDosDeviceW")]
  267. [return: System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.Bool)]
  268. 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) ;
  269. #endregion
  270. #endregion
  271. }

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

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

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