This purpose of this tutorial is to guide a programmer through the process of creating a simple UPnP application that makes use of the three core OSS components,

  • The UPnP control point stack
  • The auto generated control and eventing service classes
  • The Topology component

LinnSysTray Tutorial

LinnSysTray is a minimalistic application that runs in the OS' systray.

LinnSysTray allows a user to connect to a Linn DS, a Linn CD12, a Linn Akurate CD, or a UPnP Media Renderer and to be evented, (via a balloon tip), the current track information.

Finally LinnSysTray also allows the user to perform transport actions; namely, play, pause, stop, skip forward, and skip backward.

Getting Started

If you haven't already checked out and built the source code do that now.

You should now be in the position to run (and try out) the built executable.

The executable will have been installed into the install tree by the build system, which is by default a directory called install in the directory where you checked your code, (which we will call oss for the purposes of this tutorial). The LinnSysTray.exe can be found in the following location,


The relevant build commands for LinnSysTray are,

files = Split("""
resource = _default_env.Resgen("LinnSysTray.LinnSysTrayForm.resources", "LinnSysTray/LinnSysTrayForm.resx")
linnSysTray = _default_env.CliProgram("LinnSysTray", files, CSCTARGET=target, CLILIBS=['ControlOss', 'TopologyOss', 'System.Drawing', 'System.Windows.Forms'], CLIRESOURCES=resource)
Alias('Lib', linnSysTray)

and can be found, starting at line 145, in the SConscript file, oss/LinnGui/SConscript.

The file of interest is oss/LinnGui/LinnSysTray/LinnSysTrayForm.cs and will be the source file of discussion for the remained of this tutorial.


The first step in being able to discover sources in a system is to setup the UPnP control point stack.

The following code sets up three UPnP control point stacks,

  • One to search for devices implementing the urn:linn-co-uk:service:Product:2 service
  • One to search for devices implementing the urn:schemas-upnp-org:device:MediaServer:1 service
  • One to search for devices implementing the urn:schemas-upnp-org:device:MediaRenderer:1 service
iProductCp = new ControlPoint("urn:linn-co-uk:service:Product", 2);
iMediaServerCp = new ControlPoint("urn:schemas-upnp-org:device:MediaServer", 1);
iMediaRendererCp = new ControlPoint("urn:schemas-upnp-org:device:MediaRenderer", 1);

Next we create a ModelSystem that keeps track of the rooms and sources in those rooms dynamically for us, using the UPnP control points we just created.

Since LinnSysTray is only interested in source products we subscribe to the source added (EEventSourceAdded) and source deleted (EEventSourceDeleted) events.

iModelSystem = new ModelSystem(iProductCp, iMediaServerCp, iMediaRendererCp, false);
iModelSystem.EEventSourceAdded += EventSourceAdded;
iModelSystem.EEventSourceDeleted += EventSourceDeleted;
iProductCp.AddObserver(iModelSystem.ProductAlive, iModelSystem.ProductByeBye);
iMediaRendererCp.AddObserver(iModelSystem.MediaRendererAlive, iModelSystem.MediaRendererByeBye);
iMediaServerCp.AddObserver(iModelSystem.MediaServerAlive, iModelSystem.MediaServerByeBye);

We are now ready to start the stacks and tell them to send out a discovery packet, which will get all the devices of the type we are interested in to respond.



At this point we have an application where whenever a source comes online the EventSourceAdded method will be called and whenever a source goes offline the EventSourceDeleted will be called.