Click here to Skip to main content
15,898,222 members
Articles / Programming Languages / C++
Article

Intercept the insertion/removal of a CD/DVD

Rate me:
Please Sign up or sign in to vote.
3.37/5 (11 votes)
8 Apr 20051 min read 44.8K   16   7
How to intercept a new media in the drive, using WM_DEVICECHANGE message.

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.

C++
WindowProc=FormWndProc;

The function is defined as:

C++
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:

C++
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:

C++
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: ;
    }
}

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
Italy Italy
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionCopy of MS Article Pin
Msftone20-Mar-07 9:11
Msftone20-Mar-07 9:11 
GeneralWM_DEVICECHANGE & IOMega ZIP Drive Pin
JimmyO18-Jan-07 3:11
JimmyO18-Jan-07 3:11 
Generalprevent windows from reading the CD/DVD Pin
Darin.sh20-Aug-05 22:57
Darin.sh20-Aug-05 22:57 
GeneralCBuilder article Pin
yarp8-Apr-05 19:20
yarp8-Apr-05 19:20 
GeneralRe: CBuilder article Pin
shaman7414-Apr-05 21:01
shaman7414-Apr-05 21:01 
GeneralRe: CBuilder article Pin
yarp15-Apr-05 2:13
yarp15-Apr-05 2:13 
GeneralGood job Pin
Juha Silmujarvi8-Apr-05 6:49
Juha Silmujarvi8-Apr-05 6:49 
Good Job, I found these issues rather tricky, when I was thinking them.
But the solution was rather simple when I found the needed information.
So here is rather same thing in Visual C/C++ & MFC (Visual Studio 6).

Some years ago I made an app that was possibly running from CD.
When ejecting CD from drive, the app must be shutdown if it is running from removed CD
or we will have severe problems in the future.

Here is the code how I detected removal of the CD and exited the app.
Of course, if you want, you can detect when something is inserted too.

Application was Dialog based and the code was in the dialog's class.

Maybe someone will find this helpfull...or maybe not.


The Header:

----MyProgDlg.h----
:
:
:

// Implementation
protected:
HICON m_hIcon;

// Generated message map functions
//{{AFX_MSG(CMyProgDlg)


afx_msg BOOL OnDeviceChange( UINT nEventType, DWORD dwData );

//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};

----EOF MyProgDlg.h----



And the cpp:

----MyProgDlg.cpp----

:
:
:
:

BEGIN_MESSAGE_MAP(CMyProgDlg, CDialog)
//{{AFX_MSG_MAP(CMyProgDlg)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
:
:
:
ON_WM_DEVICECHANGE()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
:
:
:
:
:
:
:
:
:

BOOL CMyProgDlg::OnDeviceChange( UINT nEventType, DWORD dwData ){



if(nEventType==DBT_DEVICEQUERYREMOVE || nEventType==DBT_DEVICEREMOVECOMPLETE || nEventType==DBT_DEVICEREMOVEPENDING){


if(m_appsettings.IsExeInCD()){
PDEV_BROADCAST_HDR pDBHDR= (PDEV_BROADCAST_HDR) dwData;
PDEV_BROADCAST_VOLUME pBRVOL;

if(pDBHDR->dbch_devicetype == DBT_DEVTYP_VOLUME){ //Volume removed
pBRVOL=(PDEV_BROADCAST_VOLUME) pDBHDR;
unsigned char chA='A'; //A-drive
unsigned char chCD=(unsigned char) * m_appsettings.GetDrive(); //CD-drive where this prog is running from
//This was detected during the startup of the app
if(chCD > 90) //if driveletter is uppercase, do it lowercase
chCD-=32;
unsigned char chDrvDiff=chCD-chA; //difference of driveletters

DWORD dwExpResult=1;
dwExpResult= dwExpResult << chDrvDiff;

if(dwExpResult & pBRVOL->dbcv_unitmask){ //is masks ar the very same
//then prog is running from cd that was just removed
::PostQuitMessage(0); //Oh Oh, the drive where this exe is running from was removed...so we quit immediatelly and
return TRUE;
}

}

}
}
return TRUE;
}

:
:
:
----EOF MyProgDlg.cpp----

--------------
Just do it

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.