What is Windows Media Center?
Windows Media Center is a standard piece of software, shipping with most versions of Windows. It has the ability to present varying media in a common “sexy” way. It’s mostly intended for use with media center pc’s, therefore the name. It has built in support for IR remotes, and can search for song and video data through Microsofts webservices.
I find it pretty awesome, and use it for my own media center. Primarily for playing video files. It also has extensive support for custom addins. That’s what we are going to dive into today.
(You can read more about Windows Media Center on wikipedia: http://en.wikipedia.org/wiki/Windows_Media_Center)
The tools we will need
Before we can begin any kind of developement for Windows Media Center, we are going to need some tools. If you use Visual Studio 2010 as I do, you are going to need the Visual Studio 2008. And if you want to play with VB.NET… Well, you can forget it.
I personally don’t really care about which language it is, but I originally wanted to go for VB.NET. That wasn’t an option, and it didn’t stand anywhere that it wasn’t. So I thought that the SDK wasn’t installed and I started debugging. I just by accident opened Visual C# 2008 Express and saw that the SDK popped up there.
You can download Visual Studio 2008 Express edition on this link: Visual Studio 2008 Express editions.
Now onto the SDK itself. The one we will need is this file WindowsMediaCenterSDK6.msi. Remember to install visual studio c# 2008 first.
Creating a basic “Hello World” addin
Let’s begin by opening Visual C# 2008. If everything went smoothly, you will see a new project template listed.

Just click OK to continue. Wait a few seconds for it to create the project. It may take a little longer than normal.
Then the project has been created, it should automatically open the Readme.htm file in Visual Studio.
This actually serves as a quite good manual for starting out with addin development.
The first point in the list is:
Create a strong name key file and add it to the assembly
This is needed as we are going to install the addin in to the Global Assembly Cache (more on that later). The Global Assembly Cache is by short means a common place for .NET applications to store shared DLL files. But in order to load a DLL file into the GAC (short name), the DLL file must be signed with a strong name. It does not need to be a code signing certificate. A self signed certificate is more than enough.
Anyway, here are the steps involved.
- Goto project > MediaCenterApplication1 Properties.
- Select “Signing” in menu to the left.
- If it’s not checked, go check the “Sign the assembly” option.
- Click the dropdown menu and select “”.
- Write some key file name (for this purpose I’m calling it “HSP Test Certificate”) and write a password too (in this case just “HSPSoftware”). Always remember password, even though it should be a little stronger than mine ;)
- Hold won CTRL + SHIFT and press S on the keyboard. This is just the shortcut for saving all files. (File > Save all).
Press F6 to build the solution (Shortcut for Build > Build solution).
Enter the strong name key in the registration file
In this section we are going to “link” the strong name key to the Registration file used later on, then installing the addin.
But first we need to get the Public Key for our strong name. To do this, we will add an “external tool” to Visual Studio for easy access in the future.
- First go to Tools > External Tools.
- Write “Get Public Key” or anything else which makes sense to you in to the title box.
- Write C:\Program Files\Microsoft SDKs\Windows\v6.0A\bin\sn.exe in to the command box.
- Write -Tp “$(TargetPath)” in the arguments box.
- And lastly, uncheck anything else except “Use Output Window”.
- Hit Apply and OK.
- Go to Tools > Get Public Key (or whatever you named it). Note: You may need to hit debug (F5) first, even though you can’t run a DLL file in the IDE.
- Copy the Public Key Token from the outputwindow.
- Open Setup > Registration.xml.
- Replace “insert_public_key_token_here” with the actual public key.
- Save all and go to Build > Rebuild Solution.
Installing the actual DLL to the GAC and registering with Windows Media Center
Microsoft has been kind enough to provide us with a simple solution to do just that. It’s name is DevInstall.cmd and is placed in the root of your project… Unfortunately this don’t work for me. Neither on my old laptop. So you are free to try it out.
If it doesn’t work, or you just prefer to use some 15 year old kids work instead of Microsofts, you can use my own version of the script here. Their script copies the DLL and XML files to %Program Files%. My script just registers both from the Visual Studio bin/Release directory. Well, you can’t move your project folder around without re-running the script. But who does that anyway? (at least not very often). I just prefer it that way so my system isn’t cluttered up by random folders from random developments.
The download link is here: Custom Media Center Installer (121). Remember to edit the file to suit your project name. You are of course free to use it or redistribute it however you like. I don’t take any responsibility for either software- or hardware failure caused by this script.
Supposed you followed all the steps correctly until now, you can open Windows Media Center and see this nice little notification pop up.

