SetupDiGetDeviceRegistryProperty
(1)
TSHI.MediaRemovable
(1)
TSHI.DeviceHotplug
(1)
XP
(1)
WriteCacheEnableOverride
(1)
GetDriveTypeA
(1)
DwFlagsAndAttributes
(1)
PreventMediaRemoval
(1)
Alternative to GetDriveType for Large USB drives
Asked By Dave O.
06-Nov-09 09:31 AM
Hi
A USB thumb drive is identified by GetDriveType as a removable but a higher
capacity hard drive connected via the USB port is reported as a fixed drive.
Is there any way to reveal how a drive is connected.
The reason is that I store the locations of certain files but if they are on
a removable drive the drive letter can change so my program needs to know
when it should check the drive map to see if it can find the file and when
it should not bother.
Thanks in advance
Dave O.
See the online version of GetDriveType:GetDriveType Function:http://msdn.
Nobody replied to Dave O.
06-Nov-09 10:18 AM
See the online version of GetDriveType:
GetDriveType Function:
http://msdn.microsoft.com/en-us/library/aa364939(VS.85).aspx
Quote: "To determine whether a drive is a USB-type drive, call
SetupDiGetDeviceRegistryProperty and specify the SPDRP_REMOVAL_POLICY
property."
This flag however requires XP+, and is difficult to use, unless you find a
sample. Try searching the web for "vb -dotnet
SetupDiGetDeviceRegistryProperty". Also search
http://www.planet-source-code.com
Thanks, this looks exactly what I want, as for "difficult to use",
Dave O. replied to Nobody
06-Nov-09 10:23 AM
Thanks, this looks exactly what I want, as for "difficult to use", from
Microsoft I would not expect anything else.
Utility <inversely proportional> Ease of use
Thanks again
Dave O.
Well that was fun, suitable samples are rare indeed, as usual I could notfind
Dave O. replied to Nobody
06-Nov-09 11:27 AM
Well that was fun, suitable samples are rare indeed, as usual I could not
find an example that just does one thing but I did find this
all-singing-all-dancing example:
http://bbs.bccn.net/thread-241496-1-1.html
Either the text of this article is in a different language, or there is a
strange cult who communicate with identical looking little boxes.
Dropping the code into notepad and saving as a frm file did not load into
VB6 but creating a blank form adding a button & a list box and pasting the
code part in does work.
The next fun bit is to extract the bit I want.
Regards
Dave O.
Dave O. wrote:Open the volume (CreateFile with something like "\\.
Uwe Sieber replied to Dave O.
09-Nov-09 06:59 AM
Open the volume (CreateFile with something like "\\.\U:")
and call DeviceIoControl with IOCTL_STORAGE_QUERY_PROPERTY.
Uwe
UweThanks, I am currently working through the example I found and that
Dave O. replied to Uwe Sieber
09-Nov-09 11:01 AM
Uwe
Thanks, I am currently working through the example I found and that uses
CreateFile & IOCTL_STORAGE_QUERY_PROPERTY is there too, so once I have
stripped out all the fluff I think I will probably have pretty much what you
suggest.
Many Thanks
Dave O.
If anybody is interested I have worked this out, it seems to function but I
Dave O. replied to Dave O.
10-Nov-09 08:50 AM

If anybody is interested I have worked this out, it seems to function but I will
need to do some proper tests before I use it for real.
All I am trying to find out is if a drive is probably removeable, I think I
might be able to simplify it a bit such as getting rid of the
STORAGE_PROPERTY_QUERY variable, but let us get it working first.
Regards
Dave O.
'####### Watch out for Mr. Line Wrap.
Private Const GENERIC_READ As Long = &H80000000
Private Const GENERIC_WRITE As Long = &H40000000
Private Const FILE_SHARE_READ As Long = &H1
Private Const OPEN_EXISTING As Long = 3
Private Const FILE_SHARE_WRITE As Long = &H2
Private Const IOCTL_STORAGE_QUERY_PROPERTY As Long = &H2D1400
Private Type STORAGE_PROPERTY_QUERY
PropertyId As Integer
QueryType As Integer
AdditionalParameters(7) As Byte
End Type
Private Declare Function CreateFile Lib "kernel32" Alias "CreateFileA"
(ByVal lpFileName As String, ByVal dwDesiredAccess As Long, ByVal
dwShareMode As Long, lpSecurityAttributes As Any, ByVal
dwCreationDisposition As Long, ByVal dwFlagsAndAttributes As Long, ByVal
hTemplateFile As Long) As Long
Private Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long)
As Long
Private Declare Function DeviceIoControl Lib "kernel32" (ByVal hDevice As
Long, ByVal dwIoControlCode As Long, lpInBuffer As Any, ByVal nInBufferSize
As Long, lpOutBuffer As Any, ByVal nOutBufferSize As Long, lpBytesReturned
As Long, lpOverlapped As Any) As Long
Private Declare Function GetDriveType Lib "kernel32" Alias "GetDriveTypeA"
(ByVal nDrive As String) As Long
Private Function IsDriveRem(DrvLet As String) As Boolean
Dim r As Long
Dim s As String
Dim hDevice As Long
Dim Query As STORAGE_PROPERTY_QUERY
Dim OutBuf(0 To 1024) As Byte
Dim ReturnedLength As Long
s = Left$(DrvLet, 1) & ":"
r = GetDriveType(s)
If (r = 2) Or (r = 5) Then ' Removeable or optical
IsDriveRem = True
Else
hDevice = CreateFile("\\.\" & s, GENERIC_READ Or GENERIC_WRITE,
FILE_SHARE_READ Or FILE_SHARE_WRITE, ByVal 0&, OPEN_EXISTING, 0&, 0&)
r = DeviceIoControl(hDevice, IOCTL_STORAGE_QUERY_PROPERTY, Query,
Len(Query), ByVal VarPtr(OutBuf(0)), 512, ReturnedLength, ByVal 0&)
CloseHandle hDevice
r = CLng(OutBuf(28))
IsDriveRem = (r = 1) Or (r = 4) Or (r = 7) 'SCSI, Firewire or USB
End If
End Function
GENERIC_READ Or GENERIC_WRITE makes it work foradmins only.
Uwe Sieber replied to Dave O.
10-Nov-09 09:48 AM
GENERIC_READ Or GENERIC_WRITE makes it work for
admins only. A 0 (info access) is good enough.
In VB6 the STORAGE_PROPERTY_QUERY struct should
use longs instead of integers.
BusTypes SATA, SD and MMC might be removable too
but many internal drives may have BusType SCSI
and are not removable...
Maybe checking for the hotplug flag is the
better choice, it is available since XP.
Call DeviceIoControl with IOCTL_STORAGE_GET_HOTPLUG_INFO,
you will find three interesting flags in the
STORAGE_HOTPLUG_INFO struct.
Here is a sample:
Uwe
Interesting about the HOTPLUG thing.
Dave O. replied to Uwe Sieber
10-Nov-09 10:33 AM

