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:
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);
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()
{
if (!WINAPI.RunningOnWin7)
{
throw new Exception("Not Windows 7");
}
string FilePattern = "*.library-ms";
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();
newLibrary.Name = Path.GetFileNameWithoutExtension(s);
List<string> folderpaths = new List<string>();
XmlDocument xmlDoc = new XmlDocument(); xmlDoc.Load(s);
XmlNodeList directories = xmlDoc.GetElementsByTagName("url");
foreach (XmlNode x in directories)
{
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 string
s, 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