Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Enumeration of USB flash drives?

1,248 views
Skip to first unread message

Bo Berglund

unread,
Apr 9, 2008, 9:45:41 AM4/9/08
to
I am writing an application that writes data to a removable USB drive.
The data are parameters for configuring automation equipment and are
supposed to be stored on a USB flash drive then delivered with the
equipment.

What I want to do is to find the USB drive amongst the drives already
mapped on the PC used in production. They usually have a number of
network shares as drive letters for instance. The PC may also have
more than one internal drive. THe flash drive registers with different
letters on different PC:s so I cannot use that as a que.

So what I want is a way to enumerate *only* the inserted USB flash
drives to limit the selection for data storage to only one or two
drives. As part of the data storage I will also force the Volume label
of the drive to the serial number of the equipment it belongs to, so
it is important not to do this on anything but the USB drive.

I have looked around and googled quite a bit but found only very
complex schemes to find USB drives for example by requesting Windows
notification on drive discovery etc....

Is it at all possible to do this with Delphi (currently using Delphi
7)??


Bo Berglund

Remy Lebeau (TeamB)

unread,
Apr 9, 2008, 2:37:50 PM4/9/08
to

"Bo Berglund" <bo.be...@system3r.se> wrote in message
news:qlhpv31dl3rev0tfo...@4ax.com...

> So what I want is a way to enumerate *only* the inserted
> USB flash drives to limit the selection for data storage to
> only one or two drives.

http://groups.google.com/group/comp.os.ms-windows.programmer.nt.kernel-mode/msg/2769095a9a17532f

> As part of the data storage I will also force the Volume label
> of the drive to the serial number of the equipment it belongs to,
> so it is important not to do this on anything but the USB drive.

Why not store a (protected/encrypted) file on the drive root instead?


Gambit


Remy Lebeau (TeamB)

unread,
Apr 9, 2008, 2:42:03 PM4/9/08
to

Bo Berglund

unread,
Apr 9, 2008, 5:01:58 PM4/9/08
to
On Wed, 9 Apr 2008 11:37:50 -0700, "Remy Lebeau \(TeamB\)"
<no....@no.spam.com> wrote:

>
>"Bo Berglund" <bo.be...@system3r.se> wrote in message
>news:qlhpv31dl3rev0tfo...@4ax.com...
>
>> So what I want is a way to enumerate *only* the inserted
>> USB flash drives to limit the selection for data storage to
>> only one or two drives.
>
> http://groups.google.com/group/comp.os.ms-windows.programmer.nt.kernel-mode/msg/2769095a9a17532f

Gosh, being a Delphi programmer it is not easy to do this so maybe I
should instead ask this:
Given the drive letter of a drive (after a folder select dialog exits)
is there any (simple) way in Delphi programming to determine that this
is a USB flash drive? If so I will just test the selected drive and
change the volume name only if it is.

And I don't want to do anything to *real* hard disks attached via
USB2, like a WD Passport drive or such. So I have to discriminate
between USB connected flash drives and USB connected real hard disks.

>
>> As part of the data storage I will also force the Volume label
>> of the drive to the serial number of the equipment it belongs to,
>> so it is important not to do this on anything but the USB drive.
>
>Why not store a (protected/encrypted) file on the drive root instead?

The Volume name excercise is to make the USB stick clearly visible in
Windows Explorer when the stick is attached. It has no security
purposes at all, just to help the install serviceman to find the drive
easily on his computer. I will change KINGSTON to the equipment serial
number or maybe our company name or similar.
The serial is already in a file on the stick.

/BoB

Remy Lebeau (TeamB)

unread,
Apr 9, 2008, 6:01:01 PM4/9/08
to

"Bo Berglund" <bo.be...@telia.com> wrote in message
news:t9bqv35avf00hs908...@4ax.com...

> Given the drive letter of a drive (after a folder select dialog
> exits) is there any (simple) way in Delphi programming to
> determine that this is a USB flash drive?

The link I gave you answered exactly that:

"Open each drive letter by CreateFile, send IOCTL_STORAGE_QUERY_PROPERTY
and look at the bus type."

> And I don't want to do anything to *real* hard disks attached
> via USB2, like a WD Passport drive or such. So I have to
> discriminate between USB connected flash drives and USB
> connected real hard disks.

I don't know if you will be able to do that. The OS will see both as
storage drives. Whether it reports them both as removable or not, I do not
know right now. Did you look at the second link I gave you?


Gambit


Bo Berglund

