How can I detect cdrom/usb device insertions?

P

Paul Steele

I am writing a C# app that needs to periodically poll for cdroms and usb
storage device insertions. I've looked at the WMI functions but haven't
found anything all that useful. The closest is Win32_DiskDrive, but it
doesn't seem to return any information on cdrom devices. I suspect there
might be a Win32 API call, but I haven't found any info yet using Google. Is
there a way to achieve this in C#?
 
G

Guest

Hi,
I'm not sure whether you can do it in C#, i.e you have any direct classes
for this, but you have Win32 API's for detecting the cdroms insertions. The
following data may be helpful to you. Courtesy, MSDN

Detecting Media Insertion or Removal
Windows sends all top-level windows a set of default WM_DEVICECHANGE
messages when new devices or media (such as a CD or DVD) are added and become
available, and when existing devices or media are removed. You do not need to
register to receive these default messages. See the Remarks section in
RegisterDeviceNotification for details on which messages are sent by default.
The messages in the code example below are among the default messages.

Each WM_DEVICECHANGE message has an associated event that describes the
change, and a structure that provides detailed information about the change.
The structure consists of an event-independent header, DEV_BROADCAST_HDR,
followed by event-dependent members. The event-dependent members describe the
device to which the event applies. To use this structure, applications must
first determine the event type and the device type. Then, they can use the
correct structure to take appropriate action.


When the user inserts a new CD or DVD into a drive, applications receive a
WM_DEVICECHANGE message with a DBT_DEVICEARRIVAL event. The application must
check the event to ensure that the type of device arriving is a volume (the
dbch_devicetype member is DBT_DEVTYP_VOLUME) and that the change affects the
media (the dbcv_flags member is DBTF_MEDIA).

When the user removes a CD or DVD from a drive, applications receive a
WM_DEVICECHANGE message with a DBT_DEVICEREMOVECOMPLETE event. Again, the
application must check the event to ensure that the device being removed is a
volume and that the change affects the media.

The following code demonstrates how to check for insertion or removal of a
CD or DVD.


#include <windows.h>
#include <dbt.h>

void Main_OnDeviceChange (HWND hwnd, WPARAM wParam, LPARAM lParam);
char FirstDriveFromMask (ULONG unitmask); //prototype

/*------------------------------------------------------------------
Main_OnDeviceChange (hwnd, wParam, lParam)

Description
Handles WM_DEVICECHANGE messages sent to the application's
top-level window.
--------------------------------------------------------------------*/

void Main_OnDeviceChange (HWND hwnd, WPARAM wParam, LPARAM lParam)
{
PDEV_BROADCAST_HDR lpdb = (PDEV_BROADCAST_HDR)lParam;
char szMsg[80];

switch(wParam)
{
case DBT_DEVICEARRIVAL:
// Check whether a CD or DVD was inserted into a drive.
if (lpdb -> dbch_devicetype == DBT_DEVTYP_VOLUME)
{
PDEV_BROADCAST_VOLUME lpdbv = (PDEV_BROADCAST_VOLUME)lpdb;

if (lpdbv -> dbcv_flags & DBTF_MEDIA)
{
wsprintf (szMsg, "Drive %c: Media has arrived.\n",
FirstDriveFromMask(lpdbv ->dbcv_unitmask));

MessageBox (hwnd, szMsg, "WM_DEVICECHANGE", MB_OK);
}
}
break;

case DBT_DEVICEREMOVECOMPLETE:
// Check whether a CD or DVD was removed from a drive.
if (lpdb -> dbch_devicetype == DBT_DEVTYP_VOLUME)
{
PDEV_BROADCAST_VOLUME lpdbv = (PDEV_BROADCAST_VOLUME)lpdb;

if (lpdbv -> dbcv_flags & DBTF_MEDIA)
{
wsprintf (szMsg, "Drive %c: Media was removed.\n",
FirstDriveFromMask(lpdbv ->dbcv_unitmask));

MessageBox (hwnd, szMsg, "WM_DEVICECHANGE", MB_OK);
}
}
break;

default:
/*
Process other WM_DEVICECHANGE notifications for other
devices or reasons.
*/
;
}
}

