Discussion:
RegisterDeviceNotification() from within a service
(too old to reply)
Jim Cavalaris [MS]
2003-07-14 18:23:14 UTC
Permalink
you cannot register for notification of DBT_DEVTYP_VOLUME events.
RegisterDeviceNotification will always fail for this type of
notification filter. DBT_DEVTYP_VOLUME type messages are
automatically broadcast to top-level windows only, for compatibility
with Windows 9x systems. they are not sent to NT services.

for a service to receive any device events at all via its control
handler, the service must register a HandlerEx control handler
using RegisterServiceCtrlHandlerEx:

HandlerEx:
http://msdn.microsoft.com/library/en-us/dllproc/base/handlerex.asp

RegisterServiceCtrlHandlerEx:
http://msdn.microsoft.com/library/en-us/dllproc/base/registerservicectrlhandlerex.asp

to receive volume arrival and removal events, register for
device interface notification using the volume device interface
class GUID.

use a DEV_BROADCAST_DEVICEINTERFACE structure for the
NotificationFilter (dbcc_devicetype must be set to
DBT_DEVTYP_DEVICEINTERFACE), and set the dbcc_classguid field to
GUID_DEVINTERFACE_VOLUME.

on volume arrivals and removals, you service's HandlerEx routine
will receive SERVICE_CONTROL_DEVICEEVENT controls with an EventType
of DBT_DEVICEARRIVAL or DBT_DEVICEREMOVECOMPLETE. check the type
of the accompanying data structure to verify it is the expected
DBT_DEVTYP_DEVICEINTERFACE. the dbcc_name field of the
DEV_BROADCAST_DEVICEINTERFACE strcuture will specify a path that
can be used to access the volume (note that this path is always
supplied as a Unicode string, even for services compiled as ANSI.)

hope this helps,
jim.
--
This posting is provided "AS IS" with no warranties, and confers no rights.
I have been trying and failing to use RegisterDeviceNotification() from
within a service on Win2K. I am calling it as prescribed to connect to
//dbdi is a DEV_BROADCAST_VOLUME
memset(&dbdi, 0, sizeof(dbdi));
dbdi.dbcv_size = sizeof(dbdi);
dbdi.dbcv_devicetype = DBT_DEVTYP_VOLUME;
dbdi.dbcv_unitmask = (1 << 3) | (1 << 5); //get drives D and F for
testing
.. these are removable (cdrom and zip) drives on my system
//twiddling this value has not helped ...
dbdi.dbcv_flags = DBTF_MEDIA;
m_hDevNotify = RegisterDeviceNotification(m_hServiceStatus, &dbdi,
DEVICE_NOTIFY_SERVICE_HANDLE);
//always returns zero...
if(!m_hDevNotify)
DWORD dwErr = GetLastError();
dwErr is always 13, ERROR_INVALID_DATA.
Is there anyone on planet Earth who has successfully called this function
from inside a *service* EXE?
Rufus DeDufus
2003-07-16 07:47:43 UTC
Permalink
By the way, how do I get a device class interface GUID starting with, say
"d:" ?

