Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / desktop / Win32

Export Windows Media Player Music Metadata to XML File

3.50/5 (3 votes)
11 May 2009CPOL4 min read 48K   559  
A C# application that uses the Windows Media Player API to export all audio metadata to an XML file

Introduction

This article looks at exporting data from Windows Media Player into an XML file and is designed as a brief introduction to COM Interop, and some of the new features in C# 3.0, namely: Object Initialisers, Auto-Implemented Properties, and LINQ to XML; it is by no means an extensive look at these features but hopefully gives a flavour for them.

Background

Looking through my old .NET 1.1 applications, I found one that is still used by my weekly backup script but I haven't put any time into it in a good while. Its purpose was to export the list of the audio entries in Windows Media Player into a CSV (Comma-Seperated Values) file which could then be loaded into Excel.

In this article, I'll create an updated version and look at optimising the code to make use of newer framework features, rationalising the classes and fixing issues discovered with the existing implementation.

Using the Code

COM Interop

To me, COM is one of those annoying aunts you get stuck next to at a party and just can't get away from; you have to wonder why after so many years of .NET, everything doesn't have a managed library... but I'm digressing here.

Selecting the COM reference for Windows Media Player

Creating the reference to the Windows Media Player COM library is a case of opening the references dialog, selecting the COM tab and scrolling down to "Windows Media Player", on my machine there are two entries and you want the one with the Path like "c:\windows\system32\wmp.dll". A reference should be added for "WMPLib" and an interop file "Interop.WMPLib.dll" placed in the bin folder; it is then just a case of adding the WMPLib namespace. The basic outline of the MediaPlayer wrapper class looks like this:

C#
...
using WMPLib;

class MediaPlayer : IDisposable
{
  private WindowsMediaPlayer mp;
  private IWMPMediaCollection media;
  private int titleIndex;
  ...

  public MediaPlayer()
  {
    this.mp = new WindowsMediaPlayer();
    this.media = mp.mediaCollection;

    // store the index of each property to improve performance
    this.titleIndex = media.getMediaAtom("Title");
    
    ...
  }

  public void Dispose()
  {
    if (mp != null)
    {
      mp.close();
    }
  }
}

As we are using COM, it is critical that we clean-up after ourselves otherwise memory leaks can/will/may (delete as appropriate) occur. A simple implementation of the IDisposable interface means we don't have to worry about when clean-up will happen, only that garbage collection will free any resources used.

Retrieving the Data

Next I grab a collection of all "Audio" type media, iterate over the items using the get_Item(i) method and add them to our list of MusicEntry (the list is  initialised to the same size as the collection so that the memory space is pre-allocated). Retrieving each attribute is done through mediaItem.getItemInfoByAtom.

C#
public List<MusicEntry> GetMusicLibrary()
{ 
  List<MusicEntry> entries; 
  IWMPPlaylist mediaList = null; 
  IWMPMedia mediaItem; 
  
  try
  { 
    // get the full audio media list 
    mediaList = media.getByAttribute("MediaType", "Audio"); 
    entries = new List<MusicEntry>(mediaList.count); 
    
    for (int i = 0; i < mediaList.count; i++) 
    {
      mediaItem = mediaList.get_Item(i);
      
      // create the new entry and populate its properties
      entry = new MusicEntry() 
      { 
        Title = GetTitle(mediaItem), 
        Album = GetAlbum(mediaItem), 
        Artist = GetArtist(mediaItem), 
        TrackNumber = GetTrackNumber(mediaItem), 
        Rating = GetRating(mediaItem), 
        FileType = GetFileType(mediaItem) 
      }; 
      
      entries.Add(entry); 
    } 
  } 
  finally 
  { 
    // make sure we clean up as this is COM 
    if (mediaList != null) 
    {
      mediaList.clear(); 
    } 
  } 
  
  return entries;
}

...

private string GetAlbum(IWMPMedia mediaItem) 
{ 
  return mediaItem.getItemInfoByAtom(albumIndex); 
}

Notice the use of C# 3.0 Object Initialisers on the instance creation of the MusicEntry class. I also converted the properties for the MusicEntry class to be Auto-Implemented.

C#
// public property backed by a private field
private string _artist;
public string Artist
{
 get { return _artist; }
 set { _artist = value; }
}

// auto-implemented property
public string Artist { get; set; }

Why XML Trumps CSV

Originally, I used CSV file as Excel can read this well and it meant that I didn't have to use Excel Interop (or Visual Studio Tools for Office) but whilst refactoring the code, I discovered that Excel cannot read Unicode characters within CSV files, you get "é" for "é" even though other file types can be read correctly (sigh).

Switching to XML allowed the characters to be read correctly into Excel and using LINQ to XML simplified the code by:

  • Removing the entries.Sort method call and Lambda Expression
  • Removing the MusicEntry.CompareTo method
  • Removing the File handling namespace and logic
  • No longer needing to escape double quotes in CSV strings
  • No longer needing to perform line-level string manipulation and concatenation

The LINQ to XML syntax is more elegant than both the bespoke CSV writer and (the now old-school) XmlDocument, it also provides sorting, automatic escaping of reserved characters and file handling (amongst a very long list of other features).

C#
public static void MusicListToXml(string outputPath, List<musicentry> entries)
{
  XElement x = new XElement("Music", 
    from entry in entries 
    orderby entry.Artist, entry.Album, entry.TrackNumber 
    select new XElement("Entry",
      new XAttribute("Artist", entry.Artist),
      new XAttribute("Album", entry.Album),
      new XAttribute("Title", entry.Title),
      new XAttribute("Track", entry.TrackNumber.ToString()),
      new XAttribute("Rating", entry.Rating.ToString()), 
      new XAttribute("FileType", entry.FileType))); 
    
  x.Save(outputPath);
}

The XML output is compact and of the form:

XML
<Music>
 <Entry Artist="Zero 7" Album="When It Falls" Title="Warm Sound" 
	Track="1" Rating="" FileType="wma" />
 ...
</Music>

To load the XML file into Excel 2003, I selected the Open XML "As a read-only workbook" option which displays the data in a reasonable format:

Excel Workbook displaying the XML data

Conclusion

Times change, as do file formats and although the CSV file approach was quick and easy in .NET 1.1, the Unicode display problems within Excel were a good enough reason to convert to XML and through the power of LINQ, data manipulation is a breeze. Further, by using Object Initialisers, Auto-Implemented Properties and Generics, this exporter has been simplified and made more robust - a win, win in my book.

History

  • Version 1.0 - Initial release

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)