Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Parsing Windows 7 Libraries Without .NET 4 or the Windows7 API Codepack

0.00/5 (No votes)
10 Jan 2011 1  
This article details enumerating available Windows 7 libraries and their contained folders by parsing them as XML
demo_preview.jpg

Introduction

Working on a project in .NET 3.5, I found that I needed to enumerate the Windows 7 libraries, but I didn't have the option of .NET 4 and I decided against adding 2 libraries to my project from the Windows7 API codepack 1.1 available from Microsoft. After reading up on the matter, I determined that it wouldn't be hard to parse the libraries manually that actually worked pretty good.

Background

Windows 7 Libraries are nothing more than special XML documents that are handled by explorer, It really is a great idea, one of my favorite features of Windows 7.

The windows 7 libraries can be stored anywhere, but the ones that appear in the library pane in explorer are stored in the user's libraries directory which is located in:

<UserProfile>\AppData\Roaming\Microsoft\Windows\Libraries 

which we can easily get to as simple as:

string userLibraryFolder = String.Format(@"{0}\Microsoft\Windows\Libraries\", 
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData)); 

The libraries being XML files with the .library-ms file extension, other than that they store special directories as Windows7 "KnowFolders" which is the replacement of using clsids for folders as we did before. Using SHGetKnowFolderPath from the User32.Dll, we can pass those values in and return the full path. Knowing these things, all we have to do is get all the files with the library-ms extension in the libraries folder, load them as XML files and parse them, passing any knowfilder GUIDs to the SHGetKnowFolderPath API and we have a simple library enumerator. Now knowing this, all we have to do is take a minute to look at a library's XML, we can see that the folderpaths are saved in tags marked <URL>. But the library's name is nowhere to be found, that is because the filename of the library file is where its name is saved. So we just get the name from the file path using Path.GetFileNameWithoutExtension() There is still more information that we could get, but for now, this is the most important data.

Parsing a Windows7 Library

First, we define a simple class to represent a library:

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

This class just holds the most basic information we need from the library the name and the folder paths of the folders it contains, we could go as far as get the folder's type, and its icon, but there could be several ways to do it and I'm trying to keep this article as simple as possible.

Next, we know that any special folders are stored with GUIDs instead of paths, but even more still, they are prefaced with the text "knowfolder:". We won't know which libraries will have these special folders, so we will write a method that takes a string, check to see if it starts with "knowfolder:" and if it does, substring out that pretext and pass the GUID to SHGetKnowFolderPath, otherwise we just want to return the string, because it was already a folder path.

[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;
    }
}

With all the support methods and classes defined, all we need is to write a method that will find, parse, and return the libraries. Not that complicated, let's give it a GO!

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

With that code defined in a static class, we can easily just call the GetLibraries method and loop through the list it returns. This snippet would add the name of the library and all the paths it contains for each library to a 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";
}

Note: I know that's not the best way to build strings, but this is just an example. :)

Points of Interest

There are many ways this could be done, you could use much more interop to use Windows APIs to parse the libraries, but it's much more complicated than this, then again you could just use the .NET 4's framework classes, this is for those who want to use .NET 3.5 or just like to play around in code and see if you can do it your own way. That's why I did it and I'm happy with how it turned out.

History

  • Original post V1.0

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here