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++.
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.