In my previous question "How to find the unique serial number of a flash device?" I ended up asking for a way to get the drive letter. That problem is solved.
However, my initial question has not been answered. I wanted to be able to tell removable devices (USB drives, SD cards, (external HDDs?), etc.) apart and always be able to recognise them again when they are reconnected. This should also be possible on any other computer. Luckily, I don't care about the drives being formatted (if/when they are, they are treated as new drives in my program), so can I use partition and volume IDs as a part of my recognition? I am asking this as PNPDeviceID is NOT unique. I found that it depends on the hardware reading it, see below pictures:
So, what I am searching for is a way to detect and recognise any removable device on any computer using the following: Win32_DiskDrive, Win32_DiskPartition, Win32_LogicalDisk. I am thanking RRUZ for the original code:
program GetWMI_USBConnectedInfo;
{$APPTYPE CONSOLE}
uses
Windows,
Classes,
ActiveX,
Variants,
SysUtils,
WbemScripting_TLB in '..\..\Documents\RAD Studio\5.0\Imports\WbemScripting_TLB.pas';
procedure GetUSBDiskDriveInfo;
var
WMIServices : ISWbemServices;
Root,a,b : ISWbemObjectSet;
Item,Item2 : Variant;
i,ii,iii,iiii: Integer;
start,stop,freq:Int64;
begin
QueryPerformanceFrequency(freq);
QueryPerformanceCounter(start);
WMIServices := CoSWbemLocator.Create.ConnectServer('.', 'root\cimv2','', '', '', '', 0, nil);
Root := WMIServices.ExecQuery('Select * From Win32_DiskDrive','WQL', 0, nil);
for i := 0 to Root.Count - 1 do
begin
Item := Root.ItemIndex(i);
for ii := VarArrayLowBound(Item.Capabilities, 1) to VarArrayHighBound(Item.Capabilities, 1) do if (Item.Capabilities[ii] = 7) then begin
Writeln('Caption '+VarToStr(Item.Caption));
Writeln('Name '+VarToStr(Item.Name));
Writeln('DeviceID '+VarToStr(Item.DeviceID));
Writeln('Partitions '+VarToStr(Item.Partitions));
Writeln('PNPDeviceID '+VarToStr(Item.PNPDeviceID));
Writeln('SerialNumber '+VarToStr(Item.SerialNumber));
Writeln('Signature '+VarToStr(Item.Signature));
a := WMIServices.ExecQuery('ASSOCIATORS OF {Win32_DiskDrive.DeviceID=''' + VarToStr(Item.DeviceID) + '''} WHERE AssocClass = Win32_DiskDriveToDiskPartition','WQL', 0, nil);
for iiii := 0 to a.Count - 1 do begin
b := WMIServices.ExecQuery('ASSOCIATORS OF {Win32_DiskPartition.DeviceID=''' + VarToStr(Variant(a.ItemIndex(iiii)).DeviceID) + '''} WHERE AssocClass = Win32_LogicalDiskToPartition','WQL', 0, nil);
for iii := 0 to b.Count - 1 do begin
Item2 := b.ItemIndex(iii);
Writeln('Drive = ' + Item2.Caption);
end;
end;
Writeln;
Writeln;
end;
end;
QueryPerformanceCounter(stop);
if (freq > 0) then
Writeln('Time took: ' + FloatToStr((stop-start) / freq))
else
Writeln('Unable to measure time!');
end;
begin
try
CoInitialize(nil);
GetUSBDiskDriveInfo;
Readln;
CoUninitialize;
except
on E:Exception do
Begin
CoUninitialize;
Writeln(E.Classname, ': ', E.Message);
Readln;
End;
end;
end.
EDIT
I should add that the code detecting the drives when they are inserted is already working, though it only gives me the drive letter. I use that drive letter to get all the other info from the WMI.
Final edit
I have read that developers can safely use partition/volume ID for recognition. Can I count on that?
Solution:
So, since reading the "unique" id's is not a viable solution, there are two ways to get around this:
- Save a hidden file on the drive with a unique id that the program can recognize (compare to a local database).
- Save everything related to the drive on the drive in form of a hidden settings file. I took this approach since the program itself doesn't have any settings. All settings are per partition. This also makes the settings/program portable.