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.
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:
...
using WMPLib;
class MediaPlayer : IDisposable
{
private WindowsMediaPlayer mp;
private IWMPMediaCollection media;
private int titleIndex;
...
public MediaPlayer()
{
this.mp = new WindowsMediaPlayer();
this.media = mp.mediaCollection;
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
.
public List<MusicEntry> GetMusicLibrary()
{
List<MusicEntry> entries;
IWMPPlaylist mediaList = null;
IWMPMedia mediaItem;
try
{
mediaList = media.getByAttribute("MediaType", "Audio");
entries = new List<MusicEntry>(mediaList.count);
for (int i = 0; i < mediaList.count; i++)
{
mediaItem = mediaList.get_Item(i);
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
{
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.
private string _artist;
public string Artist
{
get { return _artist; }
set { _artist = value; }
}
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).
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:
<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:
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