Recent

Author Topic: [Solved] Get "serial number" of (removable) disk  (Read 2162 times)

Bart

  • Hero Member
  • *****
  • Posts: 5674
    • Bart en Mariska's Webstek
Re: Get "serial number" of (removable) disk
« Reply #15 on: November 21, 2025, 03:34:35 pm »
In the mean time I've written my own parser for the lsblk output.
Maybe not very fast, but since this is about disk IO, it'll be orders of magnitude faste than the actual runnig of lsblk.

In the long run I'm plannig to make a TBlockDeviceComboBox usable on Windows and Linux, where I can fill it's contents depending onm e.g. disk type and wether or not "disk in drive".


I would love to see your parser once finished if we may?

Goto https://svn.code.sf.net/p/flyingsheep/code/trunk/MijnLib/ and check out fsidiskutils.pp and fsiunixutils.pp.
Implementation may very wel change in the next days though.
And everything about he parsing is hard-coded.

... and all I am saying is if your going with a Tcombo or TStringGrid may all the gods grant you patience and strength. It is doable but why go bald trying when a Tmemo gets the job done and very well with a mono space font  ;)  I have attached a zip which hopefully will get you started. The screenshots speak for themselevs.

Because the end goal is to have a dropdown list where the user can select the drive (Windows) or mountpoint (Linux), without having to use a TSelectDirectory dialog or something similar.
The appication I need it for resides in https://svn.code.sf.net/p/flyingsheep/code/trunk/dccopy/.
Just take a look at the interface (the main form): it's supposed to be usable by users that don't even knwo how to start a filemanager...
And, since it's simple and remembers the last time you used the inserted disk, I use it myself quite often.

The program dates back to the time that plugging a digital camera into your system requiered software by the manufacturer of the camera, and more often than not, this software ceased to function after some time (it would not recognize the camera) or had too much bloat to make it usable for me.

Bart

Aruna

  • Hero Member
  • *****
  • Posts: 787
Re: Get "serial number" of (removable) disk
« Reply #16 on: November 21, 2025, 10:47:47 pm »
I would love to see your parser once finished if we may?

Goto https://svn.code.sf.net/p/flyingsheep/code/trunk/MijnLib/ and check out fsidiskutils.pp and fsiunixutils.pp.
Implementation may very wel change in the next days though.
And everything about he parsing is hard-coded.
Thank you.

... and all I am saying is if your going with a Tcombo or TStringGrid may all the gods grant you patience and strength. It is doable but why go bald trying when a Tmemo gets the job done and very well with a mono space font  ;)  I have attached a zip which hopefully will get you started. The screenshots speak for themselevs.
Because the end goal is to have a dropdown list where the user can select the drive (Windows) or mountpoint (Linux), without having to use a TSelectDirectory dialog or something similar.
The appication I need it for resides in https://svn.code.sf.net/p/flyingsheep/code/trunk/dccopy/.
Just take a look at the interface (the main form): it's supposed to be usable by users that don't even knwo how to start a filemanager...
And, since it's simple and remembers the last time you used the inserted disk, I use it myself quite often.

The program dates back to the time that plugging a digital camera into your system requiered software by the manufacturer of the camera, and more often than not, this software ceased to function after some time (it would not recognize the camera) or had too much bloat to make it usable for me.

Bart
Hey, your runniing OS check worked like magic as can be seen in the attached screenshot. I also like your UI I think it's neat! Just wish I understood dutch. Give me some time to go through your code base and see if I can give you something useful and simple. You will probbaly find a fix way before that .... ;D

Bart

  • Hero Member
  • *****
  • Posts: 5674
    • Bart en Mariska's Webstek
Re: Get "serial number" of (removable) disk
« Reply #17 on: November 22, 2025, 12:43:12 am »
Because the end goal is to have a dropdown list where the user can select the drive (Windows) or mountpoint (Linux), without having to use a TSelectDirectory dialog or something similar.

Started implementing this (TBlockDeviceComboBox ) in FsiDeviceComboBox.

