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);
}
}
}