unread,
Apr 10, 2008, 1:42:29 AM4/10/08
to
On Wed, 9 Apr 2008 15:01:01 -0700, "Remy Lebeau \(TeamB\)"
<no....@no.spam.com> wrote:

>
>"Bo Berglund" <bo.be...@telia.com> wrote in message
>news:t9bqv35avf00hs908...@4ax.com...
>
>> Given the drive letter of a drive (after a folder select dialog
>> exits) is there any (simple) way in Delphi programming to
>> determine that this is a USB flash drive?
>
>The link I gave you answered exactly that:
>
> "Open each drive letter by CreateFile, send IOCTL_STORAGE_QUERY_PROPERTY
>and look at the bus type."

You mean I should loop all letters in the alphabet and then try to
create a file there like this:

for C := 'A' to 'Z' do
begin
Path:= C + ':\mytestfile.txt';
try
AssignFile(F, Path);
Rewrite(F);
Writeln('Hello world');
CloseFile(F);
if FileExists(Path) then
begin
DeleteFile(Path); // remove test file
//*send* IOCTL_STORAGE_QUERY_PROPERTY ???
//*look at bus type???
end;
except
Continue; {Exception shows drive is not there}
end;
end;

Is this the way to see if a drive letter exists?
And what is the meaning of "send"?

>> And I don't want to do anything to *real* hard disks attached
>> via USB2, like a WD Passport drive or such. So I have to
>> discriminate between USB connected flash drives and USB
>> connected real hard disks.
>
>I don't know if you will be able to do that. The OS will see both as
>storage drives. Whether it reports them both as removable or not, I do not
>know right now. Did you look at the second link I gave you?

Yes I did, but I did not understand much. Seems to be a discussion on
C++ or similar...
I am using Delphi 7.


/BoB

Remy Lebeau (TeamB)

unread,
Apr 10, 2008, 2:42:54 AM4/10/08
to

"Bo Berglund" <bo.be...@telia.com> wrote in message
news:5m9rv3ln36gfb1las...@4ax.com...

> You mean I should loop all letters in the alphabet

Use GetLogicalDriveStrings() instead to get a list of the drive letters that
are actually in use.

> and then try to create a file there like this:

No, do not create a file. Open the actual drive itself. Despite its name,
CreateFile() opens a lot more than just files. You can open physical
devices with it as well. In this case, simply pass the drive letter to
CreateFile() to get a handle to the HDD/flashdrive/whatever that hosts the
drive letter.

> //*send* IOCTL_STORAGE_QUERY_PROPERTY ???

Use DeviceIoControl() to send commands to devices:

http://msdn2.microsoft.com/en-us/library/aa363216.aspx

> //*look at bus type???

IOCTL_STORAGE_QUERY_PROPERTY
http://msdn2.microsoft.com/en-us/library/ms803642.aspx

STORAGE_DEVICE_DESCRIPTOR
http://msdn2.microsoft.com/en-us/library/aa510117.aspx

STORAGE_BUS_TYPE
http://msdn2.microsoft.com/en-us/library/aa510102.aspx

>>I don't know if you will be able to do that. The OS will see both as
>>storage drives. Whether it reports them both as removable or not,
>>I do not know right now.

Apparently the STORAGE_DEVICE_DESCRIPTOR structure does report that. It has
a RemovableMedia field.

> Seems to be a discussion on C++ or similar...
> I am using Delphi 7.

The concepts are the same.


Gambit


bilm

unread,
Apr 10, 2008, 9:34:41 PM4/10/08
to
"Bo Berglund" <bo.be...@system3r.se> wrote in message
news:qlhpv31dl3rev0tfo...@4ax.com...

Using Windows APIs I think the closest you will come is to ID a device as
being USB and removable. As you mention there are other device types
besides flash drives that are in this same category; HDs, CD/DVDs,
others ? ...

To uniquely ID a USB removable as a flash drive I think you will have to
rely on other APIs possibly from the USB and flash drive communities, from
manufactures of these devices and various hacks discovered by others who
were in your situation.

I know cdfreaks has a forum devoted to flash drives. I do not know if you
will find anything useful there. Just continue to google on anything
related. Seach for an open source program for USB sticks like utilties,
backup programs, iPod programs, etc. They all must enumerate and
somehow ID these drives 1st.

One example app I found :
http://www.jtedley.com/jtflashmanager/index.php
the source is in java

I'm surprised MS doesn't offer the source for their USB Flash Drive
Manager or a sample program in the DDK.

