View RSS Feed

Development Team Blog

How to get volume information

Rating: 6 votes, 4.33 average.
Always wanted to know what drives are available in your computer? Wanted to know what the status of the drive is? Wanted to know the serialnumber (e.g. 503E-A401)? Wanted to know is filename casing preserved? No... then go to the next blog. Yes, read on...

Drives
Visual DataFlex has a command called GetDskInfo. You pass it an integer and because drives can be assigned to a drive letter there is a maximum of 26 drives. So we can make a loop through all the drives. In the following code this is done inside the event OnCreateTree of an TreeView object.

Code:
Procedure OnCreateTree
    Integer iDrive iDriveStatus
    String sDrive
    Handle hItem
    
    For iDrive from 1 to 26
        GetDskInfo iDrive iDriveStatus
        If (iDriveStatus <> Drive_root_not_exist) Begin
            Move (Character (iDrive + 64) - ":") to sDrive
            
            Get AddTreeItem sDrive 0 iDrive 0 0 to hItem
        End
    Loop
End_Procedure
As you can see we add 64 to the drive number to get the drive letter. If the drive does not exists (no Net Use or Subst) the drive status will be Drive_Root_Not_Exist. The treeview therefor only shows the drives available.

When the user navigates through a treeview the event OnItemChanged fires and we can use this to start a process of querying the drive/volume information. In my sample application I decided to show the information in a RichEdit object because I can make certain information bold in a control of this class.