Eric
Thanks.
Comments in the text.
Post by Jim Cavalaris [MS]
you cannot register for notification of DBT_DEVTYP_VOLUME events.
RegisterDeviceNotification will always fail for this type of
notification filter. DBT_DEVTYP_VOLUME type messages are
automatically broadcast to top-level windows only, for compatibility
with Windows 9x systems. they are not sent to NT services.
for a service to receive any device events at all via its control
handler, the service must register a HandlerEx control handler
http://msdn.microsoft.com/library/en-us/dllproc/base/handlerex.asp
http://msdn.microsoft.com/library/en-us/dllproc/base/registerservicectrlhand
lerex.asp
Post by Jim Cavalaris [MS]
to receive volume arrival and removal events, register for
device interface notification using the volume device interface
class GUID.
use a DEV_BROADCAST_DEVICEINTERFACE structure for the
NotificationFilter (dbcc_devicetype must be set to
DBT_DEVTYP_DEVICEINTERFACE), and set the dbcc_classguid field to
GUID_DEVINTERFACE_VOLUME.
OK, this works by some miracle ( I used GUID_DEVCLASS_CDROM ) but the docs
page for RegisterDeviceNotification(),
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/devio/base/
This
value is not supported.
But it returns a valid handle on Win2K?!? This is why I did not try this
sooner.
Now, I cannot get messages when I remove the device. I have called
SetServiceStatus() with dwControlsAccepted = SERVICE_ACCEPT_STOP |
SERVICE_ACCEPT_HARDWAREPROFILECHANGE which should enable it to get that
message (this is also a little unclear in the docs) but still no message.
I am reduced to guessing if the GUID is right - I have used the GUID for
MEDIA and VOLUME as well as CDROM with the same outcome : nice looking
handle but media change does not get me a message. So, what could it be?
Device name? I have tried "d:", \\?\d:, "d:\" ... and on and on.
Any ideas? You got me one step further, I have a handle now.
Thanks,
Eric
m_status.dwControlsAccepted = SERVICE_ACCEPT_STOP |
SERVICE_ACCEPT_HARDWAREPROFILECHANGE;
....
m_status.dwCurrentState = SERVICE_START_PENDING;
m_hServiceStatus = RegisterServiceCtrlHandlerEx(m_szServiceName,
_HandlerEx, 0);
if (m_hServiceStatus == NULL)
{
LogEvent(_T("Handler not installed"));
return;
}
//this uses the dwControlsAccepted set earlier, yes ..
SetServiceStatus(SERVICE_START_PENDING);
//just a nice big block of bits, plenty big enuf
memset(&dbdi, 0, sizeof(dbdi));
DEV_BROADCAST_DEVICEINTERFACE * p = (DEV_BROADCAST_DEVICEINTERFACE
*)&dbdi;
p->dbcc_size = sizeof(dbdi);
p->dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
_tcscpy(p->dbcc_name, _T("d:\\"));
p->dbcc_classguid = GUID_DEVCLASS_VOLUME;
//this returns non-null handle
m_hDevNotify = RegisterDeviceNotification(m_hServiceStatus, p,
DEVICE_NOTIFY_SERVICE_HANDLE);
Post by Jim Cavalaris [MS]
on volume arrivals and removals, you service's HandlerEx routine
will receive SERVICE_CONTROL_DEVICEEVENT controls with an EventType
of DBT_DEVICEARRIVAL or DBT_DEVICEREMOVECOMPLETE. check the type
of the accompanying data structure to verify it is the expected
DBT_DEVTYP_DEVICEINTERFACE. the dbcc_name field of the
DEV_BROADCAST_DEVICEINTERFACE strcuture will specify a path that
can be used to access the volume (note that this path is always
supplied as a Unicode string, even for services compiled as ANSI.)
hope this helps,
jim.
--
This posting is provided "AS IS" with no warranties, and confers no
rights.
Post by Jim Cavalaris [MS]
I have been trying and failing to use RegisterDeviceNotification() from
within a service on Win2K. I am calling it as prescribed to connect to
//dbdi is a DEV_BROADCAST_VOLUME
memset(&dbdi, 0, sizeof(dbdi));
dbdi.dbcv_size = sizeof(dbdi);
dbdi.dbcv_devicetype = DBT_DEVTYP_VOLUME;
dbdi.dbcv_unitmask = (1 << 3) | (1 << 5); //get drives D and F for
testing
.. these are removable (cdrom and zip) drives on my system
//twiddling this value has not helped ...
dbdi.dbcv_flags = DBTF_MEDIA;
m_hDevNotify = RegisterDeviceNotification(m_hServiceStatus, &dbdi,
DEVICE_NOTIFY_SERVICE_HANDLE);
//always returns zero...
if(!m_hDevNotify)
DWORD dwErr = GetLastError();
dwErr is always 13, ERROR_INVALID_DATA.
Is there anyone on planet Earth who has successfully called this
function
Post by Jim Cavalaris [MS]
from inside a *service* EXE?
Rufus DeDufus
2003-07-17 07:26:05 UTC
Permalink
For the sake of the search engines and others who need the answer, here it
is ... no error checking ...