If you are talking about U3 type flash drives, I can help you with some
hacks. Alas I have none for the plain USB sticks.

bilm

Bo Berglund

unread,
Apr 11, 2008, 5:23:12 PM4/11/08
to
On Wed, 9 Apr 2008 23:42:54 -0700, "Remy Lebeau \(TeamB\)"
<no....@no.spam.com> wrote:

>Use GetLogicalDriveStrings() instead to get a list of the drive letters that
>are actually in use.

Works fine! :-)

>No, do not create a file. Open the actual drive itself. Despite its name,
>CreateFile() opens a lot more than just files. You can open physical
>devices with it as well. In this case, simply pass the drive letter to
>CreateFile() to get a handle to the HDD/flashdrive/whatever that hosts the
>drive letter.

Tried this but it won't even get through syntax check (tried to
understand the Helpfile description of CreateFile and fill in the
parameters as best I could...):
var
DName: string;
DHandle: THandle;
begin
DName := 'C:\';
DHandle := CreateFile(PChar(DN), 0, FILE_SHARE_READ
+FILE_SHARE_WRITE, NIL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NIL);

It complains and says while the cursor is just before the ; on the
line above:
Incompatible types: 'Cardinal' and 'Pointer'

So it seems like I am doing something wrong (not surprising...) when
dealing with API calls...

Once this is sorted out I guess I will find other surprises down
below.. But I have all week-end now.

>> //*send* IOCTL_STORAGE_QUERY_PROPERTY ???
>
>Use DeviceIoControl() to send commands to devices:
>
> http://msdn2.microsoft.com/en-us/library/aa363216.aspx
>
>> //*look at bus type???
>
> IOCTL_STORAGE_QUERY_PROPERTY
> http://msdn2.microsoft.com/en-us/library/ms803642.aspx
>
> STORAGE_DEVICE_DESCRIPTOR
> http://msdn2.microsoft.com/en-us/library/aa510117.aspx
>
> STORAGE_BUS_TYPE
> http://msdn2.microsoft.com/en-us/library/aa510102.aspx
>
>>>I don't know if you will be able to do that. The OS will see both as
>>>storage drives. Whether it reports them both as removable or not,
>>>I do not know right now.
>
>Apparently the STORAGE_DEVICE_DESCRIPTOR structure does report that. It has
>a RemovableMedia field.


/BoB

Remy Lebeau (TeamB)

unread,
Apr 11, 2008, 5:46:17 PM4/11/08
to

"Bo Berglund" <bo.be...@telia.com> wrote in message
news:spkvv39d5kh0ceqe5...@4ax.com...

> Incompatible types: 'Cardinal' and 'Pointer'

THandle is a numeric type in Delphi, not a Pointer like HANDLE is in C++.
Use 0 instead of nil for the last parameter:

DHandle := CreateFile(PChar(DN), 0, FILE_SHARE_READ or FILE_SHARE_WRITE,
nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);


Gambit


bilm

unread,
Apr 11, 2008, 6:41:15 PM4/11/08
to
"bilm" wrote in message
> "Bo Berglund" wrote in message

Sorry 'bout the example app ... has nothing to do with flash drives.

On Portable Apps dot com there is an article about how to eject a USB
flash drive that includes an enumeration method. It uses the Setupdi and
an ioctl for getting the device number.
http://portableapps.com/node/3070

good luck,

bilm


Bo Berglund

unread,
Apr 12, 2008, 4:58:30 AM4/12/08
to
On Fri, 11 Apr 2008 23:23:12 +0200, Bo Berglund
<bo.be...@telia.com> wrote:

>> IOCTL_STORAGE_QUERY_PROPERTY
>> STORAGE_DEVICE_DESCRIPTOR
>> STORAGE_BUS_TYPE

