Home > Winamp > Out-of-process (IPC) Winamp control with multiple Winamp instances

Out-of-process (IPC) Winamp control with multiple Winamp instances


At Deviant Audio, we use bespoke tools written in C# to control Winamp playlists. The tools are processes external to Winamp which communicate via IPC (Inter-process Communication) – namely via the SendMessage() Win32 API function.

When controlling Winamp with a 3rd party process, the first thing you must do is locate Winamp’s main window. If you want playlist control, you need the playlist window handle also.

With a single instance of Winamp, this is quite simple (C++):

hWnd = FindWindow("Winamp v1.x", NULL);

will find the Winamp main window using its window class, which is always “Winamp v1.x” regardless of Winamp version used.

To find the playlist window, we simply iterate through the top-level windows on the desktop looking for a window with the correct title, which is always “Winamp Playlist Editor” (the playlist editor is always a top-level window and doesn’t have a window class of its own).

// Get user's desktop
HWND hWndDesktop = GetDesktopWindow();
// Get first child of desktop
HWND hWnd = GetWindow(hWndDesktop, GW_CHILD);

BOOL found = FALSE;

while (hWnd != NULL && !found)
{
    // Get window title
    int windowTitleLength = GetWindowTextLength(hWnd);
    char *windowTitle = new char[windowTitleLength + 1];
    GetWindowText(hWndTopLevel, windowTitle, windowTitleLength + 1);

    // Stop if found otherwise get the next sibling window on the same level
    if (!strcmp(windowTitle, "Winamp Playlist Editor"))
        found = true;
    else
        hWnd = GetWindow(hWnd, GW_HWNDNEXT);

    delete windowTitle;
}

HWND hWndPlaylist = (found)? hWnd : NULL;

We can then use SendMessage() to send IPC requests to Winamp, or Winamp’s playlist window.

Recently we introduced a second instance of Winamp to run two radio stations with separate content simultaneously from one uplink server. This created a problem with our playlist control tools: how do you choose the right instance of Winamp?

Our first – flaky – solution was to modify the startup order of applications on the server so that when our tool is started, only the instance of Winamp we actually want to control is loaded, so only one instance can be found. This has many drawbacks, particularly that it’s easy to break if one of the Winamps has to be restarted, and it doesn’t let us control both Winamps should we wish to.

Winamp 5 provides a neat solution in the form of a command-line option which allows you to set the window class of each instance:

Winamp.exe /CLASS="Winamp A"
Winamp.exe /CLASS="Winamp B"

By simply modifying the window class searched for in FindWindow(), we can now easily attach our external process to whichever instance of Winamp we want.

But there is another problem: we now have multiple playlist editor windows, they are both or all top-level, and have no window class. How do we attach to the right playlist window?

This problem is slightly taxing, but the answer comes in the form of “owner windows”, which are windows owning other windows, even if those windows are not descendants of their owners. Winamp’s playlist window is a sibling of Winamp’s main window, yet it is still owned by Winamp’s main window.

Finding the parent of a window is easy – finding the owner is a bit more tricky. The solution is to use the GetAncestor Win32 API function, specifically called hence:

hWndOwner = GetAncestor(hWndSomeWindow, GA_ROOTOWNER);

This code iterates through all the parents and owners of hWndSomeWindow to find the top-most owner window. When hWndSomeWindow is a Winamp playlist window handle, then hWndOwner will become the main Winamp window handle for the same instance of Winamp. Since we’ve already found Winamp’s main window with FindWindow(), we can iterate through all the Winamp playlist windows on the desktop, comparing the owner window handle of each with the Winamp main window handle we’ve already found, and if they match, we have found the correct playlist window for the instance we want to control:

hWndPlaylist = NULL;
do
{
    // use the code above for this function, but retain the current hWnd between calls so that we
    // can resume the search from the next sibling if we need to iterate through more windows -
    // I leave the implementation details upto you

    hWndPlaylist = GetNextPlaylistWindow();
} while (GetAncestor(hWndPlaylist, GA_ROOTOWNER) != hWndWinamp && hWndPlaylist != NULL);

After this code executes, hWndPlaylist will contain the window handle of the playlist window of the instance we want, or NULL if it couldn’t be found.

After making these changes to our tool, we are now successfully able to control multiple instances of Winamp by specifying the desired window class when calling FindWindow().

I hope you find this technique helpful if you have to use IPC on a machine with multiple Winamp instances šŸ™‚

Advertisements
  1. No comments yet.
  1. No trackbacks yet.

Share your thoughts! Note: to post source code, enclose it in [code lang=...] [/code] tags. Valid values for 'lang' are cpp, csharp, xml, javascript, php etc. To post compiler errors or other text that is best read monospaced, use 'text' as the value for lang.

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: