Almost all VCL controls that can use icons contained in an instance of TImageList can use the shell icon. But for this tutorial we'll use a TComboBoxEx as an example. For the example we'll use a TComboBoxEx to list known drives of the host computer. Drives in windows can be shown using different icons. Usually the drive icon indicates the type of the drive (such as fixed drive, CD/DVD drive, and floppy drive). But just like with folders, users can also customize the icon for a specific drive.
Now in this tutorial we'll show how to use TComboBoxEx to list drives with the same icons as shown in Shell (Windows Explorer). TComboBoxEx is selected because it supports showing icons from and TImageList.
Also note that this time we will heavily use Windows API SHGetFileInfo. This Windows API is provided to retrieve information about objects in the file system, such as a file, folder, directory, or drive root. More detailed information about this API can be read in this MSDN page.
The steps are:
- Get the drive letters. There is a full tutorial on this. Please check it out if you haven't done so.
- Get the handle of shell image list using the first (or any) drive letter we got.
- Assign the handle of shell image list to our TComboBoxEx
- For each drive detected in step 1 get the icon index for icon in the shell image list associated with the drive and then add new item in the TComboBoxEx by passing the drive letter and icon index.
And that's it. Of course to make it immediately available for user you need to put the code somewhere in the initialization stage. It could be in form's OnCreate event or in form's constructor.
Now let's translate the steps into code implementation, into a procedure.
The following procedure will populate an instance of TComboBoxEx (passed as AComboBox parameter) with available drives along with their icons and volume labels. The procedure does not generate the drive list by itself. It uses drive list provided by a TStrings passed to it as ADrives parameter (please read this tutorial about this.
procedure DisplayDrivesEx(AComboBox: TComboBoxEx; ADrives: TStrings); var i, j : Integer; vItem : TComboExItem; vFileInfo: TSHFileInfo; vImgList : THandle; S, D : string; begin ADrives.BeginUpdate; try AComboBox.ItemsEx.BeginUpdate; try // clear the ComboBoxEx from possible leftover from prior operations AComboBox.ItemsEx.Clear; // if there is no drive just exit, no further processing required if ADrives.Count < 1 then Exit; // get system imagelist handle vImgList := SHGetFileInfo(PChar(ADrives), 0, vFileInfo, SizeOf(vFileInfo), SHGFI_SYSICONINDEX or SHGFI_SMALLICON); // assign the shell image list to the ComboBoxEx SendMessage(AComboBox.Handle, CBEM_SETIMAGELIST, 0, vImgList); DestroyIcon(vFileInfo.hIcon); // for each drive do the following block for i := 0 to ADrives.Count - 1 do begin // get drive information: // SHGFI_SYSICONINDEX flag indicates we want the icon index associated with the given drive // SHGFI_SMALLICON flag indicates we want small icon (16x16) // SHGFI_DISPLAYNAME flag indicates we also want shell display name for the drive (the volume label) SHGetFileInfo(PChar(ADrives[i]), 0, vFileInfo, SizeOf(vFileInfo), SHGFI_SYSICONINDEX or SHGFI_SMALLICON or SHGFI_DISPLAYNAME); // add new item in the ComboBoxEx vItem := AComboBox.ItemsEx.Add; vItem.ImageIndex := vFileInfo.iIcon; // assign the icon index S := vFileInfo.szDisplayName; // Get the drive volume label // format the volume label j := System.Pos(':)', S); if j > 0 then begin D := System.Copy(S, j-2, 4); System.Delete(S, j-2, MaxInt); S := '[' + Copy(D, 2, 2) + '] ' + S; end; vItem.Caption := S; // set the formatted volume label to the new item of the ComboBoxEx end; AComboBox.ItemIndex := 0; // activate the first item of the ComboBoxEx finally AComboBox.ItemsEx.EndUpdate; end; finally ADrives.EndUpdate; end; end;
In the demo project (full source code attached), to populate a TComboBoxEx I added the following code for a button's OnClick event.
procedure TForm1.Button1Click(Sender: TObject); begin GetDriveLetters(ListBox1.Items); // get the drive letters DisplayDrivesEx(ComboBoxEx1, ListBox1.Items); // populate the ComboBox end;
Here is screenshot of the demo project when running before we retrieve drives information.
And here is the one after we retrieve the drives information (clicking the second Refresh button).
Full source code attached, feel free to use or improve it. Looking forward for feedback on the writing or the content.