I tried to make a call in Delphi7 to these as follows:
DB := StringOfChar(#0, 1024);
DeviceIoControl(DH, IOCTL_STORAGE_QUERY_PROPERTY, 0, 0, PChar(DB),
1024, @nLen, 0);

But all of the needed identifiers result in an error:
[Error] : Undeclared identifier: 'IOCTL_STORAGE_QUERY_PROPERTY'
[Error] : Undeclared identifier: 'STORAGE_DEVICE_DESCRIPTOR'
[Error] : Undeclared identifier: 'STORAGE_BUS_TYPE'

Apparently Delphi has no knowledge of the value of these constants,
and if I google them all I find is various ways of *using* them but no
definition whatsoever....


/BoB

Bo Berglund

unread,
Apr 12, 2008, 5:09:42 AM4/12/08
to
On Sat, 12 Apr 2008 10:58:30 +0200, Bo Berglund
<bo.be...@telia.com> wrote:

I think I misread the advice... Apparently I should make *one* call to
DeviceIoControl and watch the returned data.
I managed to Google up a page that had the information on the value of
the identifier so I did this:

const
IOCTL_STORAGE_QUERY_PROPERTY = 2954240;
begin
DN := 'C:\';
DH := CreateFile(PChar(DN), 0, FILE_SHARE_READ or FILE_SHARE_WRITE,
NIL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
DB := StringOfChar(#0, 1024);
nLen := 0;


DeviceIoControl(DH, IOCTL_STORAGE_QUERY_PROPERTY, 0, 0, PChar(DB),

1024, nLen, 0);

But to my dismay the buffer I provided is *not* written to by the
call...
The nLen value is still 0 afterwards and it seems no data have been
entered there.

DH gets the value 4294967295 after the CreateFile call so I guess it
finds something at least....


/BoB

Rudy Velthuis [TeamB]

unread,
Apr 12, 2008, 5:23:44 AM4/12/08
to
Bo Berglund wrote:

> But all of the needed identifiers result in an error:
> [Error] : Undeclared identifier: 'IOCTL_STORAGE_QUERY_PROPERTY'
> [Error] : Undeclared identifier: 'STORAGE_DEVICE_DESCRIPTOR'
> [Error] : Undeclared identifier: 'STORAGE_BUS_TYPE'
>
> Apparently Delphi has no knowledge of the value of these constants,

> and if I google them all I find is various ways of using them but no
> definition whatsoever....

They are not in the current Windows API files that come with Delphi,
indeed (and I checked in D2007). I guess someone made their own
translation of winioctl.h.

--
Rudy Velthuis [TeamB] http://www.teamb.com

"If it weren't for electricity we'd all be watching television by
candlelight." -- George Gobel.

Rudy Velthuis [TeamB]

unread,
Apr 12, 2008, 5:35:42 AM4/12/08
to
Bo Berglund wrote:

> > I tried to make a call in Delphi7 to these as follows:
> > DB := StringOfChar(#0, 1024);
> > DeviceIoControl(DH, IOCTL_STORAGE_QUERY_PROPERTY, 0, 0, PChar(DB),
> > 1024, @nLen, 0);
> >
> > But all of the needed identifiers result in an error:
> > [Error] : Undeclared identifier: 'IOCTL_STORAGE_QUERY_PROPERTY'
> > [Error] : Undeclared identifier: 'STORAGE_DEVICE_DESCRIPTOR'
> > [Error] : Undeclared identifier: 'STORAGE_BUS_TYPE'

FWIW, probably you can use the JEDI Windows API library. That should
have most of these files translated.

http://sourceforge.net/project/showfiles.php?group_id=121894&package_id=251762

--
Rudy Velthuis [TeamB] http://www.teamb.com

"Well-timed silence hath more eloquence than speech."
- Martin Fraquhar Tupper

Bo Berglund

unread,
Apr 12, 2008, 6:33:15 AM4/12/08
to
On Sat, 12 Apr 2008 11:35:42 +0200, "Rudy Velthuis [TeamB]"
<newsg...@rvelthuis.de> wrote:

>Bo Berglund wrote:
>
>> > I tried to make a call in Delphi7 to these as follows:
>> > DB := StringOfChar(#0, 1024);
>> > DeviceIoControl(DH, IOCTL_STORAGE_QUERY_PROPERTY, 0, 0, PChar(DB),
>> > 1024, @nLen, 0);
>> >
>> > But all of the needed identifiers result in an error:
>> > [Error] : Undeclared identifier: 'IOCTL_STORAGE_QUERY_PROPERTY'
>> > [Error] : Undeclared identifier: 'STORAGE_DEVICE_DESCRIPTOR'
>> > [Error] : Undeclared identifier: 'STORAGE_BUS_TYPE'
>
>FWIW, probably you can use the JEDI Windows API library. That should
>have most of these files translated.
>
>http://sourceforge.net/project/showfiles.php?group_id=121894&package_id=251762

I downloaded the package and found inside a directory Win32API with a
lot of pas files. However a grep IOCTL_STORAGE_QUERY_PROPERTY *.pas
search inside this folder turned up nothing at all...

So it seems these constants are not defined here either. :-(


/BoB

Marc Rohloff [TeamB]

unread,
Apr 12, 2008, 9:30:50 AM4/12/08
to
On Sat, 12 Apr 2008 11:09:42 +0200, Bo Berglund wrote:

> DH gets the value 4294967295 after the CreateFile call so I guess it
> finds something at least....

Not,
If you read the help for CreateFile you will see that a value of
INVALID_HANDLE_VALUE indicates a failure. You need to call
GetLastError, RaiseLastOSError or something similar to check the error
message.

--
Marc Rohloff [TeamB]
marc -at- marc rohloff -dot- com

John Clement

unread,
Apr 12, 2008, 3:15:16 PM4/12/08
to
Have a look at

www.magsys.co.uk/delphi

and download the WMI and Smart components.

I have managed to uniquely identify USB sticks using their WMI routines.
This does not work on older windows versions (win9x).

Good luck,
John

bilm

unread,
Apr 12, 2008, 3:17:11 PM4/12/08
to
"Bo Berglund" <bo.be...@telia.com> wrote in message
news:lp3104pdrh8mdhbgm...@4ax.com...

Why are you wasting your time with IOCTL_STORAGE_QUERY_PROPERTY ?
I thought your goal was "a way to enumerate *only* the inserted USB flash
drives" ? The above ioctl is only going to get you the bus type of the
device i.e. USB which you are already getting with the Setupdi functions
( CM_Get_Parent()/ DEVINST repeated 2 times ).

What you need to be doing is finding a way to distinguish between USB flash
drives and HD plug-in drives. Win APIs are not going to help you do this.
They identify both types as USB removable storage of "Disk drive" type.
You need to find a hack or figure out one yourself.

For example my flash drive is formatted with FAT16. May all flash
drives are ? If so maybe all USB plug-in HDs are formatted with FAT32.
That could be your distinguishing difference (filter). Or maybe reading the
MBR. There could be a difference.

If you work at searching with google you will find forums and groups that
discuss these subjects. That's where to post your question.

It could be that there is just no sure way to programmatically tell the
difference between these 2 devices.

bilm


Rudy Velthuis [TeamB]

unread,
Apr 12, 2008, 3:16:59 PM4/12/08
to
Bo Berglund wrote:

> >
http://sourceforge.net/project/showfiles.php?group_id=121894&package_id=251762
>
> I downloaded the package and found inside a directory Win32API with a
> lot of pas files. However a grep IOCTL_STORAGE_QUERY_PROPERTY *.pas
> search inside this folder turned up nothing at all...
>
> So it seems these constants are not defined here either. :-(

Oh, indeed. I'll see if I can convert it for you. Just hang on,
probably until tomorrow.

--
Rudy Velthuis [TeamB] http://www.teamb.com

"I think 'Hail to the Chief' has a nice ring to it."
-- John F. Kennedy (1917-1963) when asked what is his favorite
song

bilm

unread,
Apr 12, 2008, 3:22:12 PM4/12/08
to
"John Clement" <john...@gmx.net> wrote in message
news:4801...@newsgroups.borland.com...

If this is true I stand corrected. I didn't think there was any Win API that
could tell the difference between a USB flash drive and a USB plug-in HD.
This would definitely be the way to go.

What does WMI show for a USB flash vs. HD ?

bilm


John Clement

unread,
Apr 13, 2008, 6:42:05 AM4/13/08
to
I used the WMi Win32_DiskDrive to get a list of all disk drives on the
system.
This returns a list of all hard disks and USB.
In the WMI info for each device you can check if the device is a hard disk
or USB.
InterfaceType = USB
Size = size in bytes
PNPDeviceID returns the (long) string seen in the details page of the device
manager for each drive type.
This includes a number near the end of the ID that so far was unique for
every USB stick I have tested.
I don't know that this code is always going to be unique but with its length
I have so far managed to uniquely identify a large number of USB sticks from
different manufacturers. I have also had no problems with a large batch of
sticks from a single souce - eg. Sandisk Cruzer.

To identify a USB stick is maybe difficult but since you have the DeviceID
on a USB interface you have reduce the list of devices.
Using the size information you can differentiate between a hard disk, which
will be bigger than most sticks, and a USB stick.

I use the deviceID to check for the presence of a specific USB stick when my
application runs.

There is a demo application you can use to try out the WMI commands included
in the Magenta routines.

I use the following command to get information about a USB device after
reading the list of devices.

SELECT * FROM Win32_DiskDrive WHERE Name ="\\\\.\\PHYSICALDRIVE0

where the last character will change for different devices. (0, 1, 2, etc)
Use the MagWmiGetOneQ to investigate the returned data.
For example, get the PNPDeviceID or InterfaceType.

It is not perfect but maybe it can help you identify USB sticks.

As I said, in my system I know the PNPDeviceID and check for this when
running to make sure that the correct USB stick is on the machine.

Good Luck,
John


Bo Berglund

unread,
Apr 13, 2008, 11:04:23 AM4/13/08
to
On Sat, 12 Apr 2008 21:15:16 +0200, "John Clement" <john...@gmx.net>
wrote:

Downloaded and had a look:

MagWmiSmartDiskInfo specifically says *no* USB detection
and there seems to be no other wmi function to get disk drive info, or
I cannot find it...

A lot of other goodies, though. :-)


/BoB

Bo Berglund

unread,
Apr 13, 2008, 2:43:54 PM4/13/08
to
On Sun, 13 Apr 2008 12:42:05 +0200, "John Clement" <john...@gmx.net>
wrote:

>I used the WMi Win32_DiskDrive to get a list of all disk drives on the

Thanks,
I will investigate the possibilities. The test application is a bit
"weird" though. For example it is not apparent how you actually
execute the query in the combobox in the upper right corner of the
window. I cannot find a button that does it...


/BoB

Bo Berglund

unread,
Apr 13, 2008, 6:00:31 PM4/13/08
to

I found the way the query function works by looking into the source of
the example, but the values used in the query come from a combobox
populated with the following possible selections:

VolumeName
Caption
Name
MACAddress
ConfigManagerErrorCode
AdapterType
Handle
Size

These are used as the value of the Prop string when used with the
query Arg:

function MagWmiGetOneQ (const Arg, Prop: widestring ; var ResStr:
string): integer ;

Question:
Where can I find a list of valid "Prop" string values?

You mention "PNPDeviceID" and "InterfaceType", how did you come across
these names?


/BoB

John Herbster

unread,
Apr 14, 2008, 6:34:00 AM4/14/08
to
"Bo Berglund" <bo.be...@telia.com> wrote

>>>Use the MagWmiGetOneQ to investigate the returned data.
>>>For example, get the PNPDeviceID or InterfaceType.

> You mention "PNPDeviceID" and "InterfaceType", how did you come across these names?

I don't know where John C got them, but there is interesting reading here
http://www.google.com/search?hl=en&q=%22PNPDeviceID%22+%22InterfaceType%22

The longer this thread runs, the more this lurker learns.
--JohnH

John Clement

unread,
Apr 14, 2008, 1:29:43 PM4/14/08
to
I just played with the demo for a looooooong time, and got lucky.
I will try to get a clean copy of my program which I use to detect USB
sticks and post it.
It could take a while though, but I will ty to get it posted as soon as I
can.
You will need the Magenta code to compile it, so if you do not have it then
download it.

John

John Clement

unread,
Apr 14, 2008, 1:49:53 PM4/14/08
to
Ok, it was easier than I thought.
You need the Magenta WMI and Smart routines from www.magsys.co.uk/delphi

Compile and run this code.
There are 3 listboxes.
The top box will show what devices were found using WMI - click the FindUSB
devices button.
The list will show the hard disks and USB drives found.
At the end of the list I show what USB devices are in the list of found
devices.

I put all USB devices into the bottom listbox into the list of known USB
devices. These are USB devices we have already seen at least once.

The middle box has a few buttons which can be used to test one of the USB
devices found in the top box.
This will display detailed info on the USB device found.

Double-click a known USB device in the bottom box to test to see if this is
currently connected to the computer or not. The StatusLbl next to the box
will say if the USB device was found. It is identifies by its PNPDeviceID.

The list of known USB devices is saved and loaded then the routine is run
the next time.

I hope this helps.

I had to post the sample code into the attachements group since it was
rejected here, so look for it there.

John

Bo Berglund

unread,
Apr 14, 2008, 4:25:25 PM4/14/08
to
On Mon, 14 Apr 2008 19:49:53 +0200, "John Clement" <john...@gmx.net>
wrote:

>Ok, it was easier than I thought.


>You need the Magenta WMI and Smart routines from www.magsys.co.uk/delphi

Had downloaded these already. :-)

Thanks,
I found your example over at the attachments area and fired up in
Delphi 7. Had to comment out one form property that does not exist in
D7, after that it worked fine. :-)

Now I will extract the code I need to include to check the drive to
see if I can change the label or not.


/BoB

bilm

unread,
Apr 14, 2008, 8:44:49 PM4/14/08
to
"John Clement" <john...@gmx.net> wrote in message
news:4803...@newsgroups.borland.com...

Thanks for posting your program. Works great.

But unless I'm missing something, the only way to distinguish between flash
drives and portable USB hard drives is to check the size (capacity).

It's still a hack but it's the best one I run across so far. Good one.
Plus it's easy to implement thanks to the Magenta WMI.

Lets hope these sticks don't become too much bigger ; )

bilm


John Clement

unread,
Apr 15, 2008, 3:22:18 AM4/15/08
to
Glad it worked for you.
It is the best I have found so far. Pity that USB sticks do not have serial
numbers like hard disk drives.
I have seen some companies advertising secure USB sticks which are supposed
to have unique serial numbers and other security features but these are very
expensive compared to standard sticks.
I have tested the WMI code with many sticks from different sources and so
far have found them to be uniquely identifiable with the WMI code.
Of course this may not be true for all sticks. I think that especially the
cheapest sticks may present problems.
I am staying with known brands, such as Sandisk, since these work for me.
I just hope that this WMI code is not going to be
removed/changed/broken/made-obsolete by a certain computer company in the
near future.

WMI seems to offer quite a few interesting possibilities and is worth
looking at.

John


bilm

unread,
Apr 15, 2008, 11:44:16 PM4/15/08
to
"John Clement" <john...@gmx.net> wrote in message
news:4804...@newsgroups.borland.com...

> Glad it worked for you.
> It is the best I have found so far. Pity that USB sticks do not have
> serial numbers like hard disk drives.
> I have seen some companies advertising secure USB sticks which are
> supposed to have unique serial numbers and other security features but
> these are very expensive compared to standard sticks.
> I have tested the WMI code with many sticks from different sources and so
> far have found them to be uniquely identifiable with the WMI code.
> Of course this may not be true for all sticks. I think that especially the
> cheapest sticks may present problems.
> I am staying with known brands, such as Sandisk, since these work for me.
> I just hope that this WMI code is not going to be
> removed/changed/broken/made-obsolete by a certain computer company in the
> near future.
As far as I can tell MS continues to strongly support the WMI.

>
> WMI seems to offer quite a few interesting possibilities and is worth
> looking at.

Agreed.

>
> John
>
>
Here is a method that doesn't use Magenta's WMI routines. I got it from a
post to a Japanese newsgroup I think. Didn't get his/her name so can't give
attribution. I changed and added to it quite a bit.
Here it is.


unit demo1;

interface

uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, ActiveX, ComObj;

type
TForm1 = class(TForm)
Button1: TButton;
Memo1: TMemo; // make memo wide enough to handle 80 chars
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;

var
Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.Button1Click(Sender: TObject);
var Locator: OleVariant;
WMI: OleVariant;
RET: OleVariant;
Enum: IEnumVariant;
Tmp: OleVariant;
Value: Cardinal;
dnum : Integer;
begin;
dnum := 0;
Locator := CreateOleObject('WbemScripting.SWbemLocator');
WMI := Locator.ConnectServer('.', '', '', '');
Ret := WMI.ExecQuery('SELECT * FROM Win32_DiskDrive');

Enum:= IUnknown(RET._NewEnum) as IEnumVariant;
while (Enum.Next(1, Tmp, Value) = S_OK) do
begin
Inc(dnum); // drive number for capabilities array
if Tmp.InterfaceType = 'USB' then // filter out bus types except USB
begin
Memo1.Lines.Add(#13+#10+' Disk Drive ' + inttostr(dnum) + ' is a USB
drive.');
Memo1.Lines.Add(' PNP ID : ' + Tmp.PNPDeviceID);
Memo1.Lines.Add(' Interface Type : ' + Tmp.InterfaceType);
Memo1.Lines.Add(' Device Type : ' + Tmp.Description);
Memo1.Lines.Add(' Media Type : ' + Tmp.MediaType);
Memo1.Lines.Add(' Model : ' + Tmp.Model);
case Tmp.Capabilities[dnum] of
0 : Memo1.Lines.Add(' Unknown');
2 : Memo1.Lines.Add(' Sequential Access');
3 : Memo1.Lines.Add(' Random Access');
4 : Memo1.Lines.Add(' Supports Writing');
5 : Memo1.Lines.Add(' Encryption');
6 : Memo1.Lines.Add(' Compression');
7 : Memo1.Lines.Add(' Supports Removable Media');
10 : Memo1.Lines.Add(' SMART Notification');
12 : Memo1.Lines.Add(' Ejection Prior to Drive Dismount Not
Required');
else Memo1.Lines.Add(' Unknown');
end;
Memo1.Lines.Add(' Bytes per sector : ' +
Inttostr(Tmp.BytesPerSector));
Memo1.Lines.Add(' Total Sectors : ' + FormatFloat('0,###',
Tmp.TotalSectors) );
Memo1.Lines.Add(' Drive Capacity : ' +
FormatFloat('0,###',Tmp.Size));
Memo1.Lines.Add(' Status is ' + Tmp.Status);
end
else Memo1.Lines.Add(#13+#10+' Disk Drive ' + inttostr(dnum) + ' is
active but not a USB drive.');
end;
Memo1.Lines.Add(#13+#10+' There are ' + inttostr(dnum) + ' Disk Drives
active on this system.');
end;

end.

I did find some problems with it.
It will not return all the values in the Win32_DiskDrive WMI class.
I get error "Invalid variant conversion" on some of the uint64 values
but oddly not all of them. It returns Size & TotalSectors but not
DefaultBlockSize; yet all are uint64 values.

I get error "not supported by automation object" on some
string values like SerialNumber but not on others like
InterfaceType & Model, etc.

Maybe you can figure it out.
It would be nice to get the SerialNumber value but maybe there just is none.

bilm

bilm

unread,
Apr 16, 2008, 7:31:00 PM4/16/08
to
My misunderstanding of what an "uint16 Capabilities[]" is and how to handle
it resulted in a silly implementation.
Below is one that should work.

unit demo1;

interface

var
Form1: TForm1;

implementation

{$R *.DFM}

dnum, i : Integer;


begin;
dnum := 0;
Locator := CreateOleObject('WbemScripting.SWbemLocator');
WMI := Locator.ConnectServer('.', '', '', '');
Ret := WMI.ExecQuery('SELECT * FROM Win32_DiskDrive');

Enum:= IUnknown(RET._NewEnum) as IEnumVariant;
while (Enum.Next(1, Tmp, Value) = S_OK) do
begin
Inc(dnum);

if Tmp.InterfaceType = 'USB' then // filter out bus types except USB
begin
Memo1.Lines.Add(#13+#10+' Disk Drive ' + inttostr(dnum) + ' is a USB
drive.');
Memo1.Lines.Add(' PNP ID : ' + Tmp.PNPDeviceID);
Memo1.Lines.Add(' Interface Type : ' + Tmp.InterfaceType);
Memo1.Lines.Add(' Device Type : ' + Tmp.Description);
Memo1.Lines.Add(' Media Type : ' + Tmp.MediaType);
Memo1.Lines.Add(' Model : ' + Tmp.Model);

Memo1.Lines.Add(#13+#10+' This drives has the following capabilities
: ' );
for i := 0 to 2 do // only get 3
begin
if Tmp.Capabilities[i] <> 0 then
begin
case Tmp.Capabilities[i] of
2 : Memo1.Lines.Add(' Supports Sequential Access');
3 : Memo1.Lines.Add(' Supports Random Access');


4 : Memo1.Lines.Add(' Supports Writing');

5 : Memo1.Lines.Add(' Supports Encryption');
6 : Memo1.Lines.Add(' Supports Compression');


7 : Memo1.Lines.Add(' Supports Removable Media');

10 : Memo1.Lines.Add(' Supports SMART Notification');
12 : Memo1.Lines.Add(' Supports Ejection Prior to Drive

Dismount Not Required');
else Memo1.Lines.Add(' Unknown');
end;

end;
end;

Memo1.Lines.Add(#13+#10+' Bytes per sector : ' +

Inttostr(Tmp.BytesPerSector));
Memo1.Lines.Add(' Total Sectors : ' + FormatFloat('0,###',
Tmp.TotalSectors) );
Memo1.Lines.Add(' Drive Capacity : ' +
FormatFloat('0,###',Tmp.Size));
Memo1.Lines.Add(' Status is ' + Tmp.Status);
end
else Memo1.Lines.Add(#13+#10+' Disk Drive ' + inttostr(dnum) + ' is
active but not a USB drive.');
end;
Memo1.Lines.Add(#13+#10+' There are ' + inttostr(dnum) + ' Disk Drives
active on this system.');
end;

end.

bilm


0 new messages