How to trap all the WM_DEVICECHANGE messages

Sep 10 2004 3:45 AM
Since I am a new programmer, I know very few about Win32 API. In fact, I am writing something about detecting storage device attaching/detaching, and media insert/remove from the storage device. I've got two questions. 1) How can I trap all the WM_DEVICECHANGE messages, not just the default two (DBT_DEVICEARRIVAL and DBT_DEVICEREMOVECOMPLETE). 2) How can I detect media insert/remove from the storage device, such as: insert/remove CF card from a USB card reader? * I've tried to read the MSDN online to get some ideas, but I still don't understand. If I can, may I ask for a complete sample? Thanks, Thanks, Thanks a lot!!! ---Here is the class: using System; using System.Text; using System.Windows.Forms; using System.Runtime.InteropServices; namespace Staging { /// /// Delegate used to implement the class events /// public delegate void DeviceVolumeAction(int aMask); /// /// Custom exception /// public class DeviceVolumeMonitorException: ApplicationException { public DeviceVolumeMonitorException(string aMessage):base(aMessage) {} } internal class _DeviceVolumeMonitor: NativeWindow { /// /// Private fields /// DeviceVolumeMonitor fMonitor; #region API constants and structures /// /// Constant defined for the WM_DEVICECHANGE message in WinUser.h /// const int WM_DEVICECHANGE = 0x0219; /// /// Constants and structs defined in DBT.h /// public enum DeviceEvent:int { Arrival = 0x8000, //DBT_DEVICEARRIVAL QueryRemove = 0x8001, //DBT_DEVICEQUERYREMOVE QueryRemoveFailed = 0x8002, //DBT_DEVICEQUERYREMOVEFAILED RemovePending = 0x8003, //DBT_DEVICEREMOVEPENDING RemoveComplete = 0x8004, //DBT_DEVICEREMOVECOMPLETE Specific = 0x8005, //DBT_DEVICEREMOVECOMPLETE Custom = 0x8006 //DBT_CUSTOMEVENT } public enum DeviceType:int { OEM = 0x00000000, //DBT_DEVTYP_OEM DeviceNode = 0x00000001, //DBT_DEVTYP_DEVNODE Volume = 0x00000002, //DBT_DEVTYP_VOLUME Port = 0x00000003, //DBT_DEVTYP_PORT Net = 0x00000004 //DBT_DEVTYP_NET } public enum VolumeFlags:int { Media = 0x0001, //DBTF_MEDIA Net = 0x0002 //DBTF_NET } public struct BroadcastHeader //_DEV_BROADCAST_HDR { public int Size; //dbch_size public DeviceType Type; //dbch_devicetype private int Reserved; //dbch_reserved } public struct Volume //_DEV_BROADCAST_VOLUME { public int Size; //dbcv_size public DeviceType Type; //dbcv_devicetype private int Reserved; //dbcv_reserved public int Mask; //dbcv_unitmask public int Flags; //dbcv_flags } #endregion /// /// Constructor /// /// A DeviceVolumeMonitor instance that ownes the object /// The Windows handle to be used public _DeviceVolumeMonitor(DeviceVolumeMonitor aMonitor) { fMonitor = aMonitor; } /// /// WndProc method that traps all messages sent to the Handle /// /// A Windows message protected override void WndProc(ref Message aMessage) { BroadcastHeader lBroadcastHeader; Volume lVolume; DeviceEvent lEvent; base.WndProc(ref aMessage); Console.WriteLine(""+aMessage.Msg); if(aMessage.Msg==WM_DEVICECHANGE && fMonitor.Enabled) { lEvent = (DeviceEvent)aMessage.WParam.ToInt32(); //Select needed event // if(lEvent == 18) // { //MessageBox.Show(""+aMessage.WParam.ToInt32()); // } if (lEvent==DeviceEvent.Arrival || lEvent==DeviceEvent.RemoveComplete) { lBroadcastHeader = (BroadcastHeader)Marshal.PtrToStructure(aMessage.LParam,typeof(BroadcastHeader)); //Select needed device type if(lBroadcastHeader.Type==DeviceType.Volume) { lVolume = (Volume)Marshal.PtrToStructure(aMessage.LParam,typeof(Volume)); if((lVolume.Flags & (int)VolumeFlags.Media)!=0) { fMonitor.TriggerEvents(lEvent==DeviceEvent.Arrival,lVolume.Mask); } } } } } } /// /// DeviceVolumeMonitor class /// Derived from NativeWindow and implements IDisposable /// Built to monitor volume insertion and removal from devices which implement software ejection from removable media /// public class DeviceVolumeMonitor: IDisposable { /// /// Private fields /// _DeviceVolumeMonitor fInternal; IntPtr fHandle; bool fDisposed; bool fEnabled; bool fAsync; IntPtr handleNotification; /// /// Events /// These events are invoked on Volume insertion an removal /// public event DeviceVolumeAction OnVolumeInserted; public event DeviceVolumeAction OnVolumeRemoved; /// /// Enables or disables message trapping /// public bool Enabled { get { return fEnabled; } set { if(!fEnabled && value) { if (fInternal.Handle==IntPtr.Zero) { fInternal.AssignHandle(fHandle); } fEnabled = true; } if(fEnabled && !value) { if(fInternal.Handle!=IntPtr.Zero) { fInternal.ReleaseHandle(); } fEnabled = false; } } } /// /// Enables or disables asynchronous event calls /// public bool AsynchronousEvents { get { return fAsync; } set { fAsync = value; } } /// /// Constructor with no parameters /// This constructo can be used if the instance is created in a form. /// Caution: the form must already have an assigned Handle /// public DeviceVolumeMonitor() { if (Form.ActiveForm!=null) { fHandle = Form.ActiveForm.Handle; } else { throw new DeviceVolumeMonitorException("There is no active form!"); } _DeviceVolumeMonitor.Volume lVolume = new Staging._DeviceVolumeMonitor.Volume(); lVolume.Size = Marshal.SizeOf(typeof(_DeviceVolumeMonitor.Volume)); lVolume.Type = _DeviceVolumeMonitor.DeviceType.Volume; lVolume.Flags = (int)_DeviceVolumeMonitor.VolumeFlags.Media; //lVolume.Mask // IntPtr tmpIntPtr = IntPtr.Zero; // Marshal.StructureToPtr(lVolume,tmpIntPtr,true); handleNotification = RegisterDeviceNotification(fHandle,ref lVolume,0x00000000); Initialize(); } [DllImport("User32", CharSet=CharSet.Auto)] private static extern IntPtr RegisterDeviceNotification(IntPtr hRecipient,ref _DeviceVolumeMonitor.Volume NotificationFilter,int Flags); [DllImport("User32", CharSet=CharSet.Auto)] private static extern bool UnregisterDeviceNotification(IntPtr Handle); /// /// Preferred constructor, accepts a Window Handle as single parameter /// /// Window handle to be captured public DeviceVolumeMonitor(IntPtr aHandle) { if (aHandle!=IntPtr.Zero) { fHandle = aHandle; } else { throw new DeviceVolumeMonitorException("Invalid handle!"); } Initialize(); } /// /// Internal initialize method /// Sets all the private fields initial values and enables message trapping /// private void Initialize() { fInternal = new _DeviceVolumeMonitor(this); fDisposed = false; fEnabled = false; fAsync = false; Enabled = true; } /// /// Internal method used by _DeviceVolumeMonitor /// /// Flag set if volume inserted /// bit vector returned by the field dbcv_unitmask in the _DEV_BROADCAST_VOLUME structure internal void TriggerEvents(bool aInserted, int aMask) { if (AsynchronousEvents) { if(aInserted) { OnVolumeInserted.BeginInvoke(aMask,null,null); } else { OnVolumeRemoved.BeginInvoke(aMask,null,null); } } else { if(aInserted) { OnVolumeInserted(aMask); } else { OnVolumeRemoved(aMask); } } } /// /// Platform invoke the API function QueryDosDevice /// Fills aPath with the device path mapped to a DOS drive letter or device name in aName /// Returns the device path count (NOTE: used to retrieve a single device path) /// [DllImport("kernel32.dll", CharSet=CharSet.Auto)] private static extern int QueryDosDevice(string aName,[Out] StringBuilder aPath,int aCapacity); /// /// Returns a comma delimited string with all the drive letters in the bit vector parameter /// /// bit vector returned by the field dbcv_unitmask in the _DEV_BROADCAST_VOLUME structure /// public string MaskToLogicalPaths(int aMask) { int lMask = aMask; int lValue = 0; StringBuilder lReturn = new StringBuilder(128); try { lReturn = new StringBuilder(128); if(aMask>0) { for (;lMask!=0;lMask>>=1) { if ((lMask & 1)!=0) { lReturn.Append((char)(65+lValue)); lReturn.Append(":,"); } lValue ++; } lReturn.Remove(lReturn.Length-1,1); } return lReturn.ToString(); } finally { lReturn = null; } } /// /// Returns a comma delimited string with all the device paths in the bit vector parameter /// /// bit vector returned by the field dbcv_unitmask in the _DEV_BROADCAST_VOLUME structure /// public string MaskToDevicePaths(int aMask) { string[] lLogical = MaskToLogicalPaths(aMask).Split(Convert.ToChar(",")); StringBuilder lBuffer; StringBuilder lReturn; try { lBuffer = new StringBuilder(256); lReturn = new StringBuilder(256); foreach(string lPath in lLogical) { if (QueryDosDevice(lPath,lBuffer,lBuffer.Capacity)>0) { lReturn.Append(lBuffer.ToString()); lReturn.Append(","); } } lReturn.Remove(lReturn.Length-1,1); return lReturn.ToString(); } finally { lBuffer = null; lReturn = null; } } /// /// IDisposable implementation acording to the preferred design pattern /// public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool aDisposing) { if(!this.fDisposed) { if(fInternal.Handle!=IntPtr.Zero) { fInternal.ReleaseHandle(); fInternal = null; } } fDisposed = true; } ~DeviceVolumeMonitor() { UnregisterDeviceNotification(handleNotification); Dispose(false); } } }