namespace ConsoleApplication { using System; using Microshaoft; public class Class1 { static void Main(string[] args) { DriveDetector x = new DriveDetector(); x.DeviceArrived += new DriveDetector.DriveDetectorEventHandler(x_DeviceArrived); x.DeviceRemoved += new DriveDetector.DriveDetectorEventHandler(x_DeviceRemoved); Console.WriteLine("Hello World"); Console.WriteLine(Environment.Version.ToString()); Console.ReadLine(); } static void x_DeviceRemoved(DriveDetector sender, DriveDetectorEventArgs e) { Console.WriteLine("DeviceRemoved {0}", e.Drive); } static void x_DeviceArrived(DriveDetector sender, DriveDetectorEventArgs e) { Console.WriteLine("DeviceArrived {0}", e.Drive); } } } namespace Microshaoft { using System; using System.Windows.Forms; // required for Message using System.Runtime.InteropServices; // required for Marshal using System.IO; using Microsoft.Win32.SafeHandles; using System.Threading; public class DriveDetectorEventArgs : EventArgs { public DriveDetectorEventArgs() { Cancel = false; Drive = ""; HookQueryRemove = false; } public bool Cancel; public string Drive; public bool HookQueryRemove; } public class DriveDetector : IDisposable { public event DriveDetectorEventHandler DeviceArrived; public event DriveDetectorEventHandler DeviceRemoved; public event DriveDetectorEventHandler QueryRemove; public delegate void DriveDetectorEventHandler(DriveDetector sender, DriveDetectorEventArgs e); private WindowsMessagesListener _listener;// = new WindowsMessagesListener(); private IntPtr _DirHandle = IntPtr.Zero; private FileStream _FileOnFlash = null; private string _FileToOpen; private IntPtr _DeviceNotifyHandle; private IntPtr _hWnd; private string _CurrentDrive; private Thread _thread; private class WindowsMessagesListener : Form//NativeWindow { public delegate void WndProcEventHandler(ref Message m, bool cancel); public event WndProcEventHandler BeforeBaseWndProc; public event WndProcEventHandler AfterBaseWndProc; protected override void WndProc(ref Message m) { bool b = false; if (BeforeBaseWndProc != null) { BeforeBaseWndProc(ref m, b); } if (!b) { base.WndProc(ref m); if (AfterBaseWndProc != null) { AfterBaseWndProc(ref m, b); } } } } public DriveDetector() { Init(null); } public DriveDetector(string FileToOpen) { Init(FileToOpen); } private void Init(string fileToOpen) { _thread = new Thread(new ThreadStart(Run)); //监视线程: 显示滚动计数器 _thread.Start(); _FileToOpen = fileToOpen; _FileOnFlash = null; _DeviceNotifyHandle = IntPtr.Zero; _DirHandle = IntPtr.Zero; // handle to the root directory of the flash drive which we open _CurrentDrive = ""; } private void Run() { _listener = new WindowsMessagesListener(); _hWnd = _listener.Handle; _listener.AfterBaseWndProc += new WindowsMessagesListener.WndProcEventHandler(_listener_AfterBaseWndProc); Application.Run(); } void _listener_AfterBaseWndProc(ref Message m, bool canceledBaseWndProc) { int devType; char c; if (m.Msg == Win32Native.WM_DEVICECHANGE) { // WM_DEVICECHANGE can have several meanings depending on the WParam value... switch (m.WParam.ToInt32()) { // // New device has just arrived // case Win32Native.DBT_DEVICEARRIVAL : devType = Marshal.ReadInt32(m.LParam, 4); if (devType == Win32Native.DBT_DEVTYP_VOLUME) { DEV_BROADCAST_VOLUME vol; vol = (DEV_BROADCAST_VOLUME) Marshal.PtrToStructure(m.LParam, typeof(DEV_BROADCAST_VOLUME)); // Get the drive letter c = DriveMaskToLetter(vol.dbcv_unitmask); // // Call the client event handler // // We should create copy of the event before testing it and // calling the delegate - if any DriveDetectorEventHandler tempDeviceArrived = DeviceArrived; if (tempDeviceArrived != null) { DriveDetectorEventArgs e = new DriveDetectorEventArgs(); e.Drive = c + ":\\"; tempDeviceArrived(this, e); // Register for query remove if requested if (e.HookQueryRemove) { // If something is already hooked, unhook it now if (_DeviceNotifyHandle != IntPtr.Zero) { RegisterForDeviceChange(false, null); } RegisterQuery(c + ":\\"); } } // if has event handler } break; // // Device is about to be removed // Any application can cancel the removal // case Win32Native.DBT_DEVICEQUERYREMOVE : devType = Marshal.ReadInt32(m.LParam, 4); if (devType == Win32Native.DBT_DEVTYP_HANDLE) { // TODO: we could get the handle for which this message is sent // from vol.dbch_handle and compare it against a list of handles for // which we have registered the query remove message (?) //DEV_BROADCAST_HANDLE vol; //vol = (DEV_BROADCAST_HANDLE) // Marshal.PtrToStructure(_.LParam, typeof(DEV_BROADCAST_HANDLE)); // if ( vol.dbch_handle .... // // Call the event handler in client // DriveDetectorEventHandler tempQuery = QueryRemove; if (tempQuery != null) { DriveDetectorEventArgs e = new DriveDetectorEventArgs(); e.Drive = _CurrentDrive; // drive which is hooked tempQuery(this, e); // If the client wants to cancel, let Windows know if (e.Cancel) { m.Result = (IntPtr)Win32Native.BROADCAST_QUERY_DENY; } else { // Change 28.10.2007: Unregister the notification, this will // close the handle to file or root directory also. // We have to close it anyway to allow the removal so // even if some other app cancels the removal we would not know about it... RegisterForDeviceChange(false, null); // will also close the mFileOnFlash } } } break; // // Device has been removed // case Win32Native.DBT_DEVICEREMOVECOMPLETE : devType = Marshal.ReadInt32(m.LParam, 4); if (devType == Win32Native.DBT_DEVTYP_VOLUME) { devType = Marshal.ReadInt32(m.LParam, 4); if (devType == Win32Native.DBT_DEVTYP_VOLUME) { DEV_BROADCAST_VOLUME vol; vol = (DEV_BROADCAST_VOLUME) Marshal.PtrToStructure(m.LParam, typeof(DEV_BROADCAST_VOLUME)); c = DriveMaskToLetter(vol.dbcv_unitmask); // // Call the client event handler // DriveDetectorEventHandler tempDeviceRemoved = DeviceRemoved; if (tempDeviceRemoved != null) { DriveDetectorEventArgs e = new DriveDetectorEventArgs(); e.Drive = c + ":\\"; tempDeviceRemoved(this, e); } // TODO: we could unregister the notify handle here if we knew it is the // right drive which has been just removed //RegisterForDeviceChange(false, null); } } break; } } } public bool IsQueryHooked { get { if (_DeviceNotifyHandle == IntPtr.Zero) { return false; } else { return true; } } } public string HookedDrive { get { return _CurrentDrive; } } public FileStream OpenedFile { get { return _FileOnFlash; } } public bool EnableQueryRemove(string fileOnDrive) { if (fileOnDrive == null || fileOnDrive.Length == 0) { throw new ArgumentException("Drive path must be supplied to register for Query remove."); } if (fileOnDrive.Length == 2 && fileOnDrive[1] == ':') { fileOnDrive += '\\'; // append "\\" if only drive letter with ":" was passed in. } if (_DeviceNotifyHandle != IntPtr.Zero) { // Unregister first... RegisterForDeviceChange(false, null); } if (Path.GetFileName(fileOnDrive).Length == 0 || !File.Exists(fileOnDrive)) { _FileToOpen = null; // use root directory... } else { _FileToOpen = fileOnDrive; } RegisterQuery(Path.GetPathRoot(fileOnDrive)); if (_DeviceNotifyHandle == IntPtr.Zero) { return false; // failed to register } return true; } public void DisableQueryRemove() { if (_DeviceNotifyHandle != IntPtr.Zero) { RegisterForDeviceChange(false, null); } } public void Dispose() { RegisterForDeviceChange(false, null); } private void RegisterQuery(string drive) { bool register = true; if (!String.IsNullOrEmpty(_FileToOpen)) { // Make sure the path in mFileToOpen contains valid drive // If there is a drive letter in the path, it may be different from the actual // letter assigned to the drive now. We will cut it off and merge the actual drive // with the rest of the path. if (_FileToOpen.Contains(":")) { string tmp = _FileToOpen.Substring(3); string root = Path.GetPathRoot(drive); _FileToOpen = Path.Combine(root, tmp); } else { _FileToOpen = Path.Combine(drive, _FileToOpen); } } try { //mFileOnFlash = new FileStream(_FileToOpen, FileMode.Open); // Change 28.10.2007 - Open the root directory if (_FileToOpen == null) // open root directory { _FileOnFlash = null; } else { _FileOnFlash = new FileStream(_FileToOpen, FileMode.Open); } } catch (Exception) { // just do not register if the file could not be opened register = false; } if (register) { //RegisterForDeviceChange(true, mFileOnFlash.SafeFileHandle); //mCurrentDrive = drive; // Change 28.10.2007 - Open the root directory if (_FileOnFlash == null) { RegisterForDeviceChange(drive); } else { // old version RegisterForDeviceChange(true,_FileOnFlash.SafeFileHandle); } _CurrentDrive = drive; } } private void RegisterForDeviceChange(string dirPath) { IntPtr handle = Win32Native.OpenDirectory(dirPath); if (handle == IntPtr.Zero) { _DeviceNotifyHandle = IntPtr.Zero; return; } else _DirHandle = handle; // save handle for closing it when unregistering // Register for handle DEV_BROADCAST_HANDLE data = new DEV_BROADCAST_HANDLE(); data.dbch_devicetype = Win32Native.DBT_DEVTYP_HANDLE; data.dbch_reserved = 0; data.dbch_nameoffset = 0; //data.dbch_data = null; //data.dbch_eventguid = 0; data.dbch_handle = handle; data.dbch_hdevnotify = (IntPtr)0; int size = Marshal.SizeOf(data); data.dbch_size = size; IntPtr buffer = Marshal.AllocHGlobal(size); Marshal.StructureToPtr(data, buffer, true); _DeviceNotifyHandle = Win32Native.RegisterDeviceNotification(_hWnd, buffer, 0); } private void RegisterForDeviceChange(bool register, SafeFileHandle fileHandle) { if (register) { // Register for handle DEV_BROADCAST_HANDLE data = new DEV_BROADCAST_HANDLE(); data.dbch_devicetype = Win32Native.DBT_DEVTYP_HANDLE; data.dbch_reserved = 0; data.dbch_nameoffset = 0; //data.dbch_data = null; //data.dbch_eventguid = 0; data.dbch_handle = fileHandle.DangerousGetHandle(); //Marshal. fileHandle; data.dbch_hdevnotify = (IntPtr)0; int size = Marshal.SizeOf(data); data.dbch_size = size; IntPtr buffer = Marshal.AllocHGlobal(size); Marshal.StructureToPtr(data, buffer, true); _DeviceNotifyHandle = Win32Native.RegisterDeviceNotification(_hWnd, buffer, 0); } else { // close the directory handle if (_DirHandle != IntPtr.Zero) { Win32Native.CloseDirectoryHandle(_DirHandle); // string er = Marshal.GetLastWin32Error().ToString(); } // unregister if (_DeviceNotifyHandle != IntPtr.Zero) { Win32Native.UnregisterDeviceNotification(_DeviceNotifyHandle); } _DeviceNotifyHandle = IntPtr.Zero; _DirHandle = IntPtr.Zero; _CurrentDrive = ""; if (_FileOnFlash != null) { _FileOnFlash.Close(); _FileOnFlash = null; } } } private static char DriveMaskToLetter(int mask) { char c; string drives = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; // 1 = A // 2 = B // 4 = C... int i = 0; int p = mask / 2; while (p != 0) { p = p / 2; i ++; } if (i < drives.Length) { c = drives[i]; } else { c = '?'; } return c; } private class Win32Native { // Win32 constants public const int DBT_DEVTYP_DEVICEINTERFACE = 5; public const int DBT_DEVTYP_HANDLE = 6; public const int BROADCAST_QUERY_DENY = 0x424D5144; public const int WM_DEVICECHANGE = 0x0219; 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 // HDEVNOTIFY RegisterDeviceNotification(HANDLE hRecipient,LPVOID NotificationFilter,DWORD Flags); [DllImport("user32.dll", CharSet = CharSet.Auto)] public static extern IntPtr RegisterDeviceNotification(IntPtr hRecipient, IntPtr NotificationFilter, uint Flags); [DllImport("user32.dll", CharSet = CharSet.Auto)] public static extern uint UnregisterDeviceNotification(IntPtr hHandle); // // CreateFile - MSDN const uint GENERIC_READ = 0x80000000; const uint OPEN_EXISTING = 3; const uint FILE_SHARE_READ = 0x00000001; const uint FILE_SHARE_WRITE = 0x00000002; const uint FILE_ATTRIBUTE_NORMAL = 128; const uint FILE_FLAG_BACKUP_SEMANTICS = 0x02000000; static readonly IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1); // should be "static extern unsafe" [DllImport("kernel32", SetLastError = true)] static extern IntPtr CreateFile ( string FileName, // file name uint DesiredAccess, // access mode uint ShareMode, // share mode uint SecurityAttributes, // Security Attributes uint CreationDisposition, // how to create uint FlagsAndAttributes, // file attributes int hTemplateFile // handle to template file ); [DllImport("kernel32", SetLastError = true)] static extern bool CloseHandle ( IntPtr hObject // handle to object ); static public IntPtr OpenDirectory(string dirPath) { // open the existing file for reading IntPtr handle = CreateFile ( dirPath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_ATTRIBUTE_NORMAL, 0 ); if (handle == INVALID_HANDLE_VALUE) { return IntPtr.Zero; } else { return handle; } } public static bool CloseDirectoryHandle(IntPtr handle) { return CloseHandle(handle); } } // Structure with information for RegisterDeviceNotification. [StructLayout(LayoutKind.Sequential)] public struct DEV_BROADCAST_HANDLE { public int dbch_size; public int dbch_devicetype; public int dbch_reserved; public IntPtr dbch_handle; public IntPtr dbch_hdevnotify; public Guid dbch_eventguid; public long dbch_nameoffset; //public byte[] dbch_data[1]; // = new byte[1]; public byte dbch_data; public byte dbch_data1; } // Struct for parameters of the WM_DEVICECHANGE message [StructLayout(LayoutKind.Sequential)] public struct DEV_BROADCAST_VOLUME { public int dbcv_size; public int dbcv_devicetype; public int dbcv_reserved; public int dbcv_unitmask; } } }