Naraeon SSD Tools internals – 1. How to get PhysicalDrive list

Foreword

In this series, I would write about internals of Naraeon SSD Tools. So people who implements some subset of this would get some help. This series includes things I asked about. If you have further questions, feel free to email me. But I would not get request about translating code snippet from Delphi to C++.


Necessity

When a disk management tool starts, it firstly finds disks. There are two ways to try, first one is using OS-provided database ‘WMI’, and another one is using other APIs to do this. Naraeon SSD Tools didn’t choose anything, but tries these two sequentially.  Like this, in Naraeon SSD Tools, there are some parts those have various ways to do that, but cannot know which way is fine in prior. So Naraeon SSD Tools tries all them in order. Objects use this pattern named ‘TAutoXXX’ in this project.

Which type to get?

To get a list of storage, you firstly need to define the type of storage to get. Are they local drives? network drives? logical partitions? physical drives? removable disks? There are a lot of criteria and types. And there are several APIs to get each of them. Naraeon SSD Tools use PhysicalDrives(\\.\PhysicalDriveXX).

If you want to see that, set breakpoint at GetPhysicalDriveList function in TListChangeGetter. This function is called when list need to be changed by external reason(PnP signal by newly connected USB device). Result would be like next picture. Count of items would be same as storage device count of your PC. I would write about IPhysicalDrive interface and TPhysicalDrive object in the next article.

Two ways: WMI(TWMIPhysicalDriveListGetter), OS(TOSPhysicalDriveListGetter)

WMI way is query based, and it’s so easy. You can see this in TWMIPhysicalDriveListGetter object. Firstly, in GetDiskDriveSearchResult function, execute ‘Select * from Win32_DiskDrive’ query.

function TWMIPhysicalDriveListGetter.GetDiskDriveSearchResult
  (WMIObject: IDispatch): IEnumVARIANT;
const
  SelectAllDiskDrive = 'Select * from Win32_DiskDrive';
var
  ResultAsOleVariant: OleVariant;
begin
  ResultAsOleVariant :=
    OleVariant(WMIObject).ExecQuery(SelectAllDiskDrive);
  result :=
    IUnknown(ResultAsOleVariant._NewEnum) as IEnumVARIANT;
end;

Then in IfFixedOrUSBDriveAddToList inside TraverseResultAndAddFixedOrUSBDrive function, check whether it is a physical drive or not. To avoid null reference error, you need to check existence and nullity. IsCurrentDriveAvailable does this. The function checks MediaLoaded(Expected: true), MediaType(Expected: not null), DeviceID(Expected: not null) of each item.

function TWMIPhysicalDriveListGetter.IsDriveSetValidType: Boolean;
begin
  result := (not VarIsNull(CurrentDrive.MediaType));
end;

function TWMIPhysicalDriveListGetter.IsDriveLoaded: Boolean;
begin
  result := (CurrentDrive.MediaLoaded);
end;

function TWMIPhysicalDriveListGetter.IsDriveSetValidID: Boolean;
begin
  result := (not VarIsNull(CurrentDrive.DeviceID));
end;

function TWMIPhysicalDriveListGetter.IsCurrentDriveAvailable: Boolean;
begin
  result :=
    IsDriveSetValidID and
    IsDriveLoaded and
    IsDriveSetValidType;
end;

Finally, CheckMediaTypeAndAddIfRequirementMet checks each item whether it is physical drive or not. Criteria are including ‘hard’ in MediaType, InterfaceType is one of ‘IDE’, ‘SCSI’, ‘USB’. Then it adds the drive into the result list.

function TWMIPhysicalDriveListGetter.IsHarddrive(const MediaType: String):
  Boolean;
begin
  //Refer https://msdn.microsoft.com/en-us/library/aa394132%28v=vs.85%29.aspx
  result := Pos('hard', LowerCase(MediaType)) >= 0;
end;

function TWMIPhysicalDriveListGetter.IsDriveConnectedByKnownInterface: Boolean;
begin
  result :=
    IsDriveConnectedBy('IDE') or
    IsDriveConnectedBy('SCSI') or
    IsDriveConnectedBy('USB');
end;

procedure TWMIPhysicalDriveListGetter.IfDriveConnectedByKnownInterfaceAddToList;
begin
  if IsDriveConnectedByKnownInterface then
     AddDriveToDeviceIDList;
end;

procedure TWMIPhysicalDriveListGetter.CheckMediaTypeAndAddIfRequirementMet;
begin
  if IsHarddrive(CurrentDrive.MediaType) then
    IfDriveConnectedByKnownInterfaceAddToList;
end;

OS way uses QueryDosDevice API to get paths starting with ‘PhysicalDrive’. You can find this in TOSPhysicalDriveGetter object. TOSPhysicalDrivePathGetter retrieve them and parse. Because they are null-terminated, parsing is very easy. Delphi supports null-terminated string pointer(PChar) type, so only changing the pointer value to the head of the string would be sufficient.

function TOSPhysicalDrivePathGetter.ParseVolumeNameIntoList(
  const AllDrives: PTVolumeNameBuffer): TDrivePathNumberList;
var
  CurrentCharIndex: Integer;
  CurrentName: PChar;
begin
  CurrentCharIndex := 0;
  result := TDrivePathNumberList.Create;
  while CurrentCharIndex < Length(AllDrives^) do
  begin
    CurrentName := @(AllDrives^)[CurrentCharIndex];
    if CurrentName = '' then
      break;
    if IsPhysicalDrive(CurrentName) then
      result.Add(ParseSeparatedName(CurrentName));
    Inc(CurrentCharIndex, Length(CurrentName) + 1);
  end;
end;

Existence of those PhysicalDrive can be checked with IOCTL_STORAGE_CHECK_VERIFY ioctl code. Details would be written at next article.

Pattern(?) in Naraeon SSD Tools: TAutoXXX

Naraeon SSD Tools includes one pattern. If something has various ways to access and ambiguous what to select, it is passed to TAutoXXX object. And then the object would select the best way. In this portion, the app uses TAutoPhysicalDriveListGetter to get the list. In that object, firstly tries WMI, and when there is some error, fallback to OS way. In the whole app, the pattern is used frequently(Command set, Partition type). TAutoXXX are made with metaclass feature of Delphi.

1 comment on “Naraeon SSD Tools internals – 1. How to get PhysicalDrive list

Leave a Reply

Your email address will not be published. Required fields are marked *