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

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

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

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

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

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

 

代码片段如下:

using System; using System.Runtime.InteropServices; ///

/// 监听设备的插入和拔出 /// public class DriveDetector { /// /// 设备插入事件 /// public event EventHandler DeviceArrived = null; /// /// 设备拔出事件 /// public event EventHandler DeviceRemoved = null; /// /// 消息处理(HwndSourceHook委托的签名) /// /// /// /// /// /// /// public IntPtr WndProc( IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) { if (msg == NativeConstants.WM_DEVICECHANGE) { #warning USB设备检测目前只支持32位系统) switch (wParam.ToInt32()) { case NativeConstants.DBT_DEVICEARRIVAL: { var devType = Marshal.ReadInt32(lParam, 4); if (devType == NativeConstants.DBT_DEVTYP_VOLUME) { var drive = GetDrive(lParam); if (DeviceArrived != null) { var args = new DriveDectctorEventArgs(drive); DeviceArrived(this, args); //触发设备插入事件 } } } break; case NativeConstants.DBT_DEVICEREMOVECOMPLETE: { var devType = Marshal.ReadInt32(lParam, 4); if (devType == NativeConstants.DBT_DEVTYP_VOLUME) { var drive = GetDrive(lParam); if (DeviceRemoved != null) { var args = new DriveDectctorEventArgs(drive); DeviceRemoved(this, args); } } } break; } } return IntPtr.Zero; } private static string GetDrive(IntPtr lParam) { var volume = (DEV_BROADCAST_VOLUME)Marshal.PtrToStructure(lParam, typeof(DEV_BROADCAST_VOLUME)); var letter = GetLetter(volume.dbcv_unitmask); return string.Format("{0}://", letter); } /// /// 获得盘符 /// /// /// 1 = A /// 2 = B /// 4 = C... /// /// 结果是A~Z的任意一个字符或者为'?' private static char GetLetter(uint dbcvUnitmask) { const char nona = '?'; const string drives = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; if (dbcvUnitmask == 0) return nona; var i = 0; var pom = dbcvUnitmask >> 1; while (pom != 0) { pom = pom >> 1; i++; } if (i < drives.Length) return drives[i]; return nona; } /* private static void GetLetterTest() { for (int i = 0; i < 67108864; i++) { Console.WriteLine("{0} - {1}", i, GetLetter((uint)i)); i = i << 1; } //0 - ? //1 - A //3 - B //7 - C //15 - D //31 - E //63 - F //127 - G //255 - H //511 - I //1023 - J //2047 - K //4095 - L //8191 - M //16383 - N //32767 - O //65535 - P //131071 - Q //262143 - R //524287 - S //1048575 - T //2097151 - U //4194303 - V //8388607 - W //16777215 - X //33554431 - Y //67108863 - Z }*/ /// /// 设备插入或拔出事件 /// public class DriveDectctorEventArgs : EventArgs { /// /// 获得设备卷标 /// public string Drive { get; private set; } public DriveDectctorEventArgs(string drive) { Drive = drive ?? string.Empty; } } #region Win32 API public partial class NativeConstants { /// WM_DEVICECHANGE -> 0x0219 public const int WM_DEVICECHANGE = 537; /// BROADCAST_QUERY_DENY -> 0x424D5144 //public const int BROADCAST_QUERY_DENY = 1112363332; //public const int DBT_DEVTYP_DEVICEINTERFACE = 5; //public const int DBT_DEVTYP_HANDLE = 6; public const int DBT_DEVICEARRIVAL = 0x8000; // system detected a new device //public const int DBT_DEVICEQUERYREMOVE = 0x8001; // Preparing to remove (any program can disable the removal) public const int DBT_DEVICEREMOVECOMPLETE = 0x8004; // removed public const int DBT_DEVTYP_VOLUME = 0x00000002; // drive type is logical volume } [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] public struct DEV_BROADCAST_VOLUME { /// DWORD->unsigned int public uint dbcv_size; /// DWORD->unsigned int public uint dbcv_devicetype; /// DWORD->unsigned int public uint dbcv_reserved; /// DWORD->unsigned int public uint dbcv_unitmask; /// WORD->unsigned short public ushort dbcv_flags; } [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] public struct OVERLAPPED { /// ULONG_PTR->unsigned int public uint Internal; /// ULONG_PTR->unsigned int public uint InternalHigh; /// Anonymous_7416d31a_1ce9_4e50_b1e1_0f2ad25c0196 public Anonymous_7416d31a_1ce9_4e50_b1e1_0f2ad25c0196 Union1; /// HANDLE->void* public System.IntPtr hEvent; } [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Explicit)] public struct Anonymous_7416d31a_1ce9_4e50_b1e1_0f2ad25c0196 { /// Anonymous_ac6e4301_4438_458f_96dd_e86faeeca2a6 [System.Runtime.InteropServices.FieldOffsetAttribute(0)] public Anonymous_ac6e4301_4438_458f_96dd_e86faeeca2a6 Struct1; /// PVOID->void* [System.Runtime.InteropServices.FieldOffsetAttribute(0)] public System.IntPtr Pointer; } [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] public struct Anonymous_ac6e4301_4438_458f_96dd_e86faeeca2a6 { /// DWORD->unsigned int public uint Offset; /// DWORD->unsigned int public uint OffsetHigh; } [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] public struct SECURITY_ATTRIBUTES { /// DWORD->unsigned int public uint nLength; /// LPVOID->void* public System.IntPtr lpSecurityDescriptor; /// BOOL->int [System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.Bool)] public bool bInheritHandle; } #endregion } 

 

