65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.50/5 (4投票s)

2011年1月5日

CPOL

3分钟阅读

viewsIcon

27043

downloadIcon

372

本文详细介绍了通过将 Windows 7 库及其包含的文件夹解析为 XML 来枚举它们。

demo_preview.jpg

引言

我在 .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.DllSHGetKnowFolderPath,我们可以传入这些值并返回完整路径。 了解了这些信息,我们所要做的就是获取库文件夹中所有带有 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
© . All rights reserved.