Introduction
This article shows a method of controlling WinAmp via a web browser.
Background
Sometime ago I was working at a place where all the developers worked in the same area away
from other people. It didn't take long before our main development server began to fill up with
quite a collection of MP3's. Once this happened the server then got connected to the phone
system and to a decent set of speakers. The only thing left to do was remotely control the
playlist. That's where this article comes in.
Using the code
WinAmp can be controlled using the Windows Messaging system. First you need to get a handle
to WinAmp, this is done using the FindWindow
API. It turns out that all of the WomAmp
versions have the same class name: "Winamp v1.x".
Messages are sent in the form of:
int returnVal = SendMessage(hwndWinamp, WM_USER ,data, id);
In order for a Windows service to obtain the handle of the running WinAmp it needs to be
able to interact with the desktop. This is done by editing the service properties and checking
the 'Allow service to interact with desktop' button.
The web application communicates with the windows service via remoting. The class
winampcontroller
is remoted between the web application and the windows service. In fact
you could do without the Windows service if you wanted to allow IIS to interact with the
desktop, but we didn't want to modify the properties of IIS to do this because of security
fears.
Since the application is written using the .NET framework, interop is used to call the
native Windows API, the following code snippet shows how to send simple commands to WinAmp.
public class winampcontroller : MarshalByRefObject
{
[DllImport("user32.dll", CharSet=CharSet.Auto)]
public static extern IntPtr FindWindow(
[MarshalAs(UnmanagedType.LPTStr)] string lpClassName,
[MarshalAs(UnmanagedType.LPTStr)] string lpWindowName);
[StructLayout(LayoutKind.Sequential)]
struct COPYDATASTRUCT
{
public IntPtr dwData;
public int cbData;
[MarshalAs(UnmanagedType.LPStr)] public string lpData;
}
[DllImport("user32.dll", CharSet=CharSet.Auto)]
static extern int SendMessage(IntPtr hwnd, int wMsg, int wParam,
[In()] ref COPYDATASTRUCT lParam);
[DllImport("user32.dll", CharSet=CharSet.Auto)]
static extern int SendMessageA(IntPtr hwnd, int wMsg, int wParam,
int lParam);
const int WM_COMMAND = 0x111;
const int WM_USER = 1024;
const int WA_PLAY = 40045;
const int WA_STOP = 40047;
const int WA_PAUSE = 40046;
const int WA_PREVTRACK = 40044;
const int WA_NEXTTRACK = 40048;
const int WA_GETSTATUS = 104;
const int WA_PLAYLISTLEN = 124;
const int WA_SETVOLUME = 122;
const int WA_SETPLAYLISTPOS = 121;
const int WA_WRITEPLAYLIST = 120;
const int WA_VOLUMEUP = 40058;
const int WA_VOLUMEDOWN = 40059;
const int WA_CLEARPLAYLIST = 101;
const int WA_NOTHING = 0;
const int WA_RESTART = 135;
const int WA_REFRESHPLCACHE = 247;
public const int STOPPED = 0;
public const int PLAYING = 1;
public const int PAUSED = 3;
protected string m_windowName;
public winampcontroller()
{
try
{
m_windowName = ConfigurationSettings.AppSettings["WindowClassName"];
}
catch(NullReferenceException)
{
m_windowName = "Winamp v1.x";
}
}
public void Stop()
{
IntPtr hwnd = FindWindow(m_windowName, null);
SendMessageA(hwnd, WM_COMMAND, WA_STOP, WA_NOTHING);
}
public void Play()
{
IntPtr hwnd = FindWindow(m_windowName, null);
SendMessageA(hwnd, WM_COMMAND, WA_PLAY, WA_NOTHING);
}
public void Pause()
{
IntPtr hwnd = FindWindow(m_windowName, null);
SendMessageA(hwnd, WM_COMMAND, WA_PAUSE, WA_NOTHING);
}
}
WinAmp has pre-defined responses to different values of data, some of these are:
0 |
Retrieves the version of Winamp running. Version will be 0x20yx for 2.yx. |
100 |
Starts playback. |
101 |
Clears Winamp's playlist. |
102 |
Plays selected track. |
104 |
Returns the status of playback. If 'returnVal' is 1, Winamp is playing. If
'returnVal' is 3, Winamp is paused. Otherwise, playback is stopped. |
121 |
Sets the playlist position to the position specified in tracks in 'data'. |
122 |
Sets the volume to 'data', which can be between 0 (silent) and 255 (maximum). |
124 |
Returns length of the current playlist, in tracks. |
125 |
Returns the position in the current playlist, in tracks (requires Winamp 2.05+). |
129 |
Adds the specified file |
135 |
Restarts Winamp |
The windows service consists of a few lines of code outside of the code generated by Visual
Studio:
protected override void OnStart(string[] args)
{
int tcpChannelVal = 0;
try
{
tcpChannelVal = int.Parse(ConfigurationSettings.AppSettings["ChannelNum"]);
}
catch(Exception){
tcpChannelVal = 1096;
}
m_channel = new TcpChannel(tcpChannelVal);
ChannelServices.RegisterChannel(m_channel);
RemotingConfiguration.ApplicationName = "WinampController";
RemotingConfiguration.RegisterWellKnownServiceType(typeof(winampcontroller),
"WinampController",WellKnownObjectMode.Singleton);
}
protected override void OnStop()
{
ChannelServices.UnregisterChannel(m_channel);
}
To install the windows service, you need to use the InstallUtil program that comes with the
.NET Framework SDK or Visual Studio .NET
The web application's web.config files need to be changed to point to a directory
that contains MP3's
The whole application was put together in a few hours, us being programmers and not designers
means that the front end is not very pretty, but since it's only HTML it's easy to change.
In terms of features, this example controls a playlist of files in a directory. More
functionality and better error handling was destined for this project, but as usual not enough
time has meant that it will have to stay this way.
If anyone wants to create a better front end or add more features you are more than welcome
too, I would of course update the article with your name and contributions.
Points of Interest
When this code was originally written WinAmp was at version 2. Currently WinAmp is at
version 3, which does not provide the same methods for manipulating the player; however, I
did find this plugin which replicates the desired functions:
Winamp 2.x Plugin
Manager for Winamp 3
History
10/12/2002 - Article Created