现在,你可以在你的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

摘取代码如下:

using System; using System.IO; using System.Security.Permissions; public class Watcher { public static void Main() { Run(); } [PermissionSet(SecurityAction.Demand, Name="FullTrust")] public static void Run() { string[] args = System.Environment.GetCommandLineArgs(); // If a directory is not specified, exit program. if(args.Length != 2) { // Display the proper way to call the program. Console.WriteLine("Usage: Watcher.exe (directory)"); return; } // Create a new FileSystemWatcher and set its properties. FileSystemWatcher watcher = new FileSystemWatcher(); watcher.Path = args[1]; /* Watch for changes in LastAccess and LastWrite times, and the renaming of files or directories. */ watcher.NotifyFilter = NotifyFilters.LastAccess | NotifyFilters.LastWrite | NotifyFilters.FileName | NotifyFilters.DirectoryName; // Only watch text files. //watcher.Filter = "*.txt"; watcher.IncludeSubdirectories=true; // Add event handlers. watcher.Changed += new FileSystemEventHandler(OnChanged); watcher.Created += new FileSystemEventHandler(OnChanged); watcher.Deleted += new FileSystemEventHandler(OnChanged); watcher.Renamed += new RenamedEventHandler(OnRenamed); // Begin watching. watcher.EnableRaisingEvents = true; // Wait for the user to quit the program. Console.WriteLine("Press /'q/' to quit the sample."); while(Console.Read()!='q'); } // Define the event handlers. private static void OnChanged(object source, FileSystemEventArgs e) { // Specify what is done when a file is changed, created, or deleted. Console.WriteLine("File: " + e.FullPath + " " + e.ChangeType); } private static void OnRenamed(object source, RenamedEventArgs e) { // Specify what is done when a file is renamed. Console.WriteLine("File: {0} renamed to {1}", e.OldFullPath, e.FullPath); } }  

 

 

 

二、软件弹出可移动媒体

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

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

using System; using System.Runtime.InteropServices; using System.Threading; ///

/// 弹出可移动媒体 /// /// public static class Eject { private static void ReportError(string szMsg) { const string szErrorFormat = "Error {0}: {1}"; var error = string.Format(szErrorFormat, GetLastError(), szMsg); Console.Error.WriteLine(error); } private static IntPtr OpenVolume(char driveLetter) { const string volumeFormat = "////.//{0}:"; const string rootFormat = "{0}://"; int accessFlags; var rootName = string.Format(rootFormat, driveLetter); var driveType = GetDriveTypeW(rootName); switch (driveType) { case DRIVE_REMOVABLE: accessFlags = GENERIC_READ | GENERIC_WRITE; break; case DRIVE_CDROM: accessFlags = GENERIC_READ; break; default: Console.Error.WriteLine("Cannot eject. Drive type is incorrect."); return new IntPtr(INVALID_HANDLE_VALUE); } var volumeName = string.Format(volumeFormat, driveLetter); var hVolume = CreateFileW( volumeName, accessFlags, FILE_SHARE_READ | FILE_SHARE_WRITE, IntPtr.Zero, OPEN_EXISTING, 0, IntPtr.Zero); if (hVolume == new IntPtr(INVALID_HANDLE_VALUE)) ReportError("CreateFile"); return hVolume; } private static bool CloseVolume(IntPtr hVolume) { return CloseHandle(hVolume); } private static bool LockVolume(IntPtr hVolume) { const int LOCK_TIMEOUT = 10000; //10 Seconds const int LOCK_RETRIES = 20; var sleepAmount = LOCK_TIMEOUT / LOCK_RETRIES; //Do this in a loop until a timeout period has expired for (int tryCount = 0; tryCount < LOCK_RETRIES; tryCount++) { int dwBytesReturned; if (DeviceIoControl( hVolume, FSCTL_LOCK_VOLUME, IntPtr.Zero, 0, IntPtr.Zero, 0, out dwBytesReturned, IntPtr.Zero)) return true; //return Thread.Sleep(sleepAmount); } return false; } private static bool DismountVolume(IntPtr hVolume) { int dwBytesReturned; return DeviceIoControl( hVolume, FSCTL_DISMOUNT_VOLUME, IntPtr.Zero, 0, IntPtr.Zero, 0, out dwBytesReturned, IntPtr.Zero); } private static bool PresentRemovalOfVolume(IntPtr hVolume,bool preventRemoval) { PREVENT_MEDIA_REMOVAL pmrBuffer; pmrBuffer.PreventMediaRemoval = preventRemoval; var size = Marshal.SizeOf(pmrBuffer); IntPtr ptr = Marshal.AllocHGlobal(size); try { Marshal.StructureToPtr(pmrBuffer, ptr, false); int dwBytesReturned; return DeviceIoControl( hVolume, IOCTL_STORAGE_MEDIA_REMOVAL, ptr, (uint) size, IntPtr.Zero, 0, out dwBytesReturned, IntPtr.Zero ); } finally { Marshal.DestroyStructure(ptr, pmrBuffer.GetType()); } } private static bool AutoEjectVolume(IntPtr hVolume) { int dwBytesReturned; return DeviceIoControl( hVolume, IOCTL_STORAGE_EJECT_MEDIA, IntPtr.Zero, 0, IntPtr.Zero, 0, out dwBytesReturned, IntPtr.Zero); } private static bool RemoveVolumeDefinition(string deviceName) { return DefineDosDeviceW(DDD_REMOVE_DEFINITION, deviceName, null); } public static bool EjectVolume(char driveLetter, bool removeVolumeDefinition) { var removeSafely = false; var autoEject = false; //Open the volume. var hVolume = OpenVolume(driveLetter); if (hVolume == new IntPtr(INVALID_HANDLE_VALUE)) return false; //Lock and dismount the volume. if(LockVolume(hVolume)&&DismountVolume(hVolume)) { removeSafely = true; //Set prevent removal to false and eject the volume. if (PresentRemovalOfVolume(hVolume, false) && AutoEjectVolume(hVolume)) autoEject = true; } //Close the volume so other processes can use the drive. if (!CloseVolume(hVolume)) return false; if(autoEject) { Console.Out.WriteLine("Media in Drive {0} has been ejected safely.",driveLetter); } else { if (removeSafely) { Console.Out.WriteLine("Media in Drive {0} can be safely removed.", driveLetter); } else { Console.Error.WriteLine("Media in Drive {0} is working, and can't be safely removed.", driveLetter); return false; } } if(removeVolumeDefinition) RemoveVolumeDefinition(string.Format("{0}:", driveLetter)); return true; } public static void Usage() { Console.Out.WriteLine("Usage: Eject "); Console.Out.WriteLine(); } static void Main(string[] args) { if(args.Length != 1) { Usage(); return; } if(!EjectVolume(args[0][0], true)) { Console.Error.WriteLine("Failure ejecting drive {0}.",args[0][0]); } } #region WIN32 API /// Return Type: DWORD->unsigned int [System.Runtime.InteropServices.DllImportAttribute("kernel32.dll", EntryPoint = "GetLastError")] public static extern uint GetLastError(); /// Return Type: UINT->unsigned int ///lpRootPathName: LPCWSTR->WCHAR* [System.Runtime.InteropServices.DllImportAttribute("kernel32.dll", EntryPoint = "GetDriveTypeW")] public static extern uint GetDriveTypeW([System.Runtime.InteropServices.InAttribute()] [System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.LPWStr)] string lpRootPathName); /// Return Type: HANDLE->void* ///lpFileName: LPCWSTR->WCHAR* ///dwDesiredAccess: DWORD->unsigned int ///dwShareMode: DWORD->unsigned int ///lpSecurityAttributes: LPSECURITY_ATTRIBUTES->_SECURITY_ATTRIBUTES* ///dwCreationDisposition: DWORD->unsigned int ///dwFlagsAndAttributes: DWORD->unsigned int ///hTemplateFile: HANDLE->void* [System.Runtime.InteropServices.DllImportAttribute("kernel32.dll", EntryPoint = "CreateFileW")] 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); /// Return Type: BOOL->int ///hObject: HANDLE->void* [System.Runtime.InteropServices.DllImportAttribute("kernel32.dll", EntryPoint = "CloseHandle")] [return: System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.Bool)] public static extern bool CloseHandle([System.Runtime.InteropServices.InAttribute()] System.IntPtr hObject); /// Return Type: BOOL->int ///hDevice: HANDLE->void* ///dwIoControlCode: DWORD->unsigned int ///lpInBuffer: LPVOID->void* ///nInBufferSize: DWORD->unsigned int ///lpOutBuffer: LPVOID->void* ///nOutBufferSize: DWORD->unsigned int ///lpBytesReturned: LPDWORD->DWORD* ///lpOverlapped: LPOVERLAPPED->_OVERLAPPED* [System.Runtime.InteropServices.DllImportAttribute("kernel32.dll", EntryPoint = "DeviceIoControl")] [return: System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.Bool)] 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); /// DRIVE_REMOVABLE -> 2 public const int DRIVE_REMOVABLE = 2; /// DRIVE_CDROM -> 5 public const int DRIVE_CDROM = 5; /// INVALID_HANDLE_VALUE -> -1 public const int INVALID_HANDLE_VALUE = -1; /// GENERIC_READ -> (0x80000000L) public const int GENERIC_READ = -2147483648; /// GENERIC_WRITE -> (0x40000000L) public const int GENERIC_WRITE = 1073741824; /// FILE_SHARE_READ -> 0x00000001 public const int FILE_SHARE_READ = 1; /// FILE_SHARE_WRITE -> 0x00000002 public const int FILE_SHARE_WRITE = 2; /// OPEN_EXISTING -> 3 public const int OPEN_EXISTING = 3; //WinIoCtl.h // //#define CTL_CODE( DeviceType, Function, Method, Access ) ( / // ((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method) / //) private const int FILE_DEVICE_FILE_SYSTEM = 0x00000009; private const int METHOD_BUFFERED = 0; private const int FILE_ANY_ACCESS = 0; //#define FSCTL_LOCK_VOLUME CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 6, METHOD_BUFFERED, FILE_ANY_ACCESS) public const int FSCTL_LOCK_VOLUME = ((FILE_DEVICE_FILE_SYSTEM) << 16) | ((FILE_ANY_ACCESS) << 14) | ((6) << 2) | (METHOD_BUFFERED); public const int FSCTL_UNLOCK_VOLUME = ((FILE_DEVICE_FILE_SYSTEM) << 16) | ((FILE_ANY_ACCESS) << 14) | ((7) << 2) | (METHOD_BUFFERED); public const int FSCTL_DISMOUNT_VOLUME = ((FILE_DEVICE_FILE_SYSTEM) << 16) | ((FILE_ANY_ACCESS) << 14) | ((8) << 2) | (METHOD_BUFFERED); //#define IOCTL_STORAGE_BASE FILE_DEVICE_MASS_STORAGE private const int FILE_DEVICE_MASS_STORAGE = 0x0000002d; private const int IOCTL_STORAGE_BASE = FILE_DEVICE_MASS_STORAGE; //#define FILE_READ_ACCESS ( 0x0001 ) // file & pipe private const int FILE_READ_ACCESS = 0x0001; //#define IOCTL_STORAGE_MEDIA_REMOVAL CTL_CODE(IOCTL_STORAGE_BASE, 0x0201, METHOD_BUFFERED, FILE_READ_ACCESS) public const int IOCTL_STORAGE_MEDIA_REMOVAL = ((IOCTL_STORAGE_BASE) << 16) | ((FILE_READ_ACCESS) << 14) | ((0x0201) << 2) | (METHOD_BUFFERED); //#define IOCTL_STORAGE_EJECT_MEDIA CTL_CODE(IOCTL_STORAGE_BASE, 0x0202, METHOD_BUFFERED, FILE_READ_ACCESS) public const int IOCTL_STORAGE_EJECT_MEDIA = ((IOCTL_STORAGE_BASE) << 16) | ((FILE_READ_ACCESS) << 14) | ((0x0202) << 2) | (METHOD_BUFFERED); [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] public struct PREVENT_MEDIA_REMOVAL { /// BOOLEAN->BYTE->unsigned char [MarshalAs(UnmanagedType.I1)] public bool PreventMediaRemoval; } #region Remove Volume Definition /// DDD_REMOVE_DEFINITION -> 0x00000002 public const int DDD_REMOVE_DEFINITION = 2; /// Return Type: BOOL->int ///dwFlags: DWORD->unsigned int ///lpDeviceName: LPCWSTR->WCHAR* ///lpTargetPath: LPCWSTR->WCHAR* [System.Runtime.InteropServices.DllImportAttribute("kernel32.dll", EntryPoint="DefineDosDeviceW")] [return: System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.Bool)] 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) ; #endregion #endregion }

 

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

 

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

 

你可能感兴趣的:(代码收藏和演示,小问题及解决)