Introduction
While working (with Borland C++), I've found myself in the trouble of wiritng write an application that reacts to the insertion/removal of a media (CD/DVD) in one of the PC's drives.
The Event
After some surfing on the web, I found that the best way to do this was to use a handler to a specific Windows message, WM_DEVICECHANGE
, that is sent by the system when a medium is inserted or removed, as specified in an MSDN page.
The Handler
I've used the WindosProc
function to override the default message handler of the application, WndProc
, to manage the WM_DEVICECHANGE
message. The first thing to do is to redirect the WindowProc
function to our message handler, called FormWndProc
.
WindowProc=FormWndProc;
The function is defined as:
void __fastcall TFormSpyMsg::FormWndProc(Messages::TMessage &Message)
{
switch (Message.Msg)
{
case WM_DEVICECHANGE:
handle_WM_DEVICECHANGE(Message);
break;
default:
WndProc(Message);
}
}
It writes down the handler and is used to handle the WM_DEVICEMESSAGE
message. To manage both cases, insertion and removal, we have to understand the parameters of the message we receive, specified in TMessage.LParam
. This is a pointer to a DEV_BROADCAST_HDR
structure:
typedef struct DEV_BROADCAST_HDR
{
DWORD dbch_size;
DWORD dbch_devicetype;
DWORD dbch_reserved;
}
DEV_BROADCAST_HDR,
*PDEV_BROADCAST_HDR;
The media type is defined in dbch_devicetype
. With another cast, from PDEV_BROADCAST_HDR
to PDEV_BROADCAST_VOLUME
, we could retrieve the flags that identify the media, lpdbv -> dbcv_flags
. The identifier of the drive that has been changed is specified in lpdbv ->dbcv_unitmask
. To decode this value, I've used a function found on the MSDN page:
char FirstDriveFromMask (ULONG unitmask)
{
char i;
for (i = 0; i < 26; ++i)
{
if (unitmask & 0x1) break;
unitmask = unitmask >> 1;
}
return (i + 'A');
}
The handler is coded below:
void TFormSpyMsg::handle_WM_DEVICECHANGE(TMessage Msg)
{
PDEV_BROADCAST_HDR lpdb = (PDEV_BROADCAST_HDR)Msg.LParam;
AnsiString smsg;
char drive;
switch(Msg.WParam)
{
case DBT_DEVICEARRIVAL:
if (lpdb -> dbch_devicetype == DBT_DEVTYP_VOLUME)
{
PDEV_BROADCAST_VOLUME lpdbv = (PDEV_BROADCAST_VOLUME)lpdb;
if (lpdbv -> dbcv_flags & DBTF_MEDIA)
{
drive = FirstDriveFromMask(lpdbv ->dbcv_unitmask);
smsg = "Device inserte in drive " +
AnsiString(drive);
Memo1->Lines->Add(smsg);
}
}
break;
case DBT_DEVICEREMOVECOMPLETE:
if (lpdb -> dbch_devicetype == DBT_DEVTYP_VOLUME)
{
PDEV_BROADCAST_VOLUME lpdbv = (PDEV_BROADCAST_VOLUME)lpdb;
if (lpdbv -> dbcv_flags & DBTF_MEDIA)
{
drive = FirstDriveFromMask(lpdbv ->dbcv_unitmask);
smsg = "Device removed from drive " +
AnsiString(drive);
Memo1->Lines->Add(smsg);
}
}
break;
default: ;
}
}