//dbdi is a DEV_BROADCAST_HANDLE ...
memset(&dbdi, 0, sizeof(dbdi));

dbdi.dbch_size = sizeof(dbdi);
dbdi.dbch_devicetype = DBT_DEVTYP_HANDLE;
dbdi.dbch_handle = CreateFile(_T("\\\\?\\c:"), FILE_LIST_DIRECTORY,
FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, 0);

m_hDevNotify = RegisterDeviceNotification(m_hServiceStatus, &dbdi,
DEVICE_NOTIFY_SERVICE_HANDLE);


Then down in HandlerEx():

DEV_BROADCAST_HANDLE * p = 0;
DebugBreak();

switch (dwOpcode)
{
case SERVICE_CONTROL_DEVICEEVENT:
p = (DEV_BROADCAST_HANDLE *)pvEvtData;
if(p->dbch_devicetype != DBT_DEVTYP_HANDLE)
return NO_ERROR;

if(IsEqualGUID(GUID_IO_MEDIA_REMOVAL, p->dbch_eventguid))
{
//do work ..
return NO_ERROR;
}

It was not in the *least* straightforward, and thanks for your help.
Jim Cavalaris [MS]
2003-07-22 20:01:16 UTC
Permalink
GUID_DEVCLASS_CDROM is the GUID representing the device setup class
for CD-ROM devices, not the device interface class. the device
interface class GUID that is registered for CD-ROM devices is
GUID_DEVINTERFACE_CDROM, which should be defined in winioctl.h.
this is the GUID you should use with RegisterDeviceNotification
to register for DBT_DEVTYP_DEVICEINTERFACE arrival and removal
events for CD-ROM devices. you do not need to specify any special
SERVICE_ACCEPT_* flags to receive device events, just make sure
you have registered a HandlerEx type handler, as i mentioned
previously. also, do not copy any string in the dbcc_name field; this
field is only used when you receive a notification event, to describe
the device interface that the event is for.

many device interface class GUID definitions were renamed in the
Windows XP DDK and later to use a GUID_DEVINTERFACE_* naming
convention. this was done specifically to avoid confusing these
with device setup classes (which have always had GUID_DEVCLASS_*
names). in previous versions of the DDK, GUID_DEVINTERFACE_CDROM
is defined as CdRomClassGuid.

the following docs should help explain the differences between
device setup classes and device interface classes.

Setup Classes Versus Interface Classes:
http://msdn.microsoft.com/library/en-us/install/hh/install/setup-cls_39wn.asp

Introduction to Device Interfaces:
http://msdn.microsoft.com/library/en-us/install/hh/install/setup-cls_8vs7.asp

hope this helps,
jim.
--
This posting is provided "AS IS" with no warranties, and confers no rights.
Thanks.
Comments in the text.
OK, this works by some miracle ( I used GUID_DEVCLASS_CDROM ) but the docs
page for RegisterDeviceNotification(),
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/devio/base/
This
value is not supported.
But it returns a valid handle on Win2K?!? This is why I did not try this
sooner.
Now, I cannot get messages when I remove the device. I have called
SetServiceStatus() with dwControlsAccepted = SERVICE_ACCEPT_STOP |
SERVICE_ACCEPT_HARDWAREPROFILECHANGE which should enable it to get that
message (this is also a little unclear in the docs) but still no message.
I am reduced to guessing if the GUID is right - I have used the GUID for
MEDIA and VOLUME as well as CDROM with the same outcome : nice looking
handle but media change does not get me a message. So, what could it be?
Device name? I have tried "d:", \\?\d:, "d:\" ... and on and on.
Any ideas? You got me one step further, I have a handle now.
Thanks,
Eric
m_status.dwControlsAccepted = SERVICE_ACCEPT_STOP |
SERVICE_ACCEPT_HARDWAREPROFILECHANGE;
....
m_status.dwCurrentState = SERVICE_START_PENDING;
m_hServiceStatus = RegisterServiceCtrlHandlerEx(m_szServiceName,
_HandlerEx, 0);
if (m_hServiceStatus == NULL)
{
LogEvent(_T("Handler not installed"));
return;
}
//this uses the dwControlsAccepted set earlier, yes ..
SetServiceStatus(SERVICE_START_PENDING);
//just a nice big block of bits, plenty big enuf
memset(&dbdi, 0, sizeof(dbdi));
DEV_BROADCAST_DEVICEINTERFACE * p = (DEV_BROADCAST_DEVICEINTERFACE
*)&dbdi;
p->dbcc_size = sizeof(dbdi);
p->dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
_tcscpy(p->dbcc_name, _T("d:\\"));
p->dbcc_classguid = GUID_DEVCLASS_VOLUME;
//this returns non-null handle
m_hDevNotify = RegisterDeviceNotification(m_hServiceStatus, p,
DEVICE_NOTIFY_SERVICE_HANDLE);
Rufus DeDufus
2003-07-24 22:10:01 UTC
Permalink
Post by Jim Cavalaris [MS]
GUID_DEVCLASS_CDROM is the GUID representing the device setup class
for CD-ROM devices, not the device interface class. the device
interface class GUID that is registered for CD-ROM devices is
GUID_DEVINTERFACE_CDROM, which should be defined in winioctl.h.
this is the GUID you should use with RegisterDeviceNotification
to register for DBT_DEVTYP_DEVICEINTERFACE arrival and removal
events for CD-ROM devices. [ .....]
Jim, I am finding only
DEFINE_GUID(CdRomClassGuid, 0x53f56308L, 0xb6bf, 0x11d0, 0x94,
0xf2, 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b);

in that file ... I take it this is what you mean - ?
Rufus DeDufus
2003-07-25 06:34:03 UTC
Permalink
He's baaaaack ..... :-o

I am still unable to get this to work. Now I get a valid handle from
RegisterDeviceNotification() but no events are sent.

m_hServiceStatus = RegisterServiceCtrlHandlerEx(m_szServiceName, _HandlerEx,
0);
//good status handle here ..


//dbdi_buf is just a slab of bits big enuf for our work ...
memset(&dbdi_buf, 0, sizeof(dbdi_buf));
pdbdi = (DEV_BROADCAST_DEVICEINTERFACE*)&dbdi_buf;

pdbdi->dbcc_size = sizeof(dbdi_buf);
pdbdi->dbcc_classguid = GUID_DEVINTERFACE_CDROM;
pdbdi->dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;

//got a nice service handle going in ...
m_hDevNotify = RegisterDeviceNotification(m_hServiceStatus, pdbdi,
DEVICE_NOTIFY_SERVICE_HANDLE);

//...notify handle is good here ...

HandlerEx() has a switch for SERVICE_CONTROL_DEVICEEVENT. It is not hit when
I pop media in and out ..

I had let this go because I was getting some results on DBT_DEVTYP_HANDLE,
passing in a handle I had gotten from CreateFile(). This seemed to work - I
was getting messages for a CDROM - but I dropped it when I started getting
squirrely results on a ZIP drive. When I ejected a disk, I got a
GUID_IO_MEDIA_ARRIVAL (not REMOVAL), and when I stuffed a disk in, I got
nothing at all. So I ditched it ... got any more ideas?

Eric

PS. Does it care about service status when RegisterDeviceNotification() is
called? Mine is SERVICE_START_PENDING.
Post by Jim Cavalaris [MS]
GUID_DEVCLASS_CDROM is the GUID representing the device setup class
for CD-ROM devices, not the device interface class. the device
interface class GUID that is registered for CD-ROM devices is
GUID_DEVINTERFACE_CDROM, which should be defined in winioctl.h.
this is the GUID you should use with RegisterDeviceNotification
to register for DBT_DEVTYP_DEVICEINTERFACE arrival and removal
events for CD-ROM devices. you do not need to specify any special
SERVICE_ACCEPT_* flags to receive device events, just make sure
you have registered a HandlerEx type handler, as i mentioned
previously. also, do not copy any string in the dbcc_name field; this
field is only used when you receive a notification event, to describe
the device interface that the event is for.
many device interface class GUID definitions were renamed in the
Windows XP DDK and later to use a GUID_DEVINTERFACE_* naming
convention. this was done specifically to avoid confusing these
with device setup classes (which have always had GUID_DEVCLASS_*
names). in previous versions of the DDK, GUID_DEVINTERFACE_CDROM
is defined as CdRomClassGuid.
the following docs should help explain the differences between
device setup classes and device interface classes.
http://msdn.microsoft.com/library/en-us/install/hh/install/setup-cls_39wn.asp
http://msdn.microsoft.com/library/en-us/install/hh/install/setup-cls_8vs7.asp
Post by Jim Cavalaris [MS]
hope this helps,
jim.
--
This posting is provided "AS IS" with no warranties, and confers no rights.
Thanks.
Comments in the text.
OK, this works by some miracle ( I used GUID_DEVCLASS_CDROM ) but the docs
page for RegisterDeviceNotification(),
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/devio/base/
Post by Jim Cavalaris [MS]
This
value is not supported.
But it returns a valid handle on Win2K?!? This is why I did not try this
sooner.
Now, I cannot get messages when I remove the device. I have called
SetServiceStatus() with dwControlsAccepted = SERVICE_ACCEPT_STOP |
SERVICE_ACCEPT_HARDWAREPROFILECHANGE which should enable it to get that
message (this is also a little unclear in the docs) but still no message.
I am reduced to guessing if the GUID is right - I have used the GUID for
MEDIA and VOLUME as well as CDROM with the same outcome : nice looking
handle but media change does not get me a message. So, what could it be?
Device name? I have tried "d:", \\?\d:, "d:\" ... and on and on.
Any ideas? You got me one step further, I have a handle now.
Thanks,
Eric
m_status.dwControlsAccepted = SERVICE_ACCEPT_STOP |
SERVICE_ACCEPT_HARDWAREPROFILECHANGE;
....
m_status.dwCurrentState = SERVICE_START_PENDING;
m_hServiceStatus = RegisterServiceCtrlHandlerEx(m_szServiceName,
_HandlerEx, 0);
if (m_hServiceStatus == NULL)
{
LogEvent(_T("Handler not installed"));
return;
}
//this uses the dwControlsAccepted set earlier, yes ..
SetServiceStatus(SERVICE_START_PENDING);
//just a nice big block of bits, plenty big enuf
memset(&dbdi, 0, sizeof(dbdi));
DEV_BROADCAST_DEVICEINTERFACE * p = (DEV_BROADCAST_DEVICEINTERFACE
*)&dbdi;
p->dbcc_size = sizeof(dbdi);
p->dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
_tcscpy(p->dbcc_name, _T("d:\\"));
p->dbcc_classguid = GUID_DEVCLASS_VOLUME;
//this returns non-null handle
m_hDevNotify = RegisterDeviceNotification(m_hServiceStatus, p,
DEVICE_NOTIFY_SERVICE_HANDLE);
Rufus DeDufus
2003-07-25 07:42:51 UTC
Permalink
Post by Rufus DeDufus
He's baaaaack ..... :-o
... When I ejected a disk, I got a
GUID_IO_MEDIA_ARRIVAL (not REMOVAL), and when I stuffed a disk in, I got
nothing at all. So I ditched it ... got any more ideas?
Eh, should be MOUNT_VOLUME not MEDIA_ARRIVAL. And I got it when I EJECTED a
disk. Insertion of a disk got me nothing at all.
Jim Cavalaris [MS]
2003-07-25 17:51:44 UTC
Permalink
the cd-rom device interface corresponds to the cd-rom device itself,
and is always enabled whether there is media present or not. to get
media arrival/removal notifications, you need to register for target
device notification (DBT_DEVTYP_HANDLE) separately on any such device
that you need to receive custom events for.

open a handle to the cd-rom device (using the cd-rom device interface)
and register for target device change notification using
RegisterDeviceNotification with a DEV_BROADCAST_HANDLE structure as
the NotificationFilter (DBT_DEVTYP_HANDLE).

once registered for target device change notifications, you should be
able to receive DBT_CUSTOMEVENT events (in addition to others such as
DBT_DEVICEQUERYREMOVE when the cd-rom device is being removed, etc.).

the data associated with DBT_CUSTOMEVENT events is a DEV_BROADCAST_HANDLE
structure, the dbch_eventguid member is valid and specifies a GUID
representing the custom event. for media arrival / removal notifications,
the dbch_eventguid member of this structure will specify
GUID_IO_MEDIA_ARRIVAL / GUID_IO_MEDIA_REMOVAL.

you were on the right track using the DBT_DEVTYP_HANDLE notifications
to receive the GUID_IO_MEDIA_ARRIVAL/REMOVAL notifications. not sure
why this wasn't working for the zip disk, but this should work for
cd-rom devices.

RegisterDeviceNotification does not currently check the status of the
calling service, so it will not fail, but i would recommend that you
wait until your service is fully started to register. if device events
occur after you have registered, but before your service is started, i
don't know that the SCM is guaranteed deliver those to you anyways.

hope this helps,
jim.
--
This posting is provided "AS IS" with no warranties, and confers no rights.
Post by Rufus DeDufus
He's baaaaack ..... :-o
I am still unable to get this to work. Now I get a valid handle from
RegisterDeviceNotification() but no events are sent.
m_hServiceStatus = RegisterServiceCtrlHandlerEx(m_szServiceName, _HandlerEx,
0);
//good status handle here ..
//dbdi_buf is just a slab of bits big enuf for our work ...
memset(&dbdi_buf, 0, sizeof(dbdi_buf));
pdbdi = (DEV_BROADCAST_DEVICEINTERFACE*)&dbdi_buf;
pdbdi->dbcc_size = sizeof(dbdi_buf);
pdbdi->dbcc_classguid = GUID_DEVINTERFACE_CDROM;
pdbdi->dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
//got a nice service handle going in ...
m_hDevNotify = RegisterDeviceNotification(m_hServiceStatus, pdbdi,
DEVICE_NOTIFY_SERVICE_HANDLE);
//...notify handle is good here ...
HandlerEx() has a switch for SERVICE_CONTROL_DEVICEEVENT. It is not hit when
I pop media in and out ..
I had let this go because I was getting some results on DBT_DEVTYP_HANDLE,
passing in a handle I had gotten from CreateFile(). This seemed to work - I
was getting messages for a CDROM - but I dropped it when I started getting
squirrely results on a ZIP drive. When I ejected a disk, I got a
GUID_IO_MEDIA_ARRIVAL (not REMOVAL), and when I stuffed a disk in, I got
nothing at all. So I ditched it ... got any more ideas?
Eric
PS. Does it care about service status when RegisterDeviceNotification() is
called? Mine is SERVICE_START_PENDING.
Rufus DeDufus
2003-07-25 17:57:58 UTC
Permalink
Thanks.

Now, any idea how I can track attach/detach of network drives?

Can I trap "net use * \\computer\share" or "net use x: /del" ?

Thanks again BTW.

Eric
Post by Jim Cavalaris [MS]
the cd-rom device interface corresponds to the cd-rom device itself,
and is always enabled whether there is media present or not. to get
media arrival/removal notifications, you need to register for target
device notification [....]
Jim Cavalaris [MS]
2003-07-22 21:02:42 UTC
Permalink
the MSDN documentaion for DEV_BROADCAST_HDR is currently incorrect.
the DBT_DEVTYP_DEVICEINTERFACE and DBT_DEVTYP_HANDLE values
_are_ supported for Windows XP/2000 and later, and Windows 98/Me
(they are not supported for Windows 95).

i have followed up with the SDK documentation writers to address
this issue.

hope this helps,
jim.
--
This posting is provided "AS IS" with no warranties, and confers no rights.
OK, this works by some miracle ( I used GUID_DEVCLASS_CDROM ) but the docs
page for RegisterDeviceNotification(),
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/devio/base/
This
value is not supported.
But it returns a valid handle on Win2K?!? This is why I did not try this
sooner.
Loading...