Accessing Microsoft Windows libraries.
Windows libraries are some kind of virtual storage locations, identified by a name, that appear in the filesystem tree, but that in fact are just containers to group folders, located at different places on the fixed disk or an external storage device. Common libraries of the newer Windows versions include Documents, Pictures, Music and Videos; in Windows 10 there are two supplementary libraries called "Camera Roll" and "Saved Pictures". By default, these libraries contain one single folder: the user documents, pictures, etc folder. By configuring a library, you can add other folders, as for example the public documents, pictures, etc folder. But, you can also add any other folder to a library. On my system, for example, I added a folder for the album covers (that I created in my user directory) to the Music library. As space on a 521GB SSD disk is limited, I only keep my favorite music songs on my fixed disk, the others being stored in the "Music 2" folder on an USB disk. In order to have all my music together, even though the files are actually located at different places, I added the "Music 2" folder on the USB disk to my Music library. Users can also create custom libraries, with folders at any place within the filesystem and with any content, they want. I use a library called "Programming" (for Free Pascal and other programming related files), with a folder called "Programming" in my user directory and another one, called "Public Programming" in the public user directory.
The question, that I asked to myself, was how I had to proceed if I wanted to access my Music library from a Free Pascal application. Libraries are "not really existing", thus it is not possible to access them with a file path as you would do for folders. Creating a file, where, for each library, the included folders are listed, would have been a possibility. But what I really wanted was to find a way to access Windows libraries in a general way, without any explicitly given knowledge of the folders that are part of them.
As I did not find any information about Free Pascal accessing Windows libraries on the net, I had to figure it out myself. In fact, the files, listing the folders included in a library, together with other information, exist on each Windows that supports libraries. There is a description file for each library (standard or user created), the file name being the library name (Documents, Music, ...), the extension being .library-ms, and located in C:\Users\{user_name}\AppData\Roaming\Microsoft\Windows\Libraries (you have to select to display hidden files, in order to see the AppData folder in File Explorer). Accessing library data from Free Pascal may thus be done by parsing the .library-ms files, extracting the data needed (in particular, the paths of the included folders) and then accessing the library folders as any other folders within the filesystem.
No idea why, but I did decide to use OO to write my WinLibs unit. The second object oriented program in my whole life, so, please, have some indulgence, if, perhaps, you find my code unprofessional. The unit allows basic (read-only) access to the libraries, allowing to get the names of all libraries for the current user (libraries are user-dependent), to get some general information about a given library and to get some information about the folders included in a given library (incl. the folders' path, of course). Extending the unit's functionalities (reading the files, doing write operations, etc) should not be to difficult to implement. Feel free to customize the unit in order to suit your needs.
Click the following link to download the WinLibs source code. Note, that the download archive actually is a simple command line program, that shows how to use the methods defined within the unit.
WinLibs requirements.
WinLibs needs the following 5 units: Classes, SysUtils, FileUtil, LazUTF8, and Dos. In the case of a command line program, you'll have to add the LazUtils requirement in Project Inspector in order to be able to use FileUtil and LazUTF8.
WinLibs variable types, classes and methods.
The 3 methods, implemented in the unit (cf. below) return custom types, declared as follows:
type
TLibNames = array of string;
TLibInfo = record
LibName, LibType, LibContent: string;
NLibFolders: Integer;
end;
TLibFolderInfo = array of record
FolderName, FolderPath, FolderType: string;
end;
Types description:
- TLibNames variables contain the library names (defining a custom type instead of using array of string allows to set the array length within the method that returns it).
- TLibInfo variables contain information about a given library: the name, the type (standard Windows or custom = user created library) and the library content (standard Windows content such as documents, pictures, etc, or "other").
- TLibFolderInfo variables are arrays with record elements that contain information about the folders of a given library: the name, the full path, and the folder type (default save location = normally a subfolder of the user directory; public save location = normally a folder in C:\Users\Public, or otherwise left blank).
There are two classes called TLibrary and TLibraries respectively. A TLibraries object contains the data of all libraries. It contains an array of TLibrary objects, that contain the data of a given library. All of the TLibrary methods are private, thus users can not access these objects directly. Instead, data is accessed by calling a method of TLibraries with an argument indicating the name of the library to read data from. Maybe this approach is not, how a confirmed programmer would have done; as I said, I myself, I'm a complete OO newbie. There are 3 public methods for the TLibraries class:
- GetLibraryNames: Get names of all Windows libraries (for current user).
- GetLibraryInfo: Get info about a given library (identified by the library name).
- GetLibraryFolderInfo: Get info about all (first child) folders being part of a given library (identified by the library name).
Here the class declarations:
type
TLibrary = class
constructor Create(LName: string);
destructor Destroy; override;
private
LibName, LibType, LibContent: string;
NLibFolders: Integer;
LibFolderInfo: TLibFolderInfo;
procedure ReadLibraryData(LDir: string);
end;
TLibraries = class
constructor Create(IncludeSpecial: Boolean = False);
destructor Destroy; override;
function GetLibraryNames: TLibNames;
function GetLibraryInfo(LName: string): TLibInfo;
function GetLibraryFolderInfo(LName: string; IncludeUser: Boolean = True; IncludePublic: Boolean = True; IncludeOther:
Boolean = True): TLibFolderInfo;
private
NLibraries: Integer;
Libraries: array of TLibrary;
procedure Add(Ldir, LName: string);
end;
WinLibs public methods details.
I will not publish the method code here, only giving a short description, what they do, and, at the end of the tutorial, showing how to use them in practice.
TLibraries.Create.
The TLibraries constructor creates an object that contains the data of all libraries of the current user. It uses the function FindAllFiles of the FileUtil unit to get the names of the library description files (.library-ms files), that also is the name of the library itself, and for each of these files, calls the private method TLibraries.Add method to add a TLibrary object to the TLibraries.Libraries array. TLibraries.Add calls TLibrary.Create (with the library name as argument) to create the TLibrary object and TLibrary.ReadLibraryData (cf. below) to fill in the data, as read from the parsed .library-ms file.
TLibraries.Create has an optional Boolean argument, that allows to include (True) resp. to exclude (False = default) the special libraries Camera Roll and Saved Pictures into the library list.
TLibraries.Destroy.
The destructor TLibraries.Destroy first destroys all TLibrary objects, then destroys the TLibraries object itself.
TLibraries.GetLibraryNames.
This method returns the names of all libraries in the list as a TLibNames (array of string) type. If there are no libraries, a zero-length array is returned.
TLibraries.GetLibraryInfo.
With the library name as argument, this method returns the information concerning this library as a TLibInfo (record) type (cf. above). If there is no library with the name passed as argument, the number of library folders returned is 0, all other return values are empty strings.
TLibraries.GetLibraryFolderInfo.
With the library name as argument, this method returns the information concerning the folders being part of this library as TLibFolderInfo (array of records) type (cf. above). If there are no folders found, a zero-length array is returned.
Beside the library name, the TLibraries.GetLibraryFolderInfo method has three optional Boolean arguments that allow to include (True = default) resp to exclude (False) a given folder type. Concerned folders (in this order!): user folder (default save location), public folder (public save location), custom folders.
Short description of the TLibrary.ReadLibraryData method.
The (private) method TLibrary.ReadLibraryData is called by TLibraries.Create to fill in the data of the newly created TLibrary object. This data is retrieved from the corresponding library description (.library-ms) file.
Content of my "Music" library (standard Windows library) description file.
Content of my "Programming" library (custom library) description file.
Library type.
If <url> of one of the library folders contains a standard Microsoft folder type identifier, the library type value is set to "standard {something} library" (where {something} is "documents", "pictures", etc. If this is not the case, the type value is set to "custom library" (as for example for my Programming library).
Library content.
If <folderType> contains a standard Microsoft folder content identifier, the library content value is set to the corresponding file content (documents, pictures, etc). If this is not the case, the content type is set to "other".
Folder path.
The <url> of a standard library folder contains a standard Microsoft folder type identifier. This uniquely identifies the folder, without naming the folder path. However, on Windows systems, such folders are located at a precise place within the filesystem tree. For the user documents, pictures, music and video folders, they are subfolders of C:\Users\{User name}. Camera roll and Saved Pictures are subfolders of C:\Users\{User name}\Pictures. And public documents, pictures, music and video folders are subfolders of C:\Users\Public. For non-standard (user-added) folders, <url> contains the full path to the folder.
Folder type.
The folder type value contains information about the storage type of this folder: default (= user) storage location, public storage location, or empty string if none of these. The value is determined by looking at the content of <isDefaultSaveLocation> and <isDefaultNonOwnerSaveLocation>.
WinLibs usage examples.
Example 1: List of libraries directories.
program libdirs;
{$mode objfpc}{$H+}
uses
Classes, WinLibs;
var
I, J: Integer;
Libs: TLibraries;
LibNames: TLibNames;
LibInfo: TLibInfo;
Folders: TLibFolderInfo;
begin
Writeln('List of Windows libraries directories'); Writeln;
// Create the TLibraries object
Libs := TLibraries.Create(True);
// Get and display names of all directories
LibNames := Libs.GetLibraryNames;
Writeln('Libraries:');
for I := 0 to Length(LibNames) - 1 do
Write(' ', LibNames[I], ' ');
Writeln; Writeln;
// For each library, get and display library and folder info
for I := 0 to Length(LibNames) - 1 do begin
LibInfo := Libs.GetLibraryInfo(LibNames[I]);
Folders := Libs.GetLibraryFolderInfo(LibNames[I]);
// Library info
Writeln(LibInfo.LibName, ': ', LibInfo.LibType, ' (', LibInfo.LibContent, '), Folders = ', LibInfo.NLibFolders);
for J := 0 to Length(Folders) - 1 do begin
// Folder info
Write(' ', Folders[J].FolderPath);
if Folders[J].FolderType <> '' then
Write(' (', Folders[J].FolderType, ')');
Writeln;
end;
Writeln;
end;
Libs.Destroy;
Writeln; Write('Hit ENTER to terminate the program...'); Readln;
end.
Example 2: List of album and song MP3 in Music library.
program musiclib;
{$mode objfpc}{$H+}
uses
Classes, SysUtils, FileUtil, WinLibs;
var
N, I, J: Integer;
Temp: string;
MusicFilesList: TStringList;
MusicFiles: array of string;
Libs: TLibraries;
Folders: TLibFolderInfo;
begin
Writeln('List of all MP3 albums and songs in the Music library'); Writeln;
// Create the TLibraries object
Libs := TLibraries.Create;
// Get music library folder info. The call of the method as done here, includes the user music folder and any extra folders,
// excluding the public music folder. That is how music files are stored on my system: the best albums and songs in the user
// music folders, the others on an USB disk (the public music folder containing non-album/songs related MP3)
Folders := Libs.GetLibraryFolderInfo('Music', True, False, True);
// For each folder (including its subfolders), read MP3 files
N := 0;
for I := 0 to Length(Folders) - 1 do begin
// Get music files list
MusicFilesList := FindAllFiles(Folders[I].FolderPath, '*.mp3', True);
// Store music filenames into array
for J := 0 to MusicFilesList.Count - 1 do begin
Inc(N); SetLength(MusicFiles, N);
MusicFiles[N - 1] := ExtractFilename(MusicFilesList.Strings[J]);
end;
MusicFilesList.Free;
end;
// Sort the music files array
for I := 0 to Length(MusicFiles) - 2 do begin
for J := I + 1 to Length(MusicFiles) - 1 do begin
if MusicFiles[I] > MusicFiles[J] then begin
Temp := MusicFiles[I]; MusicFiles[I] := MusicFiles[J]; MusicFiles[J] := Temp;
end;
end;
end;
// Display the music files names
for I := 0 to Length(MusicFiles) - 1 do begin
Writeln(MusicFiles[I]);
end;
Libs.Destroy;
Writeln; Write('Hit ENTER to terminate the program...'); Readln;
end.
If you find this text helpful, please, support me and this website by signing my guestbook.