Code:
Procedure OnItemChanged Handle hItem Handle hItemOld
    String sRootPath
    Integer iDrive
    
    Get ItemLabel hItem to sRootPath
    Get ItemData hItem to iDrive
    
    Send ShowVolumeInfo of oVolumeInfoRichEdit (sRootPath - '\') iDrive 
End_Procedure
Windows API Function
For the information about a volume we can use the GetVolumeInformation Windows API function. We declare (import) the function with the following instruction;

Code:
External_Function GetVolInfo "GetVolumeInformationA" Kernel32.Dll;
    Address aRoot Address aVName Integer nVNameSize Address aVSerNum ;
    Address aMaxCLen Address aFSysFlag Address aFSysName ;
    Integer nFSysNameSize Returns Integer
The names of the arguments are not important but are pretty much equal to what the Window API documentation lists.

Using the function is done with:

Code:
Move (GetVolInfo (AddressOf (sRootPathName), AddressOf (sVolumeNameBuffer), ;
    (MAX_PATH+1), AddressOf (iSerialNumber), AddressOf (iMaximumComponentLength), ;
    AddressOf (iFileSystemFlags), AddressOf (sFileSystemName), (MAX_PATH+1))) ;
    to iResult
Some of the parameters are defined as "__out" which means that the function call will write its information in the memory allocated for that parameter. Instead of passing the variable we pass the AddressOf() the variable, the memory location. To obtain memory location of a variable the variable needs to be initialized or in case of the strings have the space available to write the information back into. The following code does that:

Code:
ZeroString (MAX_PATH+1) to sVolumeNameBuffer
ZeroString (MAX_PATH+1) to sFileSystemName
Move 0 to iSerialNumber
Move 0 to iMaximumComponentLength
Move 0 to iFileSystemFlags
Of course to be called before the GetVolInfo() is executed!

Show the Results
As mentioned before I created a cRichEdit object to show the information. The method to get the volume information is also coded inside this object. To show the information as illustrated below we have to toggle the pbBold boolean property to true and false before and after adding information. Instead of writing many lines of code I added a method called AppendTextBold to the RichEdit object. You could also add it to a subclass if you were planning to use this more often.
Code:
Procedure AppendBoldText String sText
    Set pbBold to True
    Send AppendText sText
    Set pbBold to False
End_Procedure
The Windows API function returns some strings that we can simply add to the editor control with a line like:
Code:
Send AppendText "VolumeName: " 
Send AppendBoldText (Cstring (sVolumeNameBuffer))
Send AppendTextLn " "
but it also contains integer values like the flags (iFileSystemFlags) and an integer that represent the volume's serialnumber.

We can use the IsFlagIn() function to find out if a certain flag has been set. I used a slightly modified version that returns the words "true" and "false" because they are easier to understand for someone that looks at the picture. The code for that is:
Code:
Function FlagStatus Integer iFlag Integer iFlags Returns String
    If (IsFlagIn (iFlag, iFlags)) Begin
        Function_Return "True"
    End
    
    Function_Return "False"
End_Function
The serialnumber is usually shown as a hexadecimal value in 2 groups of 4 characters. We use a function named Dec2Hex written a long time ago for DFCONFIG.
Code:
Function Dec2Hex Global Integer iDec Returns String
    String sHex
    
    Repeat
        Move (Insert (Mid ("0123456789ABCDEF", 1, ((iDec iand |CI$0F) + 1)), sHex, 1)) to sHex
        Move (iDec / |CI$10) to iDec
    Until (iDec = 0)
    
    Function_Return (Right (Trim ("0000" - sHex), 4))
End_Function
The serial number we split with the folded integer splitting functions Hi() and Low(). So the code is:
Code:
(Dec2Hex (Hi (iSerialNumber)))
The full source is:
Code:
Use Windows.pkg
Use Dll.pkg
Use cApplication.pkg
Use dfTreeVw.pkg
Use cRichEdit.pkg

Object oApplication is a cApplication
End_Object

Function Dec2Hex Global Integer iDec Returns String
    String sHex
    
    Repeat
        Move (Insert (Mid ("0123456789ABCDEF", 1, ((iDec iand |CI$0F) + 1)), sHex, 1)) to sHex
        Move (iDec / |CI$10) to iDec
    Until (iDec = 0)
    
    Function_Return (Right (Trim ("0000" - sHex), 4))
End_Function

Function Hex2Dec Global String sHex Returns Integer
    Function_Return ("$" + Trim (sHex))
End_Function

// Value                      Meaning
//
// FS_CASE_IS_PRESERVED       If this flag is set, the file system preserves the case
//                            of filenames when it places a name on disk.
// FS_CASE_SENSITIVE          If this flag is set, the file system supports case-sensitive
//                            filenames.
// FS_UNICODE_STORED_ON_DISK  If this flag is set, the file system supports Unicode in
//                            filenames as they appear on disk.
// FS_PERSISTENT_ACLS         If this flag is set, the file system preserves and enforces
//                            ACLs. For example, NTFS preserves and enforces ACLs and FAT
//                            does not.
// FS_FILE_COMPRESSION        The file system supports file-based compression.
// FS_VOL_IS_COMPRESSED       The specified volume is a compressed volume; for example,
//                            a DoubleSpace volume.

Define FILE_CASE_SENSITIVE_SEARCH for |CI$00000001
Define FILE_CASE_PRESERVED_NAMES  for |CI$00000002
Define FILE_UNICODE_ON_DISK       for |CI$00000004
Define FILE_PERSISTENT_ACLS       for |CI$00000008
Define FILE_FILE_COMPRESSION      for |CI$00000010
Define FILE_VOLUME_IS_COMPRESSED  for |CI$00008000

Define FS_CASE_IS_PRESERVED      for FILE_CASE_PRESERVED_NAMES
Define FS_CASE_SENSITIVE         for FILE_CASE_SENSITIVE_SEARCH
Define FS_UNICODE_STORED_ON_DISK for FILE_UNICODE_ON_DISK
Define FS_PERSISTENT_ACLS        for FILE_PERSISTENT_ACLS
Define FS_VOL_IS_COMPRESSED      for FILE_VOLUME_IS_COMPRESSED
Define FS_FILE_COMPRESSION       for FILE_FILE_COMPRESSION

External_Function GetVolInfo "GetVolumeInformationA" Kernel32.Dll;
    Address aRoot Address aVName Integer nVNameSize Address aVSerNum Address aMaxCLen Address aFSysFlag Address aFSysName Integer nFSysNameSize Returns Integer

Object oPanel is a BasicPanel
    Set Location to 3 3
    Set Label to "Volume Information"
    Set Icon to "Default.Ico"
    Set Size to 267 410

    Object oDrivesTreeView is a TreeView
        Set Size to 257 100
        Set Location to 5 5
        Set peAnchors to anTopBottomLeft

        Procedure OnCreateTree
            Integer iDrive iDriveStatus
            String sDrive
            Handle hItem
            
            For iDrive from 1 to 26
                GetDskInfo iDrive iDriveStatus
                If (iDriveStatus <> Drive_root_not_exist) Begin
                    Move (Character (iDrive + 64) - ":") to sDrive
                    
                    Get AddTreeItem sDrive 0 iDrive 0 0 to hItem
                End
            Loop
        End_Procedure    
        
        Procedure OnItemChanged Handle hItem Handle hItemOld
            String sRootPath
            Integer iDrive
            
            Get ItemLabel hItem to sRootPath
            Get ItemData hItem to iDrive
            
            Send ShowVolumeInfo of oVolumeInfoRichEdit (sRootPath - '\') iDrive 
        End_Procedure
    End_Object

    Object oVolumeInfoRichEdit is a cRichEdit
        Set Size to 257 296
        Set Location to 5 110
        Set peAnchors to anAll
    
        Procedure AppendBoldText String sText
            Set pbBold to True
            Send AppendText sText
            Set pbBold to False
        End_Procedure
        
        Function FlagStatus Integer iFlag Integer iFlags Returns String
            If (IsFlagIn (iFlag, iFlags)) Begin
                Function_Return "True"
            End
            
            Function_Return "False"
        End_Function
    
        Procedure ShowVolumeInfo String sRootPathName Integer iDrive
            Integer iSerialNumber iMaximumComponentLength iFileSystemFlags
            String sVolumeNameBuffer sFileSystemName sDriveStatus
            Integer iResult iDriveStatus
        
            ZeroString (MAX_PATH+1) to sVolumeNameBuffer
            ZeroString (MAX_PATH+1) to sFileSystemName
            Move 0 to iSerialNumber
            Move 0 to iMaximumComponentLength
            Move 0 to iFileSystemFlags
        
            Move (GetVolInfo (AddressOf (sRootPathName), AddressOf (sVolumeNameBuffer), (MAX_PATH+1), ;
                AddressOf (iSerialNumber), AddressOf (iMaximumComponentLength), AddressOf (iFileSystemFlags), ;
                AddressOf (sFileSystemName), (MAX_PATH+1))) to iResult
                
            Send Delete_Data
            
            GetDskInfo iDrive iDriveStatus
            
            Case Begin
                Case (iDriveStatus = Drive_not_available)
                    Move "Drive not available" to sDriveStatus
                    Case Break
                Case (iDriveStatus = Drive_removable)
                    Move "Drive is Removable" to sDriveStatus
                    Case Break
                Case (iDriveStatus = Drive_fixed)
                    Move "Drive is Fixed" to sDriveStatus
                    Case Break
                Case (iDriveStatus = Drive_remote)
                    Move "Drive is Remote" to sDriveStatus
                    Case Break
                Case (iDriveStatus = Drive_cdrom)
                    Move "Drive is CDROM" to sDriveStatus
                    Case Break
                Case (iDriveStatus = Drive_ramdisk)
                    Move "Drive is RAM Disk" to sDriveStatus
                    Case Break
            Case End
            
            Send AppendText "Drive:"        
            Send AppendBoldText (String (iDrive) * sRootPathName  * sDriveStatus)
            Send AppendTextLn " "
        
            If (iResult = 1) Begin
                Send AppendText "VolumeName: " 
                Send AppendBoldText (Cstring (sVolumeNameBuffer))
                Send AppendTextLn " "
                
                Send AppendText "VolumeSerialNumber: " 
                Send AppendBoldText (Dec2Hex (Hi (iSerialNumber)) - "-" - Dec2Hex (Low (iSerialNumber)))
                Send AppendTextLn " "
                
                Send AppendText "MaximumComponentLength: " 
                Send AppendBoldText iMaximumComponentLength
                Send AppendTextLn " "
                
                Send AppendText "FS_CASE_IS_PRESERVED: " 
                Send AppendBoldText (FlagStatus (Self, FS_CASE_IS_PRESERVED, iFileSystemFlags))
                Send AppendTextLn " "
                
                Send AppendText "FS_CASE_SENSITIVE: " 
                Send AppendBoldText (FlagStatus (Self, FS_CASE_SENSITIVE, iFileSystemFlags))
                Send AppendTextLn " "
                
                Send AppendText "FS_UNICODE_STORED_ON_DISK: " 
                Send AppendBoldText (FlagStatus (Self, FS_UNICODE_STORED_ON_DISK, iFileSystemFlags))
                Send AppendTextLn " "
                
                Send AppendText "FS_PERSISTENT_ACLS: " 
                Send AppendBoldText (FlagStatus (Self, FS_PERSISTENT_ACLS, iFileSystemFlags))
                Send AppendTextLn " "
                
                Send AppendText "FS_VOL_IS_COMPRESSED: " 
                Send AppendBoldText (FlagStatus (Self, FS_VOL_IS_COMPRESSED, iFileSystemFlags))
                Send AppendTextLn " "
                
                Send AppendText "FS_FILE_COMPRESSION: " 
                Send AppendBoldText (FlagStatus (Self, FS_FILE_COMPRESSION, iFileSystemFlags))
                Send AppendTextLn " "
                
                Send AppendText "FileSystemName: " 
                Send AppendBoldText (Cstring (sFileSystemName))
                Send AppendTextLn " "
            End
        End_Procedure
    End_Object
End_Object

Start_UI oPanel
Attached Thumbnails Attached Thumbnails Click image for larger version. 

Name:	VolumeInfo.jpg 
Views:	1864 
Size:	74.8 KB 
ID:	3766  

Comments