/*------------------------------------------------------------------
FirstDriveFromMask (unitmask)

Description
Finds the first valid drive letter from a mask of drive letters.
The mask must be in the format bit 0 = A, bit 1 = B, bit 3 = C,
etc. A valid drive letter is defined when the corresponding bit
is set to 1.

Returns the first drive letter that was found.
--------------------------------------------------------------------*/

char FirstDriveFromMask (ULONG unitmask)
{
char i;

for (i = 0; i < 26; ++i)
{
if (unitmask & 0x1)
break;
unitmask = unitmask >> 1;
}

return (i + 'A');
}

Pradeep Kumar.R
 
P

Paul Steele

This is definitely a good starting point. Unfortunately it does not detect
USB key insertions...
 
P

Paul Steele

I presume you are referring to the comment:

"The first one is dbcv_flags. This field will tell us the nature of the
volume that was mounted (or dismounted). It can be either a media volume
like a CD (the flag will have a DBTF_MEDIA flag) or a network share
(DBTF_NET). In this case we will only be interested in filtering the
DBTF_MEDIA value. The other field is dbcv_unitmask, which is a bit vector
that tells us which drive letters were mounted."

If I remove the check for DBTF_MEDIA in the code, then it does detect USB
keys. Unfortunately it also detects network share mounts. There doesn't seem
to be a differentiation between a USB Key and network media. Am I
overlooking something?
 
W

Willy Denoyette [MVP]

Paul Steele said:
I am writing a C# app that needs to periodically poll for cdroms and usb
storage device insertions. I've looked at the WMI functions but haven't
found anything all that useful. The closest is Win32_DiskDrive, but it
doesn't seem to return any information on cdrom devices. I suspect there
might be a Win32 API call, but I haven't found any info yet using Google.
Is there a way to achieve this in C#?

Use 'Win32_LogicalDisk' instead of Win32_DiskDrive and register a
asynchronous event handler for __InstanceModificationEvent, something like
this will do.

