Zune Now Playing support for Digsby and Winamp-compatible nowplaying software.

July 17, 2010 by Dave · 4 Comments
Filed under: Code Snippets, Software 

The Zune media player delivers Now Playing information to Windows Live Messenger, but other software isn’t able to pick up these messages.  Quite a few users have asked for Zune Now Playing support in Digsby, so I put together this little app that lets this work.  The application itself is generic, converting Zune now playing messages into the more widely implemented Winamp now playing method.

Download the executable and source (C++).

Code Listing:

#include "stdafx.h"
#include "ZuneNowPlaying.h"
#include "LimitSingleInstance.h" // KB243953
#include <regex>
 
const std::tr1::wregex pattern(L".*?\\\\0Music\\\\0.*?\\\\0.*?\\\\0(.*?)\\\\0(.*?)\\\\0");
 
CLimitSingleInstance g_SingleInstanceObj(L"{24F3CD83-9FFE-4DE6-951D-63958942F834}");
 
HWND hWndWinamp;
HWND hWndWindowsLiveMessenger;
 
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	switch (message)
	{
		case WM_COPYDATA:
			{
				COPYDATASTRUCT *cds = (COPYDATASTRUCT*)lParam;
				wchar_t *str = (wchar_t*)cds-&gt;lpData;
 
				std::tr1::wcmatch result;
				if(std::tr1::regex_search(str, result, pattern))
				{
					std::wstring ret = result[2].str() + L" - " + result[1].str();
					SetWindowText(hWndWinamp, ret.c_str());
				}
				else
				{
					SetWindowText(hWndWinamp, L"");
				}
				break;
			}
		case WM_DESTROY:
			PostQuitMessage(0);
			break;
		case WM_USER:
			if (lParam == 104)
			{
				return 1; // Winamp=Playing (always)
			}
	}
	return DefWindowProc(hWnd, message, wParam, lParam);
}
 
void RegisterClasses(HINSTANCE hInstance)
{
	WNDCLASSEX wcex = WNDCLASSEX();
	wcex.cbSize = sizeof(WNDCLASSEX);
	wcex.lpfnWndProc	= WndProc;
	wcex.hInstance		= hInstance;
	wcex.hbrBackground	= (HBRUSH)(COLOR_WINDOW+1);
	wcex.lpszClassName	= L"MsnMsgrUIManager";
	RegisterClassEx(&amp;wcex);
 
	wcex.lpszClassName	= L"Winamp v1.x";
	RegisterClassEx(&amp;wcex);
}
 
bool CreateWindows(HINSTANCE hInstance)
{
	// Pretend to be Windows Live Messenger, so WM_COPYDATA messages are sent to us.
	hWndWindowsLiveMessenger = CreateWindow(L"MsnMsgrUIManager", L"", WS_DISABLED,
		CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);
	// Pretend to be Winamp (any version), so apps will pick us up for Now Playing.
	hWndWinamp = CreateWindow(L"Winamp v1.x", L"", WS_DISABLED,
		CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);
 
	return hWndWinamp &amp;&amp; hWndWindowsLiveMessenger;
}
 
int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE /*hPrevInstance*/, LPTSTR /*lpCmdLine*/, int /*nCmdShow*/)
{
	MSG msg = MSG();
	if (!g_SingleInstanceObj.IsAnotherInstanceRunning())
	{
		RegisterClasses(hInstance);
 
		if (CreateWindows(hInstance))
		{
			while (GetMessage(&amp;msg, NULL, 0, 0))
			{
				TranslateMessage(&amp;msg);
				DispatchMessage(&amp;msg);
			}
		}
	}
	return (int)msg.wParam;
}</regex>

How it works:

It’s pretty simple, Zune software sends a WM_COPYDATA message to a window with class name MsnMsgrUIManager, the message contains a pointer to a unicode string, that looks something like this:

ZUNE\0Music\01\0{0} - {1}\0TRACK_TITLE\0TRACK_ARTIST\0TRACK_ALBUM\0zune:ZUNE_GUID\0
 
// TRACK_TITLE: Title
// TRACK_ARTIST: Artist
// TRACK_ALBUM: Album
// ZUNE_GUID:  A GUID that may refer to the Zune device, Zune account, or Zune software itself.
 
// Note:  {0} - {1} is the format of how the string should be displayed in WLM - I ignore this entirely.
// Note2:  This method is used by other software, so I don't gate on ZUNE for the sake of maximum flexibility.  
// Note3:  The "1" after Music specifies the playing state.  0=Stopped, 1=Playing.  I ignore this and instead just use whether or not the other fields exist.

Note:  The \0’s in the above string are literally \ and 0, not NULL characters.

Using a regular expression to match the fields, The artist and title are extracted from the string, and set as the window caption for a window with class name Winamp v1.x. Software (like Digsby, or other now playing apps) may use FindWindow to locate the fake Winamp player, and read the caption with GetWindowText.

You may need to install the Microsoft Visual C++ 2010 Redistributable Package (x86).