Introduction
Windows Media Player is a commonly used application for playing media files. Many users on the Internet were unsuccessfully trying to obtain currently playing song information, including me. In most cases, they used WMPLib which is unfortunately not fully compatible with newer versions of Windows Media Player. So I decided to solve this problem in the other way.
Background
I tried to solve this problem only for my personal use and the following code may differ from the complete UI independent solution (but it works :)).
For obtaining content from components inside Windows, we have to add a reference to UIAutomationClient.dll and not run application in Single Thread Apartment (STA) State.
At last, we’ve got to have open Windows Media Player in library mode and open the Play card:
Code Description
First, we need to find Windows Media Player Window via FindWindow
function included in User32.dll. We need to know which window we are looking for so we can use Spy++ to determine strWindowName
and strClassName
parameters.
When we have the Windows Media Player window handle, we can walk through the component tree until we find the CWmpControlCntr
component and iterate though its children to obtain all desired information.
Code
public class WMPSongInfo
{
[DllImport("User32.dll")]
private static extern IntPtr FindWindow(string strClassName, string strWindowName);
public WMPSong GetCurrentPlayingSong()
{
ApartmentState state = Thread.CurrentThread.GetApartmentState();
if (state == ApartmentState.STA)
throw new InvalidOperationException
("You cannot be in Single Thread Apartment (STA) State.");
IntPtr handle = FindWindow("WMPlayerApp", "Windows Media Player");
if (handle == IntPtr.Zero)
throw new InvalidOperationException("Windows Media Player window not found.");
TreeWalker walker = TreeWalker.ControlViewWalker;
AutomationElement wmpPlayer = AutomationElement.FromHandle(handle);
AutomationElement wmpAppHost = walker.GetFirstChild(wmpPlayer);
AutomationElement wmpSkinHost = walker.GetFirstChild(wmpAppHost);
AutomationElement wmpContainer = walker.GetFirstChild(wmpSkinHost);
AutomationElement wmpSongInfo = walker.GetFirstChild(wmpContainer);
if (wmpSongInfo == null)
throw new InvalidOperationException("Unable to find CWMPControlCntr
in Windows Media Player Window. Try to switch to full view.");
while (wmpSongInfo.Current.ClassName != "CWmpControlCntr")
{
wmpSongInfo = walker.GetNextSibling(wmpSongInfo);
if (wmpSongInfo == null)
break;
}
List<AutomationElement> info = GetChildren(wmpSongInfo);
info = GetChildren(info[0]);
info = GetChildren(info[1]);
info = GetChildren(info[2]);
AutomationElement songE = info[0];
AutomationElement albumE = info[3];
AutomationElement artistE = info[4];
string name = songE.Current.Name;
string album = albumE.Current.Name;
string artist = artistE.Current.Name;
return new WMPSong(name, album, artist);
}
private List<AutomationElement> GetChildren(AutomationElement element)
{
List<AutomationElement> result = new List<AutomationElement>();
TreeWalker walker = TreeWalker.ControlViewWalker;
AutomationElement child = walker.GetFirstChild(element);
result.Add(child);
while (child != null)
{
child = walker.GetNextSibling(child);
result.Add(child);
}
return result;
}
}
Using the Code
We can simply add the WMPSongInfo.cs file to our project and then we can use it:
WMPSongInfo info = new WMPSongInfo();
WMPSong song = info.GetCurrentPlayingSong();
Console.WriteLine("{0} - {1} ({2})", song.Name, song.Artist, song.Album);
Notes
As I mentioned, this code is designed only for Windows Media Player opened in library mode with opened Play card so if you have a better solution, I would appreciate it.