using System.Management;
class TestClass{
public static void Main() {
TestClasswe = new TestClass();
ManagementEventWatcher w= null;
WqlEventQuery q = new WqlEventQuery();
ManagementScope scope = new ManagementScope("root\\CIMV2");
try {
q.EventClassName = "__InstanceModificationEvent";
q.WithinInterval = new TimeSpan(0,0,3);
// DriveType - 5: CDROM
q.Condition = @"TargetInstance ISA 'Win32_LogicalDisk' and
TargetInstance.DriveType = 5 and TargetInstance.VolumeName != null";
w = new ManagementEventWatcher(scope, q);
// register async. event handler
w.EventArrived += new EventArrivedEventHandler(we.CDREventArrived);
w.Start();
// Do something usefull,block thread for testing
Console.ReadLine();
}
catch(Exception e) {
Console.WriteLine(e.Message);
}
finally {
w.Stop();
}
}
// Dump some properties
public void CDREventArrived(object sender, EventArrivedEventArgs e) {
//Get the Event object and display it
PropertyData pd;
if(( pd = e.NewEvent.Properties["TargetInstance"]) != null)
{
ManagementBaseObject mbo = pd.Value as ManagementBaseObject;
Console.WriteLine(mbo.Properties["VolumeName"].Value);
}

Willy.
 
P

Paul Steele

I put in your code more or less as given but the event never fires. I'm not
familiar with ManagementEventWatcher, which makes the code somewhat vague,
although I get the general idea behind it. Is there something I can do to
test it?

Willy Denoyette said:
Paul Steele said:
I am writing a C# app that needs to periodically poll for cdroms and usb
storage device insertions. I've looked at the WMI functions but haven't
found anything all that useful. The closest is Win32_DiskDrive, but it
doesn't seem to return any information on cdrom devices. I suspect there
might be a Win32 API call, but I haven't found any info yet using Google.
Is there a way to achieve this in C#?

Use 'Win32_LogicalDisk' instead of Win32_DiskDrive and register a
asynchronous event handler for __InstanceModificationEvent, something like
this will do.

using System.Management;
class TestClass{
public static void Main() {
TestClasswe = new TestClass();
ManagementEventWatcher w= null;
WqlEventQuery q = new WqlEventQuery();
ManagementScope scope = new ManagementScope("root\\CIMV2");
try {
q.EventClassName = "__InstanceModificationEvent";
q.WithinInterval = new TimeSpan(0,0,3);
// DriveType - 5: CDROM
q.Condition = @"TargetInstance ISA 'Win32_LogicalDisk' and
TargetInstance.DriveType = 5 and TargetInstance.VolumeName != null";
w = new ManagementEventWatcher(scope, q);
// register async. event handler
w.EventArrived += new EventArrivedEventHandler(we.CDREventArrived);
w.Start();
// Do something usefull,block thread for testing
Console.ReadLine();
}
catch(Exception e) {
Console.WriteLine(e.Message);
}
finally {
w.Stop();
}
}
// Dump some properties
public void CDREventArrived(object sender, EventArrivedEventArgs e) {
//Get the Event object and display it
PropertyData pd;
if(( pd = e.NewEvent.Properties["TargetInstance"]) != null)
{
ManagementBaseObject mbo = pd.Value as ManagementBaseObject;
Console.WriteLine(mbo.Properties["VolumeName"].Value);
}

Willy.
 
P

Paul Steele

This code is basically the same idea behind the CodeProject link Alexander
Shirshov pointed me to. Is there a device type I should be checking for that
would identify other removable media such as USB keys. DBTF_MEDIA refers to
CD/DVD drives, while DBTF_NET refers to network devices as well as USB keys
it would appear from my testing. I want to ignore network drives...

Pradeep Kumar said:
Hi,
I'm not sure whether you can do it in C#, i.e you have any direct classes
for this, but you have Win32 API's for detecting the cdroms insertions.
The
following data may be helpful to you. Courtesy, MSDN

Detecting Media Insertion or Removal
Windows sends all top-level windows a set of default WM_DEVICECHANGE
messages when new devices or media (such as a CD or DVD) are added and
become
available, and when existing devices or media are removed. You do not need
to
register to receive these default messages. See the Remarks section in
RegisterDeviceNotification for details on which messages are sent by
default.
The messages in the code example below are among the default messages.

Each WM_DEVICECHANGE message has an associated event that describes the
change, and a structure that provides detailed information about the
change.
The structure consists of an event-independent header, DEV_BROADCAST_HDR,
followed by event-dependent members. The event-dependent members describe
the
device to which the event applies. To use this structure, applications
must
first determine the event type and the device type. Then, they can use the
correct structure to take appropriate action.


When the user inserts a new CD or DVD into a drive, applications receive a
WM_DEVICECHANGE message with a DBT_DEVICEARRIVAL event. The application
must
check the event to ensure that the type of device arriving is a volume
(the
dbch_devicetype member is DBT_DEVTYP_VOLUME) and that the change affects
the
media (the dbcv_flags member is DBTF_MEDIA).

When the user removes a CD or DVD from a drive, applications receive a
WM_DEVICECHANGE message with a DBT_DEVICEREMOVECOMPLETE event. Again, the
application must check the event to ensure that the device being removed
is a
volume and that the change affects the media.

The following code demonstrates how to check for insertion or removal of a
CD or DVD.


#include <windows.h>
#include <dbt.h>

void Main_OnDeviceChange (HWND hwnd, WPARAM wParam, LPARAM lParam);
char FirstDriveFromMask (ULONG unitmask); //prototype

/*------------------------------------------------------------------
Main_OnDeviceChange (hwnd, wParam, lParam)

Description
Handles WM_DEVICECHANGE messages sent to the application's
top-level window.
--------------------------------------------------------------------*/

void Main_OnDeviceChange (HWND hwnd, WPARAM wParam, LPARAM lParam)
{
PDEV_BROADCAST_HDR lpdb = (PDEV_BROADCAST_HDR)lParam;
char szMsg[80];

switch(wParam)
{
case DBT_DEVICEARRIVAL:
// Check whether a CD or DVD was inserted into a drive.
if (lpdb -> dbch_devicetype == DBT_DEVTYP_VOLUME)
{
PDEV_BROADCAST_VOLUME lpdbv = (PDEV_BROADCAST_VOLUME)lpdb;

if (lpdbv -> dbcv_flags & DBTF_MEDIA)
{
wsprintf (szMsg, "Drive %c: Media has arrived.\n",
FirstDriveFromMask(lpdbv ->dbcv_unitmask));

MessageBox (hwnd, szMsg, "WM_DEVICECHANGE", MB_OK);
}
}
break;

case DBT_DEVICEREMOVECOMPLETE:
// Check whether a CD or DVD was removed from a drive.
if (lpdb -> dbch_devicetype == DBT_DEVTYP_VOLUME)
{
PDEV_BROADCAST_VOLUME lpdbv = (PDEV_BROADCAST_VOLUME)lpdb;

if (lpdbv -> dbcv_flags & DBTF_MEDIA)
{
wsprintf (szMsg, "Drive %c: Media was removed.\n",
FirstDriveFromMask(lpdbv ->dbcv_unitmask));

MessageBox (hwnd, szMsg, "WM_DEVICECHANGE", MB_OK);
}
}
break;

default:
/*
Process other WM_DEVICECHANGE notifications for other
devices or reasons.
*/
;
}
}

/*------------------------------------------------------------------
FirstDriveFromMask (unitmask)

Description
Finds the first valid drive letter from a mask of drive letters.
The mask must be in the format bit 0 = A, bit 1 = B, bit 3 = C,
etc. A valid drive letter is defined when the corresponding bit
is set to 1.

Returns the first drive letter that was found.
--------------------------------------------------------------------*/

char FirstDriveFromMask (ULONG unitmask)
{
char i;

for (i = 0; i < 26; ++i)
{
if (unitmask & 0x1)
break;
unitmask = unitmask >> 1;
}

return (i + 'A');
}

Pradeep Kumar.R

Paul Steele said:
I am writing a C# app that needs to periodically poll for cdroms and usb
storage device insertions. I've looked at the WMI functions but haven't
found anything all that useful. The closest is Win32_DiskDrive, but it
doesn't seem to return any information on cdrom devices. I suspect there
might be a Win32 API call, but I haven't found any info yet using Google.
Is
there a way to achieve this in C#?
 
W

Willy Denoyette [MVP]

Paul Steele said:
I put in your code more or less as given but the event never fires. I'm not
familiar with ManagementEventWatcher, which makes the code somewhat vague,
although I get the general idea behind it. Is there something I can do to
test it?

Change the query string into:
q.Condition = @"TargetInstance ISA 'Win32_LogicalDisk' and
TargetInstance.DriveType = 5";
, and insert and/or remove the CD into/from the drive.
With this change all modifications to to 'Win32_LogicalDisk' will fire an
event, while the original query will only fire when a named volume is
inserted.

Willy.
 
P

Paul Steele

Willy Denoyette said:
Change the query string into:
q.Condition = @"TargetInstance ISA 'Win32_LogicalDisk' and
TargetInstance.DriveType = 5";
, and insert and/or remove the CD into/from the drive.
With this change all modifications to to 'Win32_LogicalDisk' will fire
an event, while the original query will only fire when a named volume is
inserted.

Found the problem. I didn't notice that "finally" clause in the try/catch
was executing. I has removed the readline because I moved to code to a
button. Stupid mistake and the program does seem to work. There is one weird
side effect. On my desktop a floppy seek occurs every 3 seconds, probably
because the event requires the OS to query all logical devices, including
the floppy. It's too bad it can't make a silent check for the floppy...
 
W

Willy Denoyette [MVP]

Paul Steele said:
Found the problem. I didn't notice that "finally" clause in the try/catch
was executing. I has removed the readline because I moved to code to a
button. Stupid mistake and the program does seem to work. There is one
weird side effect. On my desktop a floppy seek occurs every 3 seconds,
probably because the event requires the OS to query all logical devices,
including the floppy. It's too bad it can't make a silent check for the
floppy...

You can prevent this by including the DeviceId in the query.

q.Condition = @"TargetInstance ISA 'Win32_LogicalDisk' and
TargetInstance.DriveType = 5 and TargetInstance.DeviceId='E:'" // where E:
is the drive letter of the CD drive

Willy.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top