Creating something a little more useful
By now, all the gears should be running and you a properly spinning around your office chair because of the great success you just had. BUT! … It isn’t really useful, is it? Showing some text in a popup, inside windows media center, is maybe cool around your geek friends. But your girlfriend is properly just going to slap you then your media center is starting to show weird Star Wars quotations.
Instead of being slapped, what about letting her control the TV from her iPhone then she can’t find the remote?
Or maybe you will just use this for switching to Discovery Channel, while she’s watching MTV, and get slapped anyway. The choice is yours.
Either way, remote control sounds pretty useful to me! So let’s get coding!
Until now, we have just lived high on the code provided by Microsoft. I haven’t covered changing it yet, but maybe you have already figured out how to change the default popup message into something else.
If you haven’t it’s okay. Just navigate to Code > Application.cs.
You will see a lot of code in there, but we only want to dive into this:
public void Start()
{
string temp = "The background application did something.";
DialogTest(temp);
}
public void DialogTest(string strClickedText)
{
int timeout = 5;
bool modal = true;
string caption = Resources.DialogCaption;
if (host != null)
{
MediaCenterEnvironment.Dialog(strClickedText,
caption,
new object[] { DialogButtons.Ok },
timeout,
modal,
null,
delegate(DialogResult dialogResult) { });
}
else
{
Debug.WriteLine("DialogTest");
}
}
The start() routine is initiated from the Launch.cs. It is called then Windows Media Player is loading the addin.
DialogTest() is not going to be needed. This is just for easy showing of dialogs.
Let’s start out by going to the top and put in these namespaces under the existing.
using System.Net.Sockets;
using System.Threading;
using System.Text;
While you are up there, define this variable just under “private AddInHost host;”
private TcpListener tcpServer;
We just set up the required using declarations and defined a “placeholder” for a our TcpListener.
We did this as we are going to use TCP based communication to receive any commands given.
Go back to the start() routine and add this to start the server and make a basic loop to accept incoming clients.
tcpServer = new TcpListener(System.Net.IPAddress.Any, 3200);
tcpServer.Start();
while (true)
{
TcpClient client = tcpServer.AcceptTcpClient();
}
There, you could just make the usual stream in and out and loop back. But instead we are going to launch each new client on a new thread. This is done so the server (our addin) can accept multiple simultaneous connections.
We will do this by creating this new routine. It accepts a single parameter, which is just an object representing a TcpClient.
It will typecast this object into a TcpClient, open the networkstream and then create a 4096 byte buffer for receiving any commands.
private void HandleClientComm(object client)
{
TcpClient tcpClient = (TcpClient)client;
NetworkStream clientStream = tcpClient.GetStream();
byte[] message = new byte[4096];
int bytesRead;
while (true)
{
bytesRead = 0;
try
{
bytesRead = clientStream.Read(message, 0, 4096);
}
catch
{
break;
}
if (bytesRead == 0)
{
break;
}
//Handle command here
}
tcpClient.Close();
}
Go back to the start routine once more. We just need to add a couple of lines after the accepting of a new TcpClient.
These lines will make the program define a new thread and then launch the HandleClientComm() while passing the connected TcpClient along to it.
Thread clientThread = new Thread(new ParameterizedThreadStart(HandleClientComm));
clientThread.Start(client);
Now we will just make a method for passing the commands given from the connected TcpClients. (I say TcpClients because there can be more than one at a time).
I could go into a lot of detail here but I will just show you my code (which I use for my own Media Center). It can do the most, play an audiofile, play a videofile, change volume, change channels, change mute, stop, pause, play and so on.
You can add your own commands if you want to of course :)
//This is just used for defining the available commands. Feel free to change.
private void handleRemoteCommand(string command)
{
//I don't know, just a habbit I have when working with network protocols (after some bad experinces).
command = command.Trim();
//First we check for basic play, pause, stop commands.
switch (command)
{
case "play":
MediaCenterEnvironment.MediaExperience.Transport.PlayRate = (float)PlayRates.PLAYRATE_PLAY;
break;
case "pause":
MediaCenterEnvironment.MediaExperience.Transport.PlayRate = (float)PlayRates.PLAYRATE_PAUSE;
break;
case "stop":
MediaCenterEnvironment.MediaExperience.Transport.PlayRate = (float)PlayRates.PLAYRATE_STOP;
break;
}
//If mute.
if (command.StartsWith("mute"))
{
command = command.Substring(5);
switch (command)
{
case "true":
MediaCenterEnvironment.AudioMixer.Mute = true;
break;
case "false":
MediaCenterEnvironment.AudioMixer.Mute = false;
break;
}
}
//Set's the volume. This took some time to figure out. Can be a little unprecise sometimes.
else if (command.StartsWith("volume"))
{
command = command.Substring(7);
int volumeSet = -1;
try
{
volumeSet = int.Parse(command);
}
catch
{
}
if (volumeSet >= 0 && volumeSet <= 50)
{
//If someone at Microsoft ever read this little comment... Please make this easier for all of us! ;)
int actualVolume = (int)System.Math.Ceiling((double)((MediaCenterEnvironment.AudioMixer.Volume / 655.35)/2));
if (actualVolume > volumeSet)
{
//This freezes the media center for a second or two. But it's acutally the way Microsoft
//tells us to set the volume. You can also just change the system volume but I'm
//not going into that now. For the purpose of this tutorial, this will do.
int timesToGo = actualVolume - volumeSet;
for (int i = 1; i <= timesToGo; i++)
{
MediaCenterEnvironment.AudioMixer.VolumeDown();
//Well... In my case it actually speeds up a little when I give it a little bit of time inbetween commands
System.Threading.Thread.Sleep(20);
}
}
else if (actualVolume < volumeSet)
{
//This freezes the media center for a second or two. But it's acutally the way Microsoft
//tells us to set the volume. You can also just change the system volume but I'm
//not going into that now. For the purpose of this tutorial, this will do.
int timesToGo = volumeSet - actualVolume;
for (int i = 1; i <= timesToGo; i++)
{
MediaCenterEnvironment.AudioMixer.VolumeUp();
//Well... In my case it actually speeds up a little when I give it a little bit of time inbetween commands
System.Threading.Thread.Sleep(20);
}
}
}
}
//This changes the channel. You can only use this if you have TV tuner.
else if (command.StartsWith("channel"))
{
Microsoft.MediaCenter.TV.Epg.Lineup LineUP = new Microsoft.MediaCenter.TV.Epg.Lineup();
command = command.Substring(8);
foreach (System.Collections.Generic.KeyValuePair CID in LineUP.GetCallSigns())
{
if (CID.Value.Contains(command))
{
MediaCenterEnvironment.PlayMedia(MediaType.TV, CID.Value, false);
MediaCenterEnvironment.MediaExperience.GoToFullScreen();
}
}
}
//Use this for playing a single audiofile
else if (command.StartsWith("playaudio"))
{
command = command.Substring(10).Replace("\\", "\\\\");
System.IO.FileInfo nfo = new System.IO.FileInfo(command);
if (nfo.Exists)
{
MediaCenterEnvironment.PlayMedia(MediaType.Audio, command, false);
MediaCenterEnvironment.MediaExperience.GoToFullScreen();
}
}
//Use this for playing a single videofile
else if (command.StartsWith("playvideo"))
{
command = command.Substring(10).Replace("\\", "\\\\");
System.IO.FileInfo nfo = new System.IO.FileInfo(command);
if (nfo.Exists)
{
MediaCenterEnvironment.PlayMedia(MediaType.Video, command, false);
MediaCenterEnvironment.MediaExperience.GoToFullScreen();
}
}
}
//Can't remember where I got these from... Or if it just was MSDN.
//Anyway, if someone feels insulted, feel free to speak up.
private enum PlayRates
{
PLAYRATE_STOP = 0,
PLAYRATE_PAUSE = 1,
PLAYRATE_PLAY = 2
}
At last just add the following couple of lines at the end of the handleClientComm() loop
UTF8Encoding encoder = new UTF8Encoding();
handleRemoteCommand(encoder.GetString(message, 0, bytesRead));
Just build it / rebuild it. And install it as the first plugin. (My script automatically uninstalls and reinstalls the updated addin. Microsoft’s seems to do that too).
Open Windows Media Center… and…. Voila! Nothing. Well, we that’s because we made it remote controlled. Nothing happens unless we give the order. So let’s create some really simple client. Just make a new project and make this void.
void sendMediaCenterCommand(string command, string mediaCenterLocation, int mediaCenterPort)
{
System.Net.Sockets.TcpClient client = new System.Net.Sockets.TcpClient();
client.Connect(mediaCenterLocation, mediaCenterPort);
byte[] outBytes = System.Text.Encoding.UTF8.GetBytes(command);
System.Net.Sockets.NetworkStream stream = client.GetStream();
if (stream.CanRead && stream.CanWrite)
{
stream.Write(outBytes, 0, outBytes.Length);
stream.Flush();
}
stream.Close();
stream.Dispose();
client.Close();
}
Now you can call sendMediaCenterCommand() as much as you want with any command. It takes three arguments.
- command: The command you want to send.
- mediaCenterLocation: The IP of the mediacenter. Just 127.0.0.1 if it’s local.
- mediaCenterPort: The port you run the addin on. In this case 3200.
You can find all the sourcecode including a full client a little lower on this page. You are free to redistribute it and use it however you like, but a little credit (my name or a link back – maybe both) is very appreciated.
Windows Media Center Addin Source Code (133)