Control Windows Media Center using a Windows Mobile 5 Device
This article demonstrates how to use a smartphone running Windows Mobile 5 to remotely control a host machine running Windows Media Center. I will describe how to create an add-in application that runs within Windows Media Center, and how to create a Windows Mobile 5 application that you can use to communicate with the Media Center over a wireless internet connection. Finally, I will demonstrate how to use this implementation to manipulate Media Center using the Windows Media Center API.
Matt Ivers
Difficulty: Intermediate
Time Required: 6-10 hours
Cost: $100-$200
Software: Visual Basic or Visual C# Express Editions, Windows Media Center SDK 5.0, Windows Mobile 5.0 Smartphone SDK
Hardware: Mobile device running Windows Mobile 5.0
Download: C# and VB.Net
Introduction
Starting with Windows Media Center Edition 2005, Media Center added extensibility support through hosted add-in assemblies. By implementing an add-in interface and registering the assembly with Media Center, the add-in assembly is given access to the Microsoft.MediaCenter.Hosting.AddInHost object. Using this object model, an add-in assembly can programmatically inspect and manipulate various aspects of Windows Media Center. In addition to the exposed Media Center namespaces, the add-in has complete access to all the capabilities of the .NET Framework.
By combining the power of a Media Center add-in with a mobile device running Windows Mobile 5.0, we can extend the capabilities of a Media Center add-in even further. The add-in service I developed runs as a listener service within Windows Media Center on the host machine. It is initialized when Windows Media Center is started, runs invisibly in the background, and allows mobile devices to connect and submit requests to it. The mobile solution leverages an existing WiFi internet connection to communicate with the add-in via TCP socket connections. The mobile device runs a .NET Compact Framework 2.0 forms application designed for devices running Windows Mobile 5 or higher.
The included remote control application is capable of the following functionality:
- Retrieve metadata about currently loaded media
- Affect playback of currently loaded media – Play, Pause, Stop, Next Track, Previous Track
- Control Media Center volume
- Retrieve a list of available media – Photos, Audio, Video
- Explicitly load media for playback – Audio, Video
Creating a Windows Media Center add-in application
The first step in our implementation is creating an add-in that will run within Windows Media Center. This involves creating a new Windows Media Center Presentation Layer Application project, implementing an interface, signing the assembly, and creating a setup project to install the assembly and register it with Media center.
Creating a new project – Windows Media Center Presentation Layer Application
Installing the Windows Media Center SDK makes this step simple. Once installed, you create this project like any other in Visual Studio:
Figure 1 - Creating the project
Using the Windows Media Center Presentation Layer Application project template yields the following project skeleton:
Figure 2 - Solution Explorer for our new project
There are two files of interest here: AddIn.vb and App.xml. AddIn.vb contains our add-in class. Interface methods implemented in this class will be invoked by Windows Media Center when our add-in is launched. App.xml contains configuration information identifying our assemblies and add-in attributes. The setup project will use this XML to register our add-in with Windows Media Center during installation.
The AddIn class
Media Center add-in applications must implement two interfaces in order for Media Center to invoke them: Microsoft.MediaCenter.Hosting.IAddInEntryPoint, and Microsoft.MediaCenter.Hosting.IAddInModule. By using the project template, our add-in class already implements both (AddIn.vb):
Visual Basic .NET
Public Class Class1
Implements IAddInModule
Implements IAddInEntryPoint
' Initialize (IAddInModule)
Public Sub Initialize(ByVal appInfo As Dictionary(Of String, Object), ByVal entryPointInfo As Dictionary(Of String, Object)) Implements IAddInModule.Initialize
' Initialization logic goes here
End Sub
' Uninitialize (IAddInModule)
Public Sub Uninitialize() Implements IAddInModule.Uninitialize
' Clean-up logic goes here
End Sub
' Launch (IAddInEntryPoint)
Public Sub Launch(ByVal host As AddInHost) Implements IAddInEntryPoint.Launch
' This is where normal execution begins
End Sub
End Class
Visual C#
public class Class1 : IAddInModule, IAddInEntryPoint
{
// Initialize (IAddInModule)
public void Initialize(Dictionary<string, object> appInfo, Dictionary<string, object> entryPointInfo)
{
// Initialization logic goes here
}
// Uninitialize (IAddInModule)
public void Uninitialize()
{
// Clean-up logic goes here
}
// Launch (IAddInEntryPoint)
public void Launch(AddInHost host)
{
// This is where normal execution begins
}
}
The IAddInModule.Initialize and IAddInModule.Uninitialize interface methods are used by Media Center to signal the beginning and end of the add-in application’s lifetime. Use Initialize to create any internal objects your application may require, and the Uninitialize to perform any clean-up. Keep in mind that since the add-in application runs within Media Center, it may decide to close your add-in application before it has a chance to exit on its own. In any case, Media Center will call the Uninitialize method and give the add-in a window of execution to exit as gracefully as possible.
The IAddInEntryPoint.Launch method is where normal execution code should begin. Media Center will pass an AddInHost object as an input parameter. Through this object, your add-in application can access all the features of the Media Center API.
Signing the assembly
Windows Media Center requires that all add-in assemblies be strongly-named. This requires that the assembly be signed using a key file. Thanks to Visual Studio 2005, signing an assembly is a straightforward process. From your add-in project’s “Properties” menu, you can browse to the “Signing” tab to create a new strong name key file for the project:
Figure 3 - Signing our assembly
Explicit instruction is also available here:
How to: Sign an Assembly (Visual Studio)
Tweaking the App.xml file
Media Center requires every add-in application to explicitly define a point of entry by which the application is to be launched. The App.xml file contains this configuration information. The Media Center project template used earlier generates the following sample:
<application title="DemoProject" id="{f6dd1141-f288-46fe-bcdb-2d9c5ffd986e}">
<entrypoint id="{31a15ce9-0487-43bc-94de-a43409129641}"
addin="DemoProject.Class1, DemoProject,Culture=Neutral,Version=6.0.6000.0"
title="DemoProject"
description="DemoProject"
ImageUrl=".\AppIcon.png">
<category category="More Programs"/>
</entrypoint>
</application>
The XML definition specifies the add-in applications and their points of entry within Media Center. Add-in applications can be launched on demand from various locations within Media Center, such as from an icon in the “More Programs” group. A full listing of all the available points of entry can be found here. Add-in applications can also be launched in the background when Media Center starts. To indicate this, we specify “Background” in the “category” attribute. The “addin” attribute must also be modified to point to our add-in class in the format “Assemblyname.ClassName”. The “title”, “description”, and “ImageUrl” attributes are used primarily for on-demand add-ins.
After making our changes, the Windows Media Center Remote application XML looks like this:
<application title="WMCServerAddIn" id="{7b71a7ca-b459-4023-ab12-d5cc8c56b991}">
<entrypoint id="{5fe9982b-ca4a-459e-b62c-40399e875d66}"
addin="WMCServerAddIn.ServerAddIn, WMCServerAddIn,Culture=Neutral,Version=6.0.6000.0"
title="WMCServerAddIn"
description="WMCServerAddIn"
ImageUrl=".\AppIcon.png">
<category category="Background"/>
</entrypoint>
</application>
Creating the setup project
With a basic interface implementation complete and a configuration XML created, we are ready to create the setup project. The setup project will install our assemblies into the Global Assembly Cache and register our XML-defined entry point with Media Center. A full step-by-step walkthrough can be found here:
Creating a Windows Installer File for a Windows Media Center Add-in
Once the setup project is complete, it’s time for a test. By using the Host.MediaCenterEnvironment.Dialog class off of the AddInHost object, we can present a dialog box to the user to ensure the add-in launches successfully. After compiling the solution, run the installer by right-clicking the setup project and choosing “Install”. Once installation has completed, run Windows Media Center. A few seconds after startup, the add-in is launched:
Figure 4 - Running our Hello World Media Center Add-in
Creating a Windows Mobile 5.0 client application
The second step in our implementation is creating a .NET Compact Framework 2.0 forms application. This is a mobile application that will run on a mobile device running Windows Mobile 5 or higher.
Creating a new project – Windows Mobile CE 5.0 application
Installing the Windows Mobile Smartphone SDK 5.0 makes this step simple. Once the SDK is installed, you can create a project in Visual Studio using the Windows CE 5.0 Project template.
The only other typical setup required for developing mobile apps is to change the deployment settings to point to your WM5 device. This can be configured within the Visual Studio toolbar:
Figure 5 - changing the deployment settings to target the mobile device
Implementing the Media Center Add-in service
With a test harness in place at both ends of the implementation, it was time to get to the real code. To facilitate the control of Media Center through the hosted add-in, the service requires a few key components. These include choosing a server / client conversation protocol, creating a multithreaded socket server, implementing object serialization, and implementing the programmatic manipulation of Media Center.
Implementing the socket server
The socket server uses the Socket object from the System.Networking.Sockets namespace for all of its communications. The listener service begins by binding to the host machine’s local address and listening on a port of your choosing. Once the socket is instantiated and bound to a local port, it begins listening for and accepting inbound connections. When a new connection is established, a target method BeginSocketConversation will be invoked on a new thread, where the actual communication between the two parties will take place. Once the new thread has been invoked, execution will continue in the AcceptConnections method. At this time the newly launched thread notifies the listener to begin accepting new connections again. A ManualResetEvent from the System.Threading namespace can be used to communicate changes in state between two or more threads. Here is the code implementation of the socket listener:
Visual Basic .NET
Private Sub AcceptConnections()
Dim localMachineInfo As IPHostEntry
Dim localEndPoint As IPEndPoint
Dim listener As New Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)
Try
localMachineInfo = Dns.Resolve(String.Empty)
localEndPoint = New IPEndPoint(localMachineInfo.AddressList(0), _port)
listener.Bind(localEndPoint)
listener.Listen(10)
While True
threadEvent.Reset()
listener.BeginAccept(New AsyncCallback(AddressOf BeginSocketConversation), listener)
threadEvent.WaitOne()
End While
Catch ex As Exception
Log.Write(ex)
End Try
End Sub
Visual C#
Public Class Class1
Implements IAddInModule
Implements IAddInEntryPoint
' Initialize (IAddInModule)
Public Sub Initialize(ByVal appInfo As Dictionary(Of String, Object), ByVal entryPointInfo As Dictionary(Of String, Object)) Implements IAddInModule.Initialize
' Initialization logic goes here
End Sub
' Uninitialize (IAddInModule)
Public Sub Uninitialize() Implements IAddInModule.Uninitialize
' Clean-up logic goes here
End Sub
' Launch (IAddInEntryPoint)
Public Sub Launch(ByVal host As AddInHost) Implements IAddInEntryPoint.Launch
' This is where normal execution begins
End Sub
End Class
Once a connection has been established, the server begins to listen for inbound messages. When new data is received, it is parsed into a custom Message object for easy inspection by a consuming class. An event is then raised to any consuming classes, passing a reference to the newly created Message object. The server then resumes listening for new data on the same socket. Here is the code implementation:
Visual Basic .NET
Private Sub BeginSocketConversation(ByVal ar As IAsyncResult)
threadEvent.Set()
RaiseEvent ClientConnected()
Dim listener As Socket = CType(ar.AsyncState, Socket)
_socket = listener.EndAccept(ar)
Dim buffer(PACKET_SIZE) As Byte
Dim bytesReadCount As Integer = 0
While True
Dim sb As New System.Text.StringBuilder
Do
Try
bytesReadCount = _socket.Receive(buffer, buffer.Length, SocketFlags.None)
Catch ex As Exception
_socket.Close()
Exit While
End Try
sb.Append(System.Text.ASCIIEncoding.ASCII.GetString(buffer, 0, bytesReadCount))
Loop While _socket.Available > 0
Dim requestMessage As New RequestMessage()
requestMessage.DeSerialize(sb.ToString())
RaiseEvent MessageReceived(requestMessage)
End While
RaiseEvent ClientDisconnected()
End Sub
Visual C#
private void BeginSocketConversation(IAsyncResult ar)
{
threadEvent.Set();
ClientConnected();
Socket listener = ((Socket)(ar.AsyncState));
_socket = listener.EndAccept(ar);
byte[,] buffer;
int bytesReadCount = 0;
while (true)
{
System.Text.StringBuilder sb = new System.Text.StringBuilder();
for (
; (_socket.Available > 0);
)
{
try
{
bytesReadCount = _socket.Receive(buffer, buffer.Length, SocketFlags.None);
}
catch (Exception ex)
{
_socket.Close();
break;
}
sb.Append(System.Text.ASCIIEncoding.ASCII.GetString(buffer, 0, bytesReadCount));
}
RequestMessage requestMessage = new RequestMessage();
requestMessage.DeSerialize(sb.ToString());
MessageReceived(requestMessage);
}
ClientDisconnected();
}
Object serialization
The limited resources of a mobile device require an efficient method of transferring data and commands between the device and the host machine. Due to their large size, the .NET remoting libraries and the binary serialization libraries provided by .NET 2.0 are not available in the .NET Mobile Framework. I chose custom serialization to facilitate the transfer of objects. While less flexible, this enabled objects to be transferred using very little bandwidth.
Implementing the AddIn.Launch method
The custom server object created in the previous section encapsulates all the grunt work of socket-level protocol, accepting connections, serializing / deserializing message objects, and data transfer. Within the AddIn class implementation, all we have to do now is instantiate the Server class, indicating the port to listen on:
Visual Basic .NET
Public Sub Launch(ByVal host As AddInHost) Implements IAddInEntryPoint.Launch
_wmcHost = host
_waitForExit = New System.Threading.ManualResetEvent(False)
_server.Start(DEFAULT_PORT)
System.Threading.Thread.CurrentThread.Priority = Threading.ThreadPriority.Lowest
_waitForExit.WaitOne()
End Sub
Visual C#
public void Launch(AddInHost host)
{
_wmcHost = host;
_waitForExit = new System.Threading.ManualResetEvent(false);
_server.Start(DEFAULT_PORT);
System.Threading.Thread.CurrentThread.Priority = Threading.ThreadPriority.Lowest;
_waitForExit.WaitOne();
}
We also perform two other critical tasks at this time. First, we assign a reference to the AddInHost to a private member variable for later use. Second, we reduce the priority of the add-in’s main thread and use a ManualResetEvent to put it to sleep until Media Center is closed. The reason for this relates to the nature of an add-in’s lifetime within Media Center. The Media Center add-in hosting process will only allow an add-in to execute for the duration of its Launch method. This means that as soon as the Launch method returns, the add-in and any child threads it has spawned will be terminated. For this reason, we must keep the add-in alive by blocking the Launch method from exiting.
Media Center manipulation
Using the communications layer and socket listener developed in the previous sections, we can finally get to the task of performing something useful with the input requests. By subscribing to the custom Server object’s MessageReceived event, the AddIn class’s OnDataReceived method will automatically be invoked when a new request message is received from the listener:
Visual Basic .NET
Private Sub OnDataReceived(ByVal message As RequestMessage) Handles _server.MessageReceived
Select Case message.RequestAction
Case RequestMessage.Action.PauseMedia
_wmcHost.MediaCenterEnvironment.MediaExperience.Transport.PlayRate = PlayRates.PLAYRATE_PAUSE
Case RequestMessage.Action.VolumeUp
_wmcHost.MediaCenterEnvironment.AudioMixer.VolumeUp()
Case RequestMessage.Action.VolumeDown
_wmcHost.MediaCenterEnvironment.AudioMixer.VolumeDown()
Case RequestMessage.Action.NextMedia _wmcHost.MediaCenterEnvironment.MediaExperience.Transport.SkipForward()
Case Else
End Select
SendAcknowledgement(message)
End Sub
Visual C#
private void OnDataReceived(RequestMessage message)
{
switch (message.RequestAction)
{
case RequestMessage.Action.PauseMedia:
_wmcHost.MediaCenterEnvironment.MediaExperience.Transport.PlayRate = PlayRates.PLAYRATE_PAUSE;
break;
case RequestMessage.Action.VolumeUp:
_wmcHost.MediaCenterEnvironment.AudioMixer.VolumeUp();
break;
case RequestMessage.Action.VolumeDown:
_wmcHost.MediaCenterEnvironment.AudioMixer.VolumeDown();
break;
case RequestMessage.Action.NextMedia:
_wmcHost.MediaCenterEnvironment.MediaExperience.Transport.SkipForward();
break;
}
SendAcknowledgement(message);
}
The RequestMessage class shown above is a custom object, originally created by Windows Mobile 5 application running on a connected mobile device. The transport object was then serialized on the device, transmitted, deserialized in the listener service, and raised to the add-in class via the MessageReceived event. The RequestAction property is an enumerable type indicating the type of action to take within Media Center. The Media Center API exposed by the AddInHost object (_wmcHost) can then be used to actually perform the desired Media Center operation. Finally, we send an acknowledgement message to the mobile device.
Implementing the Windows Mobile 5 remote client application
After completing the Media Center add-in, the mobile application is relatively simple by comparison. The remote application’s role is to present the user with a user interface similar to that of an actual remote control. The remote application directs user input to the listener add-in, and displays any response data if necessary.
Implementing the socket client
The design of the socket client is much simpler than that of the socket server. All execution will be performed on the primary thread. The client begins by establishing a connection with an add-in server. Once established, the socket client sends and receives data synchronously over the socket. Consumers of the socket client need only call the Send method, which will return with the transactional response:
Visual Basic .NET
Public Function Send(ByVal request As Message) As String
Dim responseString As String = Nothing
Try
If ((Not (_socket) Is Nothing) _
AndAlso _socket.Connected) Then
Dim requestString As String = request.Serialize
Send(requestString)
responseString = RecvSynchronous
Return responseString
End If
Catch ex As Exception
Return Nothing
End Try
Return responseString
End Function
Visual C#
public string Send(Message request)
{
string responseString = null;
try
{
if (_socket != null && _socket.Connected)
{
string requestString = request.Serialize();
Send(requestString);
responseString = RecvSynchronous();
return responseString;
}
}
catch (Exception ex)
{
return null;
}
return responseString;
}
Adding the UI layer
With the communications layer in place the next step in our remote implementation is the user interface. In this final stage, we hook up UI controls to construct the request messages that will be sent to the host service. However, before we do that we must first connect with the Media Center host machine. Here’s a look at the connection configuration screen:
Figure 6 - The configuration screen on the mobile device
Once we’ve specified the host and port of our running Media Center add-in service, we can click the “Connect” button to initialize the TCP/IP socket connection. Using the TCP Client class developed earlier, the code required to handle this event is simple:
Visual Basic .NET
Private Sub btnConnect_Click(ByVal sender As Object, ByVal e As EventArgs)
Dim host As String = Me.txtHost.Text.Trim
Dim port As String = Me.txtPort.Text.Trim
_client.Connect(host, Convert.ToInt32(port))
End Sub
Visual C#
private void btnConnect_Click(object sender, EventArgs e)
{
string host = this.txtHost.Text.Trim();
string port = this.txtPort.Text.Trim();
_client.Connect(host, Convert.ToInt32(port));
}
Once connected, the user can navigate the application using the provided tabs at the bottom of the screen. Since this application is intended to function as a remote control, there is a standard media control screen:
Figure 8: The control screen on the mobile device
Nothing exotic here, but the basic media control functionality you’d expect from a remote is available. Here’s a sample of the code required to support the “Stop” button’s click event:
Visual Basic .NET
Private Sub btnConnect_Click(ByVal sender As Object, ByVal e As EventArgs)
Dim host As String = Me.txtHost.Text.Trim
Dim port As String = Me.txtPort.Text.Trim
_client.Connect(host, Convert.ToInt32(port))
End Sub
Visual C#
private void btnStop_Click(object sender, EventArgs e)
{
RequestMessage message = new RequestMessage();
message.RequestAction = RequestMessage.Action.StopMedia;
message.Data = String.Empty;
string responseString = client.Send(message);
ResponseMessage response = new ResponseMessage(responseString);
}
The UI form uses the connected Client object to execute the Stop command. First, the form constructs a Request message, populating the type of action desired (StopMedia). Next, the Request message is passed to the Client class, where it is serialized for transport and sent over the connected TCP socket. From there, the running Media Center service will receive the request, perform the media stop, and send an acknowledgement. The Client then returns a Response message, which will contain a Boolean indicating if the request was successful or not.
On a Media Stop request, examining the Response object is not very informative. However, for other commands, the Media Center add-in service can be designed to return any kind of information required of the host. The Detail screen is a good example:
Figure 9 - The detail page
When a song or video is currently playing, the above screen can be used to retrieve any metadata associated with it. The event handler for the screen’s “Refresh” button looks similar in form to the Stop button’s handler:
Visual Basic .NET
Private Sub btnRefreshDetail_Click(ByVal sender As Object, ByVal e As EventArgs)
Dim request As RequestMessage = New RequestMessage
request.RequestAction = RequestMessage.Action.GetMediaMetadata
Dim responseString As String = _client.Send(request)
Dim response As MediaMetadataMessage = New MediaMetadataMessage(responseString)
For Each item As MediaMetadataMessage.MediaMetadataItem In message.Data
If ((Not (item.Value) Is Nothing) AndAlso (item.Value.Length > 0)) Then
Me.txtDetail.Text = (Me.txtDetail.Text + (item.Key + (": " + item.Value)))
Me.txtDetail.Text = (Me.txtDetail.Text + "" & vbCrLf)
End If
Next
End Sub
Visual C#
private void btnRefreshDetail_Click(object sender, EventArgs e)
{
RequestMessage request = new RequestMessage();
request.RequestAction = RequestMessage.Action.GetMediaMetadata;
string responseString = _client.Send(request);
MediaMetadataMessage response = new MediaMetadataMessage(responseString);
foreach (MediaMetadataMessage.MediaMetadataItem item in message.Data) {
if (item.Value != null && item.Value.Length > 0){
this.txtDetail.Text += item.Key + ": " + item.Value;
this.txtDetail.Text += "\r\n";
}
}
}
This time, however, the Response object has a list of metadata keys and values, which are then enumerated and displayed in a textbox. This Response object was originally populated by the Media Center add-in service:
Visual Basic .NET
Private Sub SendMediaMetadata()
Dim response As New MediaMetadataMessage()
Dim val As String
For Each key As String In _wmcHost.MediaCenterEnvironment.MediaExperience.MediaMetadata.Keys
val = _wmcHost.MediaCenterEnvironment.MediaExperience.MediaMetadata.Item(key).ToString()
response.Data.Add(New MediaMetadataMessage.MediaMetadataItem(key, val))
Next
response.ResponseIndicator = MediaMetadataMessage.ResponseCode.Success
_server.Send(response)
End Sub
Visual C#
private void SendMediaMetadata()
{
MediaMetadataMessage response = new MediaMetadataMessage();
string val;
foreach (string key in _wmcHost.MediaCenterEnvironment.MediaExperience.MediaMetadata.Keys)
{
val = _wmcHost.MediaCenterEnvironment.MediaExperience.MediaMetadata.Item[key].ToString();
response.Data.Add(new MediaMetadataMessage.MediaMetadataItem(key, val));
}
response.ResponseIndicator = MediaMetadataMessage.ResponseCode.Success;
_server.Send(response);
}
After receiving a request from the mobile device for the currently playing media’s metadata, the Media Center add-in service constructs a list of custom objects (MediaMetadataItem). It populates this list by enumerating properties of the AddInHost object, through the namespace MediaCenterEnvironment.MediaExperience.MediaMetadata. It then sends this list on the Response object, where the mobile device will inspect and display the data, as shown above.
The last feature that the mobile remote application provides is the ability to select specific media to play based on a list of available media on the host. This functionality is provided for audio, video, and images:
Figure 10 - Music and Video list management
The mobile application requests the list of available media in a similar fashion to the Details screen implementation. The resulting list of files is then bound and displayed in a DataGrid. The Media Center add-in service can then be instructed to play individual songs or videos using the AddInHost’s MediaCenterEnvironment.PlayMedia method.
Mobile Application UI design considerations
Developing on the mobile platform introduces design challenges not found in Windows Forms applications due to the small amount of screen real estate available. Many typical form layouts found in Windows Forms applications won’t work well on a mobile device’s small screen. When developing for a mobile application, try to keep the user interface as simple as possible. Window containers and managers such as a tabbed control enable the developer to functionally divide an application into logical components. This alleviates the clutter created by having too many controls in a single window. Also try to keep your text and controls as simple and large as possible. This makes the application easier to view and manipulate, especially in outdoor or bright conditions.
Conclusion
Once complete, the remote application gives you a level of control simply not available on hardware-based remote controls. I really enjoyed working on this application because it gave me a chance to integrate a number of different technologies into a single implementation. Be sure to download the code and take the remote for a spin. For those not familiar with all of the components used in this article (Media Center add-in services, socket communication, installer and assembly signing, etc.), don’t be intimidated. The Microsoft-provided SDK packages for both parts of the solution make creating your own add-in projects a snap. Plus, the technical challenges unique to this project have already been overcome! Feel free to modify my implementation, or reuse the included socket classes to create an add-in all your own. Finally, please post feedback or questions here or at my blog.
About the Author
Matt Ivers is a software engineer at Chicago-based Clarity Consulting. He has extensive professional experience developing .NET solutions in both Visual C# .NET and VB.NET. Check him out at his blog or through the Clarity blog site, Clarity Blogs.
References
Windows Media Center SDK 5.0
Windows Mobile 5.0 Smartphone SDK
How to: Sign an Assembly using Visual Studio 2005
How to: Create a Windows Installer File for a Windows Media Center Add-in
Windows Media Center - SDK Overview
Windows Media Center - Understanding the Basics
Windows Media Center - Development Tools
Windows Media Center - AddIn example: "Time Travel"
Using an Asynchronous Server Socket