Interesting about the HOTPLUG thing. I am not worried about the occasional
false positive on SCSI as the object here is to determine if a file is
missing or if I should check the drive map to see if the drive letter has
changed and the files is there.
I will have to look into the permissions thing, that is always a pain.
it is for a database centred media playing program where media files could be
just about anywhere or on anything, the user selects what they want then the
program has a good look for it, if it is marked as being on a removable it will
first look at any likely location then if that fails ask for the media which
hopefully has a unique name which is also in the database.
The program for personal use and as a testbed for some techniques and will
not be published, but it would be nice to do it all correctly.
To be honest the easiest way would be to regard every drive as removable and
if the file is not in the default stored location check to see if the folder
exists on any other mounted volume. The reasoning being that media on fixed
drives is likely to remain where it is put so will not fail into the search
drive-map mode. For some reason I find that less than satisfactory so would
at least like to try to do it properly.
Many thanks
Dave O.
This is fun is not it?
Dave O. replied to Uwe Sieber
10-Nov-09 10:54 AM

This is fun is not it? I had a look at the sample above, added what you have
said about access, simplified the sample down to a routine to return true
for Hotplug or Removable and come up with this little thing:
Other declarations as before
Private Const IOCTL_STORAGE_GET_HOTPLUG_INFO As Long = &H2D0C14
Private Const INVALID_HANDLE_VALUE As Long = -1
Private Type STORAGE_HOTPLUG_INFO
Size As Long
MediaRemovable As Byte
MediaHotplug As Byte
DeviceHotplug As Byte
WriteCacheEnableOverride As Byte
End Type
Public Function IsVolRemovable(DrvLetter As String) As Boolean
Dim hVol As Long
Dim r As Long
Dim tSHI As STORAGE_HOTPLUG_INFO
hVol = CreateFile("\\.\" & DrvLetter & ":", 0, FILE_SHARE_READ Or
FILE_SHARE_WRITE, ByVal 0&, OPEN_EXISTING, 0&, ByVal 0&)
If hVol <> INVALID_HANDLE_VALUE Then
Call DeviceIoControl(hVol, IOCTL_STORAGE_GET_HOTPLUG_INFO, ByVal 0&, 0&,
tSHI, Len(tSHI), r, ByVal 0&)
IsVolRemovable = (tSHI.MediaRemovable + tSHI.DeviceHotplug > 0)
CloseHandle hVol
End If
End Function
Uwe Sieber wrote:I have never heard that said before.
Karl E. Peterson replied to Uwe Sieber
10-Nov-09 01:07 PM
I have never heard that said before. Can anyone expand on that, or provide cites?
Thanks...
--
.NET: it is About Trust!
http://vfred.mvps.org
See CreateFile().
Nobody replied to Karl E. Peterson
10-Nov-09 01:28 PM
See CreateFile(). The Remarks section contains sections for each thing you
could open. Under "Disk Devices" heading, look for "Windows NT/2000/XP", and
the points after. Quote:
Windows NT/2000/XP: You can use the CreateFile function to open a disk drive
or a partition on a disk drive. The function returns a handle to the disk
device; that handle can be used with the DeviceIOControl function. The
following requirements must be met in order for such a call to succeed:
(.) The caller must have administrative privileges for the operation to
succeed on a hard disk drive.
....
Nobody wrote:Okay, but that is in reference to opening the root of a device,
Karl E. Peterson replied to Nobody
10-Nov-09 01:45 PM
Okay, but that is in reference to opening the root of a device, not to the access
requested. It was the G_R and G_W that got me curious.
--
.NET: it is About Trust!
http://vfred.mvps.org
I read "GENERIC_READ Or GENERIC_WRITE" to be direct from code and assumed
Bob Butler replied to Karl E. Peterson
10-Nov-09 02:21 PM
I read "GENERIC_READ Or GENERIC_WRITE" to be direct from code and assumed it
was the inclusion of the "Or GENERIC_WRITE" part that would require admin
rights.
Bob Butler wrote:Hmmmm, okay, so this is not some sort of GENERIC warning, but
Karl E. Peterson replied to Bob Butler
10-Nov-09 03:45 PM
Hmmmm, okay, so this is not some sort of GENERIC warning, but specific to the object
at hand then?
Raises an interesting question for me, though. I was playing around with snagging
notifications for device removals via WM_DEVICECHANGE and DBT_DEVICEQUERYREMOVE.
But that specific event was not ever being offered, so I started to think that
perhaps I needed to hold an open handle, or something, for Windows to bless me with
the query call?
Anyone ever dink with that?
--
.NET: it is About Trust!
http://vfred.mvps.org
Karl E.
Uwe Sieber replied to Karl E. Peterson
11-Nov-09 03:11 AM
Indeed I meant "GENERIC_READ Or GENERIC_WRITE" as used in the
code, in fact it is the write access which needs admin previleges
when opening a physical volume or disk.
For getting DBT_DEVICEQUERYREMOVE an info access handle to the
volume or the disk is good enough. Then RegisterDeviceNotifcation
returns a notification handle. The volume or disk handle can
be closed.
I have shown this in C++ here:
http://www.uwe-sieber.de/sourcecode/medianotify_src.zip
it is for getting media arrival/removal notifications which
needs a disk handle which is a bit complicated to get by
drive letter. For DBT_DEVICEQUERYREMOVE a volume handle
is good enough.
Uwe
If there is a problem with non-admins or not, the line works quite happilywith
Dave O. replied to Karl E. Peterson
11-Nov-09 11:54 AM
If there is a problem with non-admins or not, the line works quite happily
with zero for all the parameters except obviously the path.
hVol = CreateFile("\\.\" & DrvLetter & ":", 0, 0, ByVal 0&, 0, 0&, ByVal 0&)
Regards
Dave O.
Uwe Sieber wrote:Okay, that makes sense, sure.I will have to give it a look.
Karl E. Peterson replied to Uwe Sieber
12-Nov-09 05:58 PM
Okay, that makes sense, sure.
I will have to give it a look. I am still unclear on the timing you are suggesting.
I am registering for notification and have all that working. And you cannot actually
grab a handle to the device until after it arrives, so... Are you just saying the
volume handle should be opened and held until the queryremove, and if not then,
well, then I am really confused. Anyway, I will take a look at your code, but it
sounds like it is doing more than I am trying to do, so... Thanks!
--
.NET: it is About Trust!
http://vfred.mvps.org
Karl E. Peterson wrote:Ahhhh!
Karl E. Peterson replied to Karl E. Peterson
12-Nov-09 06:23 PM
Ahhhh! I see. "All I have to do" is call RegisterDeviceNotification for each
device I want to be queried on, in addition to having already done so for generic
notifications. I think that makes sense, thanks!
--
.NET: it is About Trust!
http://vfred.mvps.org
Karl E. Peterson wrote:Works great!
Karl E. Peterson replied to Karl E. Peterson
12-Nov-09 07:20 PM
Works great! For some reason, I was thinking I'd need to hang onto the open handle
until the query came along, which did not seem like a very cool thing to do. Thanks
much! :-)
--
.NET: it is About Trust!
http://vfred.mvps.org
Uwe Sieber wrote:Hmmmm, okay, got that working just fine, for
Karl E. Peterson replied to Uwe Sieber
12-Nov-09 07:58 PM
Hmmmm, okay, got that working just fine, for DBT_DEVTYP_HANDLE as you did. But when
I tried replicating it using DBT_DEVTYP_VOLUME and of course using the
DEV_BROADCAST_VOLUME structure instead, the call fails with an errorcode of 13. Not
very helpful. The problem with just getting the handle queries is that I am
(seemingly) not able to access the drive letter with those. Have you found a way to
get the queries on volumes?
Hmmmm, a little more googling seems to indicate the DEV_BROADCAST_VOLUME structure
is something you recieve, but never send? So I am still missing something I guess.
Gonna go spend some more time with my ol' pal Google...
--
.NET: it is About Trust!
http://vfred.mvps.org
Karl E. Peterson wrote:Well, is not this cathartic?
Karl E. Peterson replied to Karl E. Peterson
12-Nov-09 09:07 PM
Well, is not this cathartic? <g>
Okay, I see now that I can pick the drive letter out of a DBT_DEVTYP_HANDLE
structure, so that will work.
it is really weird. Before I started setting this query hook, I was still getting
two DBT_DEVICEQUERYREMOVE notifications whenever I tried to stop a USB thumbdrive.
Setting the query hook, I get an additional notification. The original two are
virtually content free, though. it is just the additional one that has a richer
DBT_DEVTYP_HANDLE from which I can pull the drive letter. There seems to be nothing
of value in the other two structures? Ah well, huh? :-)
--
.NET: it is About Trust!
http://vfred.mvps.org
Uwe Sieber wrote:Last question for today, I promise.
Karl E. Peterson replied to Uwe Sieber
12-Nov-09 09:23 PM
Last question for today, I promise. <g> Should this work for anything besides USB
devices? I am not getting Query calls for network or CD/DVD removals. Okay, I am
gonna call it a day, with that...
--
.NET: it is About Trust!
http://vfred.mvps.org
Karl E. Peterson wrote:Hmm.
Uwe Sieber replied to Karl E. Peterson
13-Nov-09 03:05 AM
Hmm. DBT_DEVICEQUERYREMOVE is a custom notification, sent only
to who registered for.
Are dbch_handle and dbch_hdevnotify really empty?
What have you done to get the other two notifications?
What do you mean with "query hook"?
Uwe
Karl E.
Uwe Sieber replied to Karl E. Peterson
13-Nov-09 03:03 AM
For about one year I keeped the drive handle open, closed it on
DBT_DEVICEQUERYREMOVE and reopened on DBT_DEVICEQUERYREMOVEFAILED.
All in vain because of a misinterpreted (or misleading?) sample
at MSDN...
(http://msdn.microsoft.com/en-us/library/aa363427%28VS.85%29.aspx)
Uwe
Karl E.
Uwe Sieber replied to Karl E. Peterson
13-Nov-09 03:13 AM
DBT_DEVICEQUERYREMOVE you get when the whole device
shall be prepared for safe removal. This works for
USB CD/DVD drives too.
On media arrival and removal you get DBT_CUSTOMEVENT
with GUID_IO_MEDIA_ARRIVAL/GUID_IO_MEDIA_REMOVAL.
The only thing about a media eject request I have
seen so far is the existence of GUID_IO_MEDIA_EJECT_REQUEST,
but I have never received it. It think it is something
you get when you have locked the door by means of
IOCTL_STORAGE_MEDIA_REMOVAL with PreventMediaRemoval=TRUE
and the user presses the eject button. Just a guess,
never tried.
Network and Subst drives work completely different,
all I have got so far is the DBT_DEVICEARRIVAL /
DBT_DEVICEREMOVECOMPLETE with DBT_DEVTYP_VOLUME.
Uwe
Uwe Sieber wrote:Yes!
Karl E. Peterson replied to Uwe Sieber
13-Nov-09 03:09 PM
Yes! I think I read that same article, and just decided that was not a very good
thing to do, just to get the notification opportunity. Sure, if you really needed
it, go for it, but not as a matter of course. I am really glad you told me to ignore
that.
--
.NET: it is About Trust!
http://vfred.mvps.org
Uwe Sieber wrote:No, they are not empty, but they are not recognizable either.
Karl E. Peterson replied to Uwe Sieber
13-Nov-09 03:21 PM
No, they are not empty, but they are not recognizable either.
Well, what can I say? It seems that *now* I am not getting them anymore. I must've
botched up the process space, or something yesterday, and just by restarting it
things are back how they should be.
Beats the heck outta me.
Sorry, calling RegisterDeviceNotification to enable the DBT_DEVICEQUERYREMOVE
notifications. Hook is probably a misleading choice of words there?
--
.NET: it is About Trust!
http://vfred.mvps.org
Uwe Sieber wrote:Okay, that makes sense. I was just inserting media, ...Ahhhh.
Karl E. Peterson replied to Uwe Sieber
13-Nov-09 03:23 PM
Okay, that makes sense. I was just inserting media, ...
Ahhhh... I do not think I was watching for that. Will do, and see what shows up.
I wonder what would happen if it was writable media, and you had an open handle?
Yeah, I am seeing those too.
Thanks again!
--
.NET: it is About Trust!
http://vfred.mvps.org