Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
745 views
in Technique[技术] by (71.8m points)

delphi - How to Read/Write Windows 7 Library Locations?

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:PersonalDocuments (Location)
  • F:BackupsDocuments (Location)

Music (Library)

  • E:MediaMusicAlbums (Location)
  • E:MediaMusicSingles (Location)

Pictures (Library)

  • E:MediaPhotos (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:UsersSOMEUSERAppDataRoamingMicrosoftWindowsLibraries

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_USERSoftwareMicrosoftWindowsCurrentVersionExplorerShell Folders
  • HKEY_CURRENT_USERSoftwareMicrosoftWindowsCurrentVersionExplorerUser 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!

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

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;

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...