How to Read/Write Windows 7 Library Locations?
delphi, registry, windows
Solution
Use one of the `SHLoadLibraryFrom...()` functions, like `SHLoadLibraryFromKnownFolder()`, to get an `IShellLibrary` interface, then you can use its methods to enumerate and manipulate the Library, such as `IShellLibrary::GetFolders()`, `IShellLibrary::AddFolder()`, `IShellLibrary::RemoveFolder()`, etc.
Update: For example:
uses
..., ActiveX, KnownFolders, ShlObj;
// The SHLoadLibraryFrom...() functions are implemented inline in the Win32 SDK
// shobjidl.h header file, so you have to implement them manually in your
// code if you you are not using a version of Delphi that already implements
// them in the RTL's ShlObj.pas unit for you...
// SHLoadLibraryFromKnownFolder() is defined wrong in ShlObj.pas!!! See QC #109306
function My_SHLoadLibraryFromKnownFolder(const kfidLibrary: TGUID; grfMode: DWORD; riid: TIID; out ppv): HRESULT;
var
plib: IShellLibrary;
begin
Result := CoCreateInstance(CLSID_ShellLibrary, nil, CLSCTX_INPROC_SERVER, IShellLibrary, plib);
if SUCCEEDED(Result) then
begin
Result := plib.LoadLibraryFromKnownFolder(kfidLibrary, grfMode);
if SUCCEEDED(Result) then
Result := plib.QueryInterface(riid, ppv);
end;
end;
function GetLibraryFileSystemFolders(FolderID: TGUID; Folders: TStrings): Boolean;
var
SL: IShellLibrary;
Arr: IShellItemArray;
Enum: IEnumShellItems;
Item: IShellItem;
Path: LPWSTR;
begin
Result := False;
if FAILED(My_SHLoadLibraryFromKnownFolder(FolderID, STGM_READ, IShellLibrary, SL)) then
Exit;
if FAILED(SL.GetFolders(LFF_FORCEFILESYSTEM, IShellItemArray, Arr)) then
Exit;
if FAILED(Arr.EnumItems(Enum)) then
Exit;
while Enum.Next(1, Item, nil) = S_OK then
begin
if FAILED(Item.GetDisplayName(SIGDN_FILESYSPATH, Path)) then
Exit;
try
Folders.Add(Path);
finally
CoTaskMemFree(Path);
end;
Item := nil;
end;
Result := True;
end;
.
var
Folders: TStringList;
begin
Folders := TStringList.Create;
try
if GetLibraryFileSystemFolders(FOLDERID_DocumentsLibrary, Folders) then
begin
//...
end;
finally
Folders.Free;
end;
end;
Update: `SHLoadLibraryFromKnownFolder()` only works for Microsoft-defined Libraries that have `KNOWNFOLDERID` values defined. If you want to access custom Libraries, you have to use a slightly modified approach to obtaining the `IShellLibrary` interface, eg:
// SHLoadLibraryFromItem() is defined wrong in ShlObj.pas!!! See QC #109306
function My_SHLoadLibraryFromItem(const psiLibrary: IShellItem; grfMode: DWORD; const riid: TIID; out ppv): HResult;
var
plib: IShellLibrary;
begin
Result := CoCreateInstance(CLSID_ShellLibrary, nil, CLSCTX_INPROC_SERVER, IID_IShellLibrary, plib);
if Succeeded(Result) then
begin
Result := plib.LoadLibraryFromItem(psiLibrary, grfMode);
if Succeeded(Result) then
Result := plib.QueryInterface(riid, ppv);
end;
end;
function GetShellLibraryforLibrary(const LibraryName: String; grfMode: DWORD; var ppv: IShellLibrary): Boolean;
var
SL: IShellLibrary;
Enum: IEnumShellItems;
Item: IShellItem;
DisplayName: LPWSTR;
hr: HRESULT;
begin
Result := False;
ppv := nil;
if FAILED(SHGetKnownFolderItem(FOLDERID_Libraries, 0, 0, IShellItem, PPointer(@Item)^) then
Exit;
hr := Item.BindToHandler(nil, BHID_EnumItems, IEnumShellItems, Enum);
if FAILED(hr) then
Exit;
Item := nil;
while Enum.Next(1, Item, nil) = S_OK do
begin
if FAILED(Item.GetDisplayName(SIGDN_NORMALDISPLAY, DisplayName)) then
Exit;
try
if AnsiSameText(DisplayName, LibraryName) then
begin
Result := SUCCEEDED(My_SHLoadLibraryFromItem(Item, grfMode, IShellLibrary, ppv));
Break;
end;
finally
CoTaskMemFree(DisplayName);
end;
Item := nil;
end;
end;
function GetLibraryFileSystemFolders(const LibraryName: String; Folders: TStrings): Boolean;
var
SL: IShellLibrary;
Arr: IShellItemArray;
Enum: IEnumShellItems;
Item: IShellItem;
Path: LPWSTR;
begin
Result := False;
if not GetShellLibraryforLibrary(LibraryName, STGM_READ, SL) then
Exit;
if FAILED(SL.GetFolders(LFF_FORCEFILESYSTEM, IShellItemArray, Arr)) then
Exit;
if FAILED(Arr.EnumItems(Enum)) then
Exit;
while Enum.Next(1, Item, nil) = S_OK then
begin
if FAILED(Item.GetDisplayName(SIGDN_FILESYSPATH, Path)) then
Exit;
try
Folders.Add(Path);
finally
CoTaskMemFree(Path);
end;
Item := nil;
end;
Result := True;
end;
.
var
Folders: TStringList;
begin
Folders := TStringList.Create;
try
if GetLibraryFileSystemFolders('MyLibrary', Folders) then
begin
//...
end;
finally
Folders.Free;
end;
end;
Problem
Intro In Windows 7 you have some special folders for documents, pictures and music etc called Libraries. If you are not aware of them, basically each Library folder can contain Locations (paths) which are basically shortcuts for each Library. Some examples: `Documents (Library)` - E:\Personal\Documents `(Location)` - F:\Backups\Documents `(Location)` `Music (Library)` - E:\Media\Music\Albums `(Location)` - E:\Media\Music\Singles `(Location)` `Pictures (Library)` - E:\Media\Photos `(Location)` When you click any of these Library folders from Windows Explorer or Start menu, Windows Explorer will show with the Locations defined inside that Library. Task What I need to be able to do is read the Locations for each Library type, and be able to write back (update) the Library with my own Locations. I have found that the Libraries are stored in the user AppData folder like this: `C:\Users\SOMEUSER\AppData\Roaming\Microsoft\Windows\Libraries` These Libraries are this file type: Library (.library-ms) - if you right click on one and select properties, Library tab you can see the Library locations associated with that Library. I don't see a way of extracting these and putting them for example into a TStringList for editing in Delphi. I wondered if these Library Locations were actually stored in the Windows Registry so with some research from Google I found these paths: - HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders - HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders But again, I don't see the actual list of Library Locations. So, how can I read the list of Locations inside a Library file in Delphi, add them to a Listbox or TStringList, edit the entries and then write back the changes? Just been able to extract the Library Location paths would be a start. I just have a feeling this is going to be one of those questions that has a simple answer I cannot seem to find!