Once that works to my staisfaction, I'll start using that control in my dccopy program, so I'll be able to use it on my linyx laptop as well.
(My new Win11 laptop does not hav a cardreader, my old Win10 (now Linux Mint 22.0 MATE) does have one, so running this on my linux machine is of personal interest to me.
I'm too lazy to buy a cardreader for my new laptop ...)

[EDIT]
Updated link to FsiDeviceComboBox unit (renamed the unit).
[/EDIT]

Bart
« Last Edit: November 29, 2025, 06:15:26 pm by Bart »

Aruna

  • Hero Member
  • *****
  • Posts: 787
Re: Get "serial number" of (removable) disk
« Reply #18 on: November 22, 2025, 12:20:56 pm »
Because the end goal is to have a dropdown list where the user can select the drive (Windows) or mountpoint (Linux), without having to use a TSelectDirectory dialog or something similar.

Started implementing this (TBlockDeviceComboBox ) in FsiControls.

Once that works to my staisfaction, I'll start using that control in my dccopy program, so I'll be able to use it on my linyx laptop as well.
(My new Win11 laptop does not hav a cardreader, my old Win10 (now Linux Mint 22.0 MATE) does have one, so running this on my linux machine is of personal interest to me.
I'm too lazy to buy a cardreader for my new laptop ...)

Bart
Bart, is there a specific reason you have to go with lsblk? I was experimenting last night and realized I can get a TShellTreeView and a TShellListView working with zero lines of custom code, a lot simpler than all the if checks with lsblk. I’m not 100% sure, but I think this approach should work cross-platform on Linux, Windows, and macOS.

Basically, you just set the Root property to the directory (or logical device) you want the tree view to populate. On my system, when I plug in my USB, it mounts as /media/aruna/EMTEC 450, so I set the root to /media/aruna and everything works perfectly.

I’ve attached a zip, give it a try. It could save you a lot of headaches down the line. Anyway, I’m planning to develop this further for my own toolbox. :)

paweld

  • Hero Member
  • *****
  • Posts: 1567
Re: Get "serial number" of (removable) disk
« Reply #19 on: November 22, 2025, 02:05:58 pm »
@Bart - the easiest way is to parse the data in JSON format. Below is the modified code for the fsiunixutils.pp unit:
Code: Pascal  [Select][+][-]
  1. { FSIUnixUtils
  2.  
  3.   Copyright (C) 2025 by Bart Broersma and Flying Sheep Inc.
  4.  
  5.   This library is free software; you can redistribute it and/or modify it
  6.   under the terms of the GNU Library General Public License as published by
  7.   the Free Software Foundation; either version 2 of the License, or (at your
  8.   option) any later version with the following modification:
  9.  
  10.   As a special exception, the copyright holders of this library give you
  11.   permission to link this library with independent modules to produce an
  12.   executable, regardless of the license terms of these independent modules,and
  13.   to copy and distribute the resulting executable under terms of your choice,
  14.   provided that you also meet, for each linked independent module, the terms
  15.   and conditions of the license of that module. An independent module is a
  16.   module which is not derived from or based on this library. If you modify
  17.   this library, you may extend this exception to your version of the library,
  18.   but you are not obligated to do so. If you do not wish to do so, delete this
  19.   exception statement from your version.
  20.  
  21.   This program is distributed in the hope that it will be useful, but WITHOUT
  22.   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  23.   FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License
  24.   for more details.
  25.  
  26.   You should have received a copy of the GNU Library General Public License
  27.   along with this library; if not, write to the Free Software Foundation,
  28.   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  29. }
  30.  
  31. Unit FsiUnixUtils;
  32.  
  33. interface
  34.  
  35. uses
  36.   SysUtils, Classes, Process, FsiDiskUtils, fpjson;
  37.  
  38. type
  39.  
  40.   TDriveType = FsiDiskUtils.TDriveType;
  41.   TDriveTypes = FsiDiskUtils.TDriveTypes;
  42.  
  43.  
  44. const
  45.   NonFixedDrives = FsiDiskUtils.NonFixedDrives;
  46.   AllDriveTypes = FsiDiskUtils.AllDriveTypes;
  47.   DriveTypeStrings: array[TDriveType] of String = ('dtUnknown',
  48.     'dtNoRootDir',
  49.     'dtRemovable',
  50.     'dtFixed',
  51.     'dtRemote',
  52.     'dtCdrom',
  53.     'dtRamDisk',
  54.     'dtDisk',
  55.     'dtPart',
  56.     'dtDeviceMapper',
  57.     'dtMultipleDevices',
  58.     'dtLoop'
  59.     );
  60.  
  61.  
  62. function GetBlockDeviceInfoRecs(Refresh: Boolean): TBlockDeviceInfoRecs;
  63. function GetRemovableMountedDisks(Refresh: Boolean): TBlockDeviceInfoRecs;
  64.  
  65. implementation
  66.  
  67. type
  68.   TBlockDeviceInfoList = class(TStringList);
  69.  
  70. var
  71.   CachedBlockDeviceInfoRecs: TBlockDeviceInfoRecs = nil;
  72.   CachedBlockDeviceInfoRecsInitialized: Boolean = False;
  73.  
  74. procedure ParseBlockDeviceInfoList(Info: TBlockDeviceInfoList; out BlockDeviceInfoRecs: TBlockDeviceInfoRecs);
  75. var
  76.   i: Integer;
  77.   jd: TJsonData;
  78.   jarr: TJsonArray;
  79.   jobj: TJsonObject;
  80. //const
  81. //  BoolStr: Array[Boolean] of String = ('FALSE','TRUE');
  82. begin
  83.   BlockDeviceInfoRecs := [];
  84.   if (Info.Count = 0) then
  85.     Exit;
  86.   try
  87.     jd := GetJson(Info.Text);
  88.     if (jd.FindPath('blockdevices') <> nil) and (jd.FindPath('blockdevices').JSONType = jtArray) then
  89.     begin
  90.       jarr := TJsonArray(jd.FindPath('blockdevices'));
  91.       SetLength(BlockDeviceInfoRecs, jarr.Count);
  92.       for i := 0 to jarr.Count - 1 do
  93.       begin
  94.         jobj := jarr.Objects[i];
  95.         BlockDeviceInfoRecs[i].NAME := jobj.FindPath('name').AsString;
  96.         //writeln(format('BlkInfoRecs[%d].NAME=%s',[i,BlockDeviceInfoRecs[i].NAME]));
  97.  
  98.         if not jobj.FindPath('mountpoint').IsNull then
  99.           BlockDeviceInfoRecs[i].MountPoint := jobj.FindPath('mountpoint').AsString;
  100.         //writeln(format('BlkInfoRecs[%d].MountPoint=%s',[i,BlockDeviceInfoRecs[i].MOUNTPOINT]));
  101.  
  102.         if not jobj.FindPath('label').IsNull then
  103.           BlockDeviceInfoRecs[i].&LABEL := jobj.FindPath('label').AsString;
  104.         //writeln(format('BlkInfoRecs[%d].&LABEL=%s',[i,BlockDeviceInfoRecs[i].&LABEL]));
  105.  
  106.         if not jobj.FindPath('fstype').IsNull then
  107.           BlockDeviceInfoRecs[i].FSTYPE := jobj.FindPath('fstype').AsString;
  108.         //writeln(format('BlkInfoRecs[%d].FSTYPE=%s',[i,BlockDeviceInfoRecs[i].FSTYPE]));
  109.  
  110.         if not jobj.FindPath('type').IsNull then
  111.           BlockDeviceInfoRecs[i].&TYPE := jobj.FindPath('type').AsString;
  112.         //writeln(format('BlkInfoRecs[%d].&TYPE=%s',[i,BlockDeviceInfoRecs[i].&TYPE]));
  113.  
  114.         if not jobj.FindPath('parttype').IsNull then
  115.           BlockDeviceInfoRecs[i].PartTYPE := jobj.FindPath('parttype').AsString;
  116.         //writeln(format('BlkInfoRecs[%d].PARTTYPE=%s',[i,BlockDeviceInfoRecs[i].PARTTYPE]));
  117.  
  118.         if not jobj.FindPath('uuid').IsNull then
  119.           BlockDeviceInfoRecs[i].UUID := jobj.FindPath('uuid').AsString;
  120.         //writeln(format('BlkInfoRecs[%d].UUID=%s',[i,BlockDeviceInfoRecs[i].UUID]));
  121.  
  122.         if not jobj.GetPath('model').IsNull then
  123.           BlockDeviceInfoRecs[i].MODEL := jobj.FindPath('model').AsString;
  124.         //writeln(format('BlkInfoRecs[%d].MODEL=%s',[i,BlockDeviceInfoRecs[i].MODEL]));
  125.  
  126.         if not jobj.FindPath('tran').IsNull then
  127.           BlockDeviceInfoRecs[i].TRAN := jobj.FindPath('tran').AsString;
  128.         //writeln(format('BlkInfoRecs[%d].TRAN=%s',[i,BlockDeviceInfoRecs[i].TRAN]));
  129.  
  130.         BlockDeviceInfoRecs[i].RM := jobj.FindPath('rm').AsBoolean;
  131.         //writeln(format('BlkInfoRecs[%d].RM=%s',[i,BoolStr[BlockDeviceInfoRecs[i].RM]]));
  132.  
  133.         BlockDeviceInfoRecs[i].SIZE := jobj.FindPath('size').AsInt64;
  134.         //writeln(format('BlkInfoRecs[%d].SIZE=%d',[i,BlockDeviceInfoRecs[i].SIZE]));
  135.  
  136.         case BlockDeviceInfoRecs[i].&TYPE of
  137.           'part' : BlockDeviceInfoRecs[i].DeviceType := dtpart;
  138.           'disk' : BlockDeviceInfoRecs[i].DeviceType := dtDisk;
  139.           'rom' : BlockDeviceInfoRecs[i].DeviceType := dtCdRom;
  140.           'loop' : BlockDeviceInfoRecs[i].DeviceType := dtLoop;
  141.           'lvm', 'crypt', 'dmraid', 'mpath', 'path', 'dm' : BlockDeviceInfoRecs[i].DeviceType := dtDeviceMapper;
  142.           'md', 'linear', 'raid0', 'raid1', 'raid4', 'raid5', 'raid10', 'multipath' : BlockDeviceInfoRecs[i].DeviceType := dtMultipleDevices;
  143.           otherwise BlockDeviceInfoRecs[i].DeviceType := dtUnknown;
  144.         end;//case
  145.  
  146.         //writeln;
  147.       end;
  148.     end;
  149.   finally
  150.     jd.Free;
  151.   end;
  152. end;
  153.  
  154. procedure GetBlockDeviceInfoList(BlockDeviceInfoList: TBlockDeviceInfoList);
  155. var
  156.   Proc: TProcess;
  157.   i: Integer;
  158. begin
  159.   BlockDeviceInfoList.Clear;
  160.   Proc := TProcess.Create(nil);
  161.   //lsblk -l -e 7 -A -o NAME,MOUNTPOINT,LABEL,FSTYPE,TYPE,PARTTYPE,UUID,MODEL,TRAN,RM,SIZE
  162.   //lsblk -l -a -o NAME,MOUNTPOINT,LABEL,FSTYPE,TYPE,PARTTYPE,UUID,MODEL,TRAN,RM,SIZE
  163.  
  164.   try
  165.   Proc.Executable := 'lsblk';  //ToDo: get this from path  ??
  166.   Proc.Parameters.Add('-l'); //list
  167.   Proc.Parameters.Add('-J'); //as json
  168.   Proc.Parameters.Add('-b'); //size in bytes
  169.   //Proc.Parameters.Add('-e7'); //exclude RAM drives
  170.   Proc.Parameters.Add('-a');
  171.   Proc.Parameters.Add('-o');
  172.   Proc.Parameters.Add('NAME,MOUNTPOINT,LABEL,FSTYPE,TYPE,PARTTYPE,UUID,MODEL,TRAN,RM,SIZE');
  173.   Proc.Options := Proc.Options + [poWaitOnExit, poUsePipes];
  174.   Proc.Execute;
  175.   BlockDeviceInfoList.LoadFromStream(Proc.Output);
  176.   for i := BlockDeviceInfoList.Count - 1 downto 0 do
  177.     if (Trim(BlockDeviceInfoList[i])='') then
  178.       BlockDeviceInfoList.Delete(i);
  179.   finally
  180.     Proc.Free;
  181.   end;
  182. end;
  183.  
  184.  
  185. function GetBlockDeviceInfoRecs(Refresh: Boolean): TBlockDeviceInfoRecs;
  186. var
  187.   BlockDeviceInfoList: TBlockDeviceInfoList;
  188. begin
  189.   if (not Refresh) and CachedBlockDeviceInfoRecsInitialized then
  190.     Exit(CachedBlockDeviceInfoRecs);
  191.   Result := [];
  192.   BlockDeviceInfoList := TBlockDeviceInfoList.Create;
  193.   try
  194.     GetBlockDeviceInfoList(BlockDeviceInfoList);
  195.     ParseBlockDeviceInfoList(BlockDeviceInfoList,  CachedBlockDeviceInfoRecs);
  196.     CachedBlockDeviceInfoRecsInitialized := True;
  197.     Result := CachedBlockDeviceInfoRecs;
  198.   finally
  199.     BlockDeviceInfoList.Free;
  200.   end;
  201. end;
  202.  
  203. function GetRemovableMountedDisks(Refresh: Boolean): TBlockDeviceInfoRecs;
  204. var
  205.   i: Integer;
  206. begin
  207.   if Refresh then
  208.     GetBlockDeviceInfoRecs(True);
  209.   Result := [];
  210.   for i := 0 to Length(CachedBlockDeviceInfoRecs) - 1 do
  211.   begin
  212.     if (CachedBlockDeviceInfoRecs[i].RM) and (CachedBlockDeviceInfoRecs[i].MOUNTPOINT <> '') then
  213.     begin
  214.       SetLength(Result, Length(Result) + 1);
  215.       Result[Length(Result) - 1] := CachedBlockDeviceInfoRecs[i];
  216.     end;
  217.   end;
  218. end;
  219.  
  220. initialization
  221.  
  222. finalization
  223.   CachedBlockDeviceInfoRecs := nil;
  224. end.
  225.  

Changes in the ParseBlockDeviceInfoList and GetBlockDeviceInfoList (here I only added new parameters) procedures.
« Last Edit: November 22, 2025, 02:09:26 pm by paweld »
Best regards / Pozdrawiam
paweld

Bart

  • Hero Member
  • *****
  • Posts: 5674
    • Bart en Mariska's Webstek
Re: Get "serial number" of (removable) disk
« Reply #20 on: November 22, 2025, 07:11:20 pm »
@paweld: thanks for the example.
I chose a "light weight" solution for parsing.
Cumbersome, but at least I can understand the code an where/why it goes wrong (if it does).
Didn't work with JSON a lot, so did not consider it at all.

I included your code (as comment) in the unit for reference.
Might use it in the (near) future.

Bart

« Last Edit: November 22, 2025, 07:17:21 pm by Bart »

Bart

  • Hero Member
  • *****
  • Posts: 5674
    • Bart en Mariska's Webstek
Re: Get "serial number" of (removable) disk
« Reply #21 on: November 29, 2025, 06:09:43 pm »
Bart, is there a specific reason you have to go with lsblk? I was experimenting last night and realized I can get a TShellTreeView and a TShellListView working with zero lines of custom code, a lot simpler than all the if checks with lsblk.

AFAIK then TShellTreeView/TShellListView have no notice of:
  • wether or not a drive (Windows) or folder represents a removable mounted device like an US stick or an SDCard
  • the UUID of the disk (well, actually UUID of the filesystem on that disk)

Both of the above is what I need.

Bart

 

TinyPortal © 2005-2018