【问题标题】:How to Read/Write Windows 7 Library Locations?如何读/写 Windows 7 库位置?
【发布时间】:2012-09-18 21:34:52
【问题描述】:

简介

在 Windows 7 中,您有一些特殊的文件夹,用于存放文档、图片和音乐等,称为库。

如果您不知道它们,基本上每个库文件夹都可以包含位置(路径),这些位置(路径)基本上是每个库的快捷方式。

一些例子:

Documents (Library)

  • E:\个人\文档(Location)
  • F:\Backups\Documents (Location)

Music (Library)

  • E:\媒体\音乐\专辑(Location)
  • E:\媒体\音乐\单曲(Location)

Pictures (Library)

  • E:\媒体\照片(Location)

当您从 Windows 资源管理器或开始菜单中单击任何这些库文件夹时,Windows 资源管理器将显示该库中定义的位置。

任务

我需要做的是读取每种库类型的位置,并能够用我自己的位置写回(更新)库。我发现库存储在用户 AppData 文件夹中,如下所示:

C:\Users\SOMEUSER\AppData\Roaming\Microsoft\Windows\Libraries

这些库是这种文件类型:库 (.library-ms) - 如果您右键单击一个并选择属性,库选项卡,您可以看到与该库关联的库位置。 p>

我没有看到提取这些并将它们放入 TStringList 以在 Delphi 中进行编辑的方法。我想知道这些库位置是否真的存储在 Windows 注册表中,所以通过 Google 的一些研究,我发现了这些路径:

  • HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell 文件夹
  • HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell 文件夹

但同样,我没有看到图书馆位置的实际列表。

那么,如何在 Delphi 中读取库文件中的位置列表,将它们添加到列表框或 TStringList,编辑条目然后写回更改?能够提取库位置路径将是一个开始。

我只是感觉这将是那些我似乎找不到简单答案的问题之一!

【问题讨论】:

  • 在 MSDN 上阅读它:msdn.microsoft.com/en-us/windows/ee658250.aspx,然后使用 API。甚至不要考虑破解特定于实现的东西。
  • 我刚刚在某处读到 .library-ms 文件可能是 XML 文件,但我丢失了我正在阅读的页面!
  • 您不应该戳这些文件。使用 API。
  • @Blobby 今天他们是 - 明天 MS 将删除文件或更改其格式 - 你的应用程序被搞砸了。如果您的目标不是像 BIND 软件那样“制造错误,出售修复”的策略,那么您可能不会故意制作与未来不兼容的程序。阅读此类程序如何使 Windows 出现故障和不稳定:blogs.msdn.com/b/oldnewthing/archive/2003/11/03/55532.aspx
  • @Arioch'我只是想为自己制作工具,我会定期格式化并重新安装 Windows 7,因此手动重置库路径可能会很痛苦,如果我自己制作了一个工具来自动执行此操作是我希望做的。我不需要担心 MS 会改变任何东西,因为我知道它总是会在 Windows 7 上使用

标签: windows delphi registry


【解决方案1】:

使用SHLoadLibraryFrom...()函数之一,如SHLoadLibraryFromKnownFolder(),得到一个IShellLibrary接口,然后你可以使用它的方法来枚举和操作Library,如IShellLibrary::GetFolders()IShellLibrary::AddFolder(),@987654326 @等

更新:例如:

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;

更新SHLoadLibraryFromKnownFolder() 仅适用于定义了 KNOWNFOLDERID 值的 Microsoft 定义的库。如果要访问自定义库,则必须使用稍微修改的方法来获取IShellLibrary 接口,例如:

// 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;

【讨论】:

  • Remy,你有 Delphi 的例子吗?
  • 我添加了一个加载库文件系统路径的示例。我会把剩下的留给你做练习。
  • @David 大多数 WinAPI 是 C,而不是 C++。谢天谢地,WinAPI 不是 Qt :-)
  • @David,对不起,你错了。绝大多数 WinAPI 都是基于 C 的,该功能的示例是 C。提供的接口(尤其是 Windows shell)是基于 C++ 的,因为(尽管它很丑) C++ 比 C 的界面友好得多。我精通 C 和其他一些语言,但界面的东西仍然很讨厌;有可用的 Delphi 示例来帮助理解它是非常有用的。一旦你开始学习曲线(我承认我还没有),Delphi 也比 C++ 支持 COM(如果不是更好的话)。
  • @KenWhite 如果示例在 Delphi 代码中不存在,那么你放弃了吗?不,你学习用 C 或 C++,甚至 C# 来阅读它们,然后将其映射到你的 Delphi 代码。您最好从编写良好且全面的 C++ 示例代码中学习,而不是从不完整且编写不佳的 Delphi 示例代码中学习。如果存在一个好的 Delphi 示例代码,那就更好了。如果您无法阅读 C、C++ 和 C# 代码并将其翻译为 Delphi,那么这有时会让您望而却步。
【解决方案2】:

我刚刚在 Marco Cantu 的源代码存储库中找到了他的 Mastering Delphi 书籍,这是 Delphi 2010 的一个示例,它显示了一种获取库中位置的方法。

存储库的链接在这里:http://code.marcocantu.com/p/marcodelphibooks/source/tree/HEAD/

delphi2010handbook 中,chapter 05 (Win7Libraries) 是示例源。

那个demo中使用的方法基本上是使用已经提到的Windows API,demo确认Library文件确实是XML格式的。


另外我发现了以下非常有用的信息:

  • SHAddFolderPathToLibrary - 将文件夹添加到库中。
  • SHCreateLibrary IShellLibrary - 创建一个对象。
  • SHLoadLibraryFromItem - 创建 IShellLibrary 并将其加载到指定库定义文件中的对象。
  • SHLoadLibraryFromKnownFolder - 为指定的 KNOWNFOLDERID 创建和加载 IShellLibrary 对象。
  • SHLoadLibraryFromParsingName - 为指定路径创建和加载 IShellLibrary 对象。
  • SHRemoveFolderPathFromLibrary - 从库中删除文件夹。
  • SHResolveFolderPathInLibrary - 尝试解析已移动或重命名的库文件夹的目标位置。
  • SHResolveLibrary - 尝试查找库的位置。
  • SHSaveLibraryInFolderPath - 将 IShellLibrary 对象保存到磁盘。
  • SHShowManageLibraryUI - 显示库管理对话框,使用户能够管理库文件夹和默认保存位置。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-07-14
    相关资源
    最近更新 更多