无需 .NET 4 或 Windows7 API 代码包解析 Windows 7 库






4.50/5 (4投票s)
本文详细介绍了通过将 Windows 7 库及其包含的文件夹解析为 XML 来枚举它们。

引言
我在 .NET 3.5 中处理一个项目时,发现我需要枚举 Windows 7 库,但我没有 .NET 4 的选项,并且我决定不从 Microsoft 提供的 Windows7 API 代码包 1.1 中向我的项目添加 2 个库。 在阅读了相关资料后,我确定手动解析这些库并不难,而且效果还不错。
背景
Windows 7 库只不过是由资源管理器处理的特殊 XML 文档,这确实是个好主意,也是 Windows 7 中我最喜欢的功能之一。
windows 7 库可以存储在任何地方,但资源管理器库窗格中显示的库存储在用户的库目录中,该目录位于
<UserProfile>\AppData\Roaming\Microsoft\Windows\Libraries
我们可以很容易地得到它,如下所示
string userLibraryFolder = String.Format(@"{0}\Microsoft\Windows\Libraries\",
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData));
这些库是带有 .library-ms 文件扩展名的 XML 文件,除此之外,它们还存储特殊的目录,即 Windows7 的“已知文件夹”,它是我们以前使用 clsids 表示文件夹的替代方案。 使用来自 User32.Dll 的 SHGetKnowFolderPath
,我们可以传入这些值并返回完整路径。 了解了这些信息,我们所要做的就是获取库文件夹中所有带有 library-ms 扩展名的文件,将它们加载为 XML 文件并进行解析,将任何 knowfilder GUID 传递给 SHGetKnowFolderPath
API,我们就拥有了一个简单的库枚举器。 现在知道了这一点,我们只需花一分钟时间查看库的 XML,我们可以看到文件夹路径保存在标记为 <URL>
的标签中。 但是库的名称无处可寻,那是因为库文件的文件名保存了它的名称。 因此,我们只需使用 Path.GetFileNameWithoutExtension()
从文件路径获取名称。 仍然可以获得更多信息,但目前,这是最重要的数据。
解析 Windows7 库
首先,我们定义一个简单的类来表示一个库
/// <summary>
/// Represents an instance of a Windows 7 Library
/// </summary>
public class Win7Library
{
public Win7Library()
{
}
public string Name { get; set; }
public string[] Folders { get; set; }
}
此类仅保存我们从库中需要的最基本信息,即名称和它包含的文件夹的文件夹路径,我们可以进一步获取文件夹的类型及其图标,但是有几种方法可以做到这一点,我正在努力使本文尽可能简单。
接下来,我们知道任何特殊文件夹都使用 GUID 而不是路径来存储,但更重要的是,它们以文本“knowfolder:”开头。 我们不会知道哪些库会包含这些特殊文件夹,因此我们将编写一个方法,该方法接受一个 string
,检查它是否以“knowfolder:”开头,如果是,则子字符串化该前缀并将 GUID 传递给 SHGetKnowFolderPath
,否则我们只想返回 string
,因为它已经是文件夹路径。
[DllImport("shell32.dll")]
static extern int SHGetKnownFolderPath([MarshalAs(UnmanagedType.LPStruct)]
Guid rfid, uint dwFlags, IntPtr hToken, out IntPtr pszPath);
//Handles call to SHGetKnownFolderPath
public static string getpathKnown(Guid rfid)
{
IntPtr pPath;
if (SHGetKnownFolderPath(rfid, 0, IntPtr.Zero, out pPath) == 0)
{
string s = System.Runtime.InteropServices.Marshal.PtrToStringUni(pPath);
System.Runtime.InteropServices.Marshal.FreeCoTaskMem(pPath);
return s;
}
else return string.Empty;
}
private static string ResolveStandardKnownFolers(string knowID)
{
if (knowID.StartsWith("knownfolder:"))
{
return getpathKnown(new Guid(knowID.Substring(12)));
}
else
{
return knowID;
}
}
定义了所有支持方法和类后,我们所需要做的就是编写一个查找、解析和返回库的方法。 并不复杂,让我们开始吧!
public static List<Win7Library> GetLibraries()
{
//check if windows 7
if (!WINAPI.RunningOnWin7)
{
throw new Exception("Not Windows 7");
}
//Windows7 Libraries are xmlfiles with this special extension
string FilePattern = "*.library-ms";
//Libraries can be stored anywhere,
// but they typically exist in the Libraries
// folder hidden in your appdata dir.
string userLibraryFolder = String.Format
(@"{0}\Microsoft\Windows\Libraries\",
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData));
List<Win7Library> retLibrary = new List<Win7Library>();
string[] libraries = Directory.GetFiles
(userLibraryFolder, FilePattern, SearchOption.TopDirectoryOnly);
foreach (string s in libraries)
{
Win7Library newLibrary = new Win7Library();
//The Name of a Library is just its file name without the extension
newLibrary.Name = Path.GetFileNameWithoutExtension(s);
List<string> folderpaths = new List<string>();
XmlDocument xmlDoc = new XmlDocument(); //* create an xml document object.
xmlDoc.Load(s); //* load the library as an xml doc.
//Grab all the URL tags in the document,
//these point toward the folders contained in the library.
XmlNodeList directories = xmlDoc.GetElementsByTagName("url");
foreach (XmlNode x in directories)
{
//Special folders use windows7 Know folders GUIDs instead
//of full file paths, so we have to resolve them
folderpaths.Add(ResolveStandardKnownFolers(x.InnerText));
}
newLibrary.Folders = folderpaths.ToArray();
retLibrary.Add(newLibrary);
}
return retLibrary;
}
Using the Code
在 static
类中定义了该代码后,我们可以轻松地调用 GetLibraries
方法并循环遍历它返回的列表。 此代码段会将库的名称以及每个库包含的所有路径添加到 richtextbox
中
foreach (DDW7LibrariesLite.Win7Library lib in
DDW7LibrariesLite.Win7Libraries.GetLibraries())
{
richTextBox1.Text += lib.Name + "\r\n";
foreach (string path in lib.Folders)
{
richTextBox1.Text += path + "\r\n";
}
richTextBox1.Text += "\r\n";
}
注意:我知道这不是构建 string
的最佳方法,但这只是一个例子。 :)
关注点
有很多方法可以做到这一点,您可以使用更多的互操作来使用 Windows API 来解析库,但这比这复杂得多,当然您也可以直接使用 .NET 4 的框架类,这是为那些想要使用 .NET 3.5 或只是喜欢在代码中玩玩,看看你是否能用自己的方式做到这一点的人准备的。 这就是我这样做的原因,我对它的结果感到满意。
历史
- 原始帖子 V1.0