Click here to Skip to main content
15,885,278 members
Articles / Desktop Programming / MFC

Using screensavers inside the Windows Media Player

Rate me:
Please Sign up or sign in to vote.
4.94/5 (12 votes)
15 Jul 2011CPOL1 min read 107.9K   3.1K   53  
Wrapping a screensaver inside a WMP visualization plug-in.
//------------------------------------------------------------------------------

#include "stdafx.h"
#include <shlobj.h>
#include <psapi.h>
#include "sheepwmp.h"

//------------------------------------------------------------------------------

#define DEFAULT_SCR_FILENAME  _T("c:\\windows\\es.scr")
#define DEFAULT_PROFILE_KEY   _T("SheepWMP")
#define DEFAULT_PROFILE_VALUE _T("SCR")
#define DEFAULT_DLL_EXT       _T(".dll")
#define DEFAULT_INI_EXT       _T(".ini")
#define DEFAULT_COMMAND_PARAM _T(" /p %u")
#define DEFAULT_TRACE_PREFIX  _T("SheepWMP: ")
#define DEFAULT_SCR_EXT       _T("*.scr")

//------------------------------------------------------------------------------

extern HINSTANCE gThisInstance ;
static HANDLE gInstance = NULL ;
static HWND gWnd = NULL ;
static RECT gRect ;
static CAtlString gRunningScr ;
static CAtlString gDefaultScr ;

class CLock 
{
public:

	CLock ()	
	{
		EnterCriticalSection ( GetCriticalSection () ) ;
	}

	~CLock ()
	{
		LeaveCriticalSection ( GetCriticalSection () ) ;
	}

private:

	LPCRITICAL_SECTION GetCriticalSection ()
	{
		static bool lInit = false ;
		static CRITICAL_SECTION lCriticalSection ;
		if ( lInit == false )
		{
			InitializeCriticalSection ( &lCriticalSection ) ;
			lInit = true ;
		}
		return &lCriticalSection ;
	}

 
} ;

//------------------------------------------------------------------------------

static HANDLE LaunchProcess ( LPCTSTR aProcessName, LPCTSTR aArgs, RECT aRect )
{
	STARTUPINFO lStartupInfo;			
	PROCESS_INFORMATION lProcessInfo;

	memset ( &lProcessInfo, 0, sizeof ( lProcessInfo ) );
	memset ( &lStartupInfo, 0, sizeof ( lStartupInfo ) );

	lStartupInfo.cb = sizeof ( lStartupInfo );
	lStartupInfo.dwFlags = STARTF_USESHOWWINDOW;
	lStartupInfo.wShowWindow = SW_SHOWNORMAL;
	lStartupInfo.dwX = aRect.left ;
	lStartupInfo.dwY = aRect.top ;
	lStartupInfo.dwXSize = aRect.right - aRect.left ;
	lStartupInfo.dwYSize = aRect.bottom - aRect.top ;

	ATLTRACE ( DEFAULT_TRACE_PREFIX _T("Set running process %s\n"), aProcessName ) ;
	gRunningScr = aProcessName ;

	// Create target process
	CreateProcess ( (LPTSTR) aProcessName, (LPTSTR) aArgs, NULL, NULL, FALSE, 0, NULL, NULL, & lStartupInfo, & lProcessInfo );

	ATLTRACE ( DEFAULT_TRACE_PREFIX _T("Set process priority to low\n") ) ;
	SetPriorityClass ( lProcessInfo.hProcess, IDLE_PRIORITY_CLASS ) ;

	return lProcessInfo.hProcess ;
}

//------------------------------------------------------------------------------

static CAtlString GetIni ()
{
	CAtlString lFileName ;
	GetModuleFileName ( gThisInstance, lFileName . GetBufferSetLength ( MAX_PATH ), MAX_PATH ) ;
	lFileName . ReleaseBuffer () ;
	lFileName . MakeLower () ;
	lFileName . Replace ( DEFAULT_DLL_EXT, DEFAULT_INI_EXT ) ;

	ATLTRACE ( DEFAULT_TRACE_PREFIX _T("INI file located at %s\n"), lFileName ) ;

	return lFileName ;
}

//------------------------------------------------------------------------------

static CAtlString GetScr ()
{
	// So we only do it once per run
	if ( gDefaultScr . IsEmpty () )
	{
		CAtlString lCmd = DEFAULT_SCR_FILENAME ;
		CAtlString lIni = GetIni () ;
		if ( lIni . IsEmpty () == FALSE )
		{
			GetPrivateProfileString ( DEFAULT_PROFILE_KEY, DEFAULT_PROFILE_VALUE, DEFAULT_SCR_FILENAME, lCmd . GetBufferSetLength ( MAX_PATH ), (DWORD) MAX_PATH, lIni ) ;
			lCmd . ReleaseBuffer () ;
		}
		
		ATLTRACE ( DEFAULT_TRACE_PREFIX _T("SCR file located at %s\n"), lCmd ) ;

		gDefaultScr = lCmd ;
	}
	return gDefaultScr ;
}

//------------------------------------------------------------------------------

static void RunProcess ( HWND aWnd, LPCTSTR aScr, RECT aRect )
{
	if ( aWnd )
	{
		CAtlString lCommand ;
		lCommand . Format ( DEFAULT_COMMAND_PARAM, (ULONGLONG) aWnd ) ;

		CAtlString lExe = ( aScr != NULL ? aScr : GetScr () ) ;

		ATLTRACE ( DEFAULT_TRACE_PREFIX _T("Launch process \"%s\"\n"), lExe + lCommand ) ;

		// Create the new instance
		gInstance = LaunchProcess ( lExe, lExe + lCommand, aRect ) ;
	}
}

//------------------------------------------------------------------------------

static void KillProcess ()
{
	if ( gInstance )
	{
		ATLTRACE ( DEFAULT_TRACE_PREFIX _T("Kill process \"%u\"\n"), (ULONGLONG) gInstance ) ;

		// Kill the old instance
		TerminateProcess ( gInstance, 0 ) ;
		gInstance = NULL ;

		gRunningScr = _T("") ;
	}
}

//------------------------------------------------------------------------------

CSheepWMP::CSheepWMP() :
m_hwndParent(NULL),
m_clrForeground(0x0000FF),
m_nPreset(0)
{
	CLock lLock ;

	CAtlString lPath ;
	SHGetFolderPath ( NULL, CSIDL_SYSTEM, NULL, 0, lPath . GetBufferSetLength ( MAX_PATH ) ) ;
	lPath . ReleaseBuffer () ;

	ATLTRACE ( DEFAULT_TRACE_PREFIX _T("Finding all scrs in %s\n"), lPath ) ;


	CAtlString lSearch = lPath ;
	PathAppend ( lSearch . GetBufferSetLength ( MAX_PATH ), DEFAULT_SCR_EXT ) ;
	lSearch . ReleaseBuffer () ;

	WIN32_FIND_DATA lFindFileData;
	HANDLE lFind = FindFirstFile ( lSearch, &lFindFileData ) ;
	if ( lFind != INVALID_HANDLE_VALUE )
	{
		do
		{
			CAtlString lScr = lPath ;
			PathAppend ( lScr . GetBufferSetLength ( MAX_PATH ), lFindFileData . cFileName  ) ;
			lScr . ReleaseBuffer () ;
		
			ATLTRACE ( DEFAULT_TRACE_PREFIX _T("Adding scr, %s\n"), lScr ) ;

			m_Scrs . push_back ( lScr ) ;
		}
		while ( FindNextFile ( lFind, &lFindFileData ) ) ;

		FindClose ( lFind ) ;
	}

}

//------------------------------------------------------------------------------

CSheepWMP::~CSheepWMP()
{
}

//------------------------------------------------------------------------------

HRESULT CSheepWMP::FinalConstruct()
{
    return S_OK;
}

//------------------------------------------------------------------------------

void CSheepWMP::FinalRelease()
{
    ReleaseCore();
}

//------------------------------------------------------------------------------

STDMETHODIMP CSheepWMP::Render(TimedLevel *pLevels, HDC hdc, RECT *prc)
{
	
    return S_OK;
}

//------------------------------------------------------------------------------

STDMETHODIMP CSheepWMP::MediaInfo(LONG lChannelCount, LONG lSampleRate, BSTR bstrTitle )
{
    return S_OK;
}


//------------------------------------------------------------------------------

STDMETHODIMP CSheepWMP::GetCapabilities(DWORD * pdwCapabilities)
{
    if (NULL == pdwCapabilities)
    {
        return E_POINTER;
    }

    *pdwCapabilities = 0;
    return S_OK;
}

//------------------------------------------------------------------------------

STDMETHODIMP CSheepWMP::GetTitle(BSTR* bstrTitle)
{
    USES_CONVERSION;

    if (NULL == bstrTitle)
    {
        return E_POINTER;
    }

    CComBSTR bstrTemp;
    bstrTemp.LoadString(IDS_EFFECTNAME); 
        
    if ((!bstrTemp) || (0 == bstrTemp.Length()))
    {
        return E_FAIL;
    }

    *bstrTitle = bstrTemp.Detach();

    return S_OK;
}

//------------------------------------------------------------------------------

LPCTSTR CSheepWMP::ActiveScr () 
{
	if ( m_nPreset > 0 && m_nPreset < (LONG) m_Scrs . size () + 1 )
		return m_Scrs [ m_nPreset - 1 ]  ;
	return NULL ;
}

//------------------------------------------------------------------------------

CAtlString CSheepWMP::GetScrName ( CAtlString aScr ) 
{
	// Initially get the filename and remove extension
	CAtlString lName = PathFindFileName ( aScr ) ;
	PathRemoveExtension ( lName . GetBufferSetLength ( MAX_PATH ) ) ;
	lName . ReleaseBuffer () ;

	// Load the DLL as a data file, and do not resolve any references
	ATLTRACE ( DEFAULT_TRACE_PREFIX _T("Loading library %s\n"), aScr ) ;
	HINSTANCE lInstance = LoadLibraryEx ( aScr, NULL, DONT_RESOLVE_DLL_REFERENCES | LOAD_LIBRARY_AS_DATAFILE ) ;
	if ( lInstance )
	{
		// Examining all SCR, string 1 is the description used by the display properties
		CAtlString lDesc ;
		LoadString ( lInstance, 1, lDesc . GetBufferSetLength ( MAX_PATH ), MAX_PATH ) ;
		lDesc . ReleaseBuffer () ;

		// Clean up
		FreeLibrary ( lInstance ) ;

		// If we have a valid description use it
		if ( lDesc . IsEmpty () == FALSE )
		{
			ATLTRACE ( DEFAULT_TRACE_PREFIX _T("Set scr name to description, %s\n"), lDesc ) ;
			lName = lDesc ;
		}
	}
	return lName ;
}

//------------------------------------------------------------------------------

STDMETHODIMP CSheepWMP::GetPresetTitle(LONG nPreset, BSTR *bstrPresetTitle)
{
    USES_CONVERSION;

    if (NULL == bstrPresetTitle)
    {
        return E_POINTER;
    }

    if ((nPreset < 0) || (nPreset >= (LONG) m_Scrs . size () + 1 ))
    {
        return E_INVALIDARG;
    }

    CComBSTR bstrTemp;
    
    switch (nPreset)
    {
		case 0:
			{
				bstrTemp.LoadString(IDS_DEFAULT); 
			} break;
		default:
			{
				// Use nice name for the screensaver
				bstrTemp = GetScrName ( m_Scrs [ nPreset - 1 ] ) ;
			} break ;
    }
    
    if ((!bstrTemp) || (0 == bstrTemp.Length()))
    {
        return E_FAIL;
    }

    *bstrPresetTitle = bstrTemp.Detach();

    return S_OK;
}

//------------------------------------------------------------------------------

HWND CSheepWMP::FindScreenSaverWnd ( HWND aWnd )
{

	HWND lWnd = NULL ;
	
	lWnd = FindWindowEx ( aWnd, NULL, _T("WindowsScreenSaverClass"), NULL ) ;
	if ( lWnd )
		return lWnd ;

	lWnd = FindWindowEx ( aWnd, NULL, _T("D3DScreenSaverClass"), NULL ) ;
	if ( lWnd )
		return lWnd ;

	lWnd = FindWindowEx ( aWnd, NULL, _T("ElectricsheepWndClass"), NULL ) ;
	if ( lWnd )
		return lWnd ;

	CAtlString lScr = ActiveScr () ;
	if ( lScr.IsEmpty () )
	{
		lScr = GetScr () ;
	}
	lScr . MakeLower () ;

	for ( HWND lChild = GetWindow ( aWnd, GW_CHILD ) ; lChild != NULL ; lChild = GetWindow ( lChild, GW_HWNDNEXT ) )
	{
		DWORD lPID ;
		GetWindowThreadProcessId ( lChild, &lPID ) ;

		HANDLE lProcess = OpenProcess ( PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, false, lPID ) ;
		if ( lProcess )
		{
			TCHAR lModuleFileName [ 256 ] = _T("") ;
			GetModuleFileNameEx ( lProcess, NULL, lModuleFileName, 256 ) ;
			PathStripPath ( lModuleFileName ) ;

			CAtlString lFileName = lModuleFileName ;
			lFileName . MakeLower () ;

			
			if ( lScr . Find ( lFileName ) != -1 )
			{
				lWnd = lChild ;
				break ;
			}
		}
	}
	return lWnd ;
}

//------------------------------------------------------------------------------

STDMETHODIMP CSheepWMP::GetPresetCount(LONG *pnPresetCount)
{
    if (NULL == pnPresetCount)
    {
        return E_POINTER;
    }

    *pnPresetCount = (LONG) m_Scrs . size () + 1 ;

    return S_OK;
}

//------------------------------------------------------------------------------

STDMETHODIMP CSheepWMP::SetCurrentPreset(LONG nPreset)
{
    if ((nPreset < 0) || (nPreset >= (LONG) m_Scrs . size () + 1 ))
    {
        return E_INVALIDARG;
    }

    m_nPreset = nPreset;

    return S_OK;
}

//------------------------------------------------------------------------------

STDMETHODIMP CSheepWMP::GetCurrentPreset(LONG *pnPreset)
{
    if (NULL == pnPreset)
    {
        return E_POINTER;
    }

    *pnPreset = m_nPreset;

    return S_OK;
}

//------------------------------------------------------------------------------

STDMETHODIMP CSheepWMP::SetCore(IWMPCore * pCore)
{
    HRESULT hr = S_OK;

    // release any existing WMP core interfaces
    ReleaseCore();

    // If we get passed a NULL core, this  means
    // that the plugin is being shutdown.

    if (pCore == NULL)
    {
        return S_OK;
    }

    m_spCore = pCore;

    return hr;
}

//------------------------------------------------------------------------------

STDMETHODIMP CSheepWMP::Create(HWND hwndParent)
{
	CLock lLock ;

	m_hwndParent = hwndParent;

	// Store new window
	gWnd = m_hwndParent ;

	KillProcess () ;

	RECT lRect ;
	GetClientRect ( gWnd, &lRect ) ;

	RunProcess ( gWnd, ActiveScr (), lRect ) ;

    return S_OK;
}

//------------------------------------------------------------------------------

STDMETHODIMP CSheepWMP::Destroy()
{
	CLock lLock ;

	m_hwndParent = NULL;
	
	gWnd = NULL ;

	KillProcess () ;

    return S_OK;
}

//------------------------------------------------------------------------------

STDMETHODIMP CSheepWMP::NotifyNewMedia(IWMPMedia *pMedia)
{
    return S_OK;
}

//------------------------------------------------------------------------------

STDMETHODIMP CSheepWMP::OnWindowMessage(UINT msg, WPARAM WParam, LPARAM LParam, LRESULT *plResultParam )
{
    // return S_OK only if the plugin has handled the window message
    // return S_FALSE to let the defWindowProc handle the message
    return S_FALSE;
}

//------------------------------------------------------------------------------

STDMETHODIMP CSheepWMP::RenderWindowed(TimedLevel *pLevels, BOOL fRequiredRender )
{
	CLock lLock ;

	// NULL parent window should not happen 
    if (NULL == m_hwndParent)
    {
        return E_UNEXPECTED;
    }

    // At this point the visualization should draw directly into the parent
    // window. This sample just calls windowless render for simplicity.
	if ( m_hwndParent )
	{
		if ( IsWindowVisible ( m_hwndParent ) == FALSE )
		{
			KillProcess () ;
			return S_OK ;
		}

		// Has the window changed?
		if ( m_hwndParent != gWnd )
		{
			ATLTRACE ( DEFAULT_TRACE_PREFIX _T("HWND changed...\n") ) ;
			// Store new window
			gWnd = m_hwndParent ;

			KillProcess () ;
		}

		// Has the window changed dimensions
		RECT lRect ;
		GetClientRect ( gWnd, &lRect ) ;
		if ( memcmp ( &gRect, &lRect, sizeof ( RECT ) ) != 0 )
		{
			ATLTRACE ( DEFAULT_TRACE_PREFIX _T("Window size changed...\n") ) ;
			memcpy ( &gRect, &lRect, sizeof ( RECT ) ) ;

			// Try to update the exinsing window
			HWND lWnd = FindScreenSaverWnd ( gWnd ) ;
			if ( lWnd ) 
			{
				ATLTRACE ( DEFAULT_TRACE_PREFIX _T("Moving screensaver window...\n") ) ;
				SetWindowPos ( lWnd, NULL, lRect . left, lRect . top, lRect . right - lRect . left, lRect . bottom - lRect . top, SWP_SHOWWINDOW ) ;
			}
			else
			{
				ATLTRACE ( DEFAULT_TRACE_PREFIX _T("Failed to find screensaver window...\n") ) ;
				// KillProcess () ;
			}
		}

		// Check we have the correct SCR running
		LPCTSTR lActive = ActiveScr () ;
		if ( gInstance )
		{
			// NULL, default
			if ( lActive == NULL )
			{
				// Check the running scr matches the default
				if ( gRunningScr != GetScr () )
				{
					ATLTRACE ( DEFAULT_TRACE_PREFIX _T("DEFAULT scr is not running...\n") ) ;
					KillProcess () ;
				}
			}
			// Check the running scr matches the active one
			else if ( gRunningScr != lActive )
			{
				ATLTRACE ( DEFAULT_TRACE_PREFIX _T("ACTIVE scr is not running...\n"), lActive ) ;
				KillProcess () ;
			}
		}

		// If no instance running
		if ( gInstance == NULL )
		{
			RunProcess ( gWnd, lActive, lRect ) ;

		}

		// Finally send keystates to the screensaver
		if ( gWnd )
		{
			// Get status of all keys
			BYTE lKeys [ 256 ] ;
			GetKeyboardState ( lKeys ) ;

			// See if a key has been pressed
			bool lPressed = false ;
			for ( int i = 0 ; i < 256 ; i ++ )
			{
				if (  lKeys [ i ] & 128 )
				{
					ATLTRACE ( DEFAULT_TRACE_PREFIX _T("Key press detected...\n") ) ;
					lPressed = true ;
					break ;
				}
			}

			// Yes a key has been pressed send it to the SCR
			if ( lPressed )
			{
				HWND lWnd = FindScreenSaverWnd ( gWnd ) ;
				if ( lWnd ) 
				{
					ATLTRACE ( DEFAULT_TRACE_PREFIX _T("Sending keypress to screensaver window...\n") ) ;
					for ( int i = 0 ; i < 256 ; i ++ )
					{
						if (  lKeys [ i ] & 128 )
						{
							SendMessage ( lWnd, WM_KEYDOWN, MAKELONG ( i ,0 ), 0) ;
						}
					}
				}
			}
		}
	}
    return S_OK;
}

//------------------------------------------------------------------------------

void CSheepWMP::ReleaseCore()
{
    if (m_spCore)
    {
        m_spCore = NULL;
    }
}

//------------------------------------------------------------------------------

STDMETHODIMP CSheepWMP::get_foregroundColor(BSTR *pVal)
{
	return ColorToWz( pVal, m_clrForeground);
}

//------------------------------------------------------------------------------

STDMETHODIMP CSheepWMP::put_foregroundColor(BSTR newVal)
{
	return WzToColor(newVal, &m_clrForeground);
}


//------------------------------------------------------------------------------

HRESULT CSheepWMP::WzToColor(const WCHAR *pwszColor, COLORREF *pcrColor)
{ 
    if (NULL == pwszColor)
    {
        //NULL color string passed in
        return E_POINTER;
    }

    if (0 == lstrlenW(pwszColor))
    {
        //Empty color string passed in
        return E_INVALIDARG;
    }

    if (NULL == pcrColor)
    {
        //NULL output color DWORD passed in
        return E_POINTER;
    }
    
    if (lstrlenW(pwszColor) != 7)
    {
        //hex color string is not of the correct length
        return E_INVALIDARG;
    }

    DWORD dwRet = 0;
    for (int i = 1; i < 7; i++)
    {
        // shift dwRet by 4
        dwRet <<= 4;
        // and add in the value of this string

        if ((pwszColor[i] >= L'0') && (pwszColor[i] <= L'9'))
        {
            dwRet += pwszColor[i] - '0';
        }
        else if ((pwszColor[i] >= L'A') && (pwszColor[i] <= L'F'))
        {
            dwRet += 10 + (pwszColor[i] - L'A');
        }
        else if ((pwszColor[i] >= L'a') && (pwszColor[i] <= L'f'))
        {
            dwRet += 10 + (pwszColor[i] - L'a');
        }
        else
        {
           //Invalid hex digit in color string
            return E_INVALIDARG;
        }
    }

    *pcrColor = SwapBytes(dwRet);

    return S_OK;
} 

//------------------------------------------------------------------------------

HRESULT CSheepWMP::ColorToWz( BSTR* pbstrColor, COLORREF crColor)
{
    _ASSERT( NULL != pbstrColor );
    _ASSERT( (crColor & 0x00FFFFFF) == crColor );

    *pbstrColor = NULL;

    WCHAR wsz[8];
    HRESULT hr  = S_OK;

    wsprintfW( wsz, L"#%06X", SwapBytes(crColor) );
    
    *pbstrColor = ::SysAllocString( wsz );

    if (!pbstrColor)
    {
        hr = E_FAIL;
    }

    return hr;
}


//------------------------------------------------------------------------------

inline DWORD CSheepWMP::SwapBytes(DWORD dwRet)
{
    return ((dwRet & 0x0000FF00) | ((dwRet & 0x00FF0000) >> 16) | ((dwRet & 0x000000FF) << 16));
}

//------------------------------------------------------------------------------

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Web Developer
Australia Australia
Developing windows applications for over 15 years now starting on Win 3.1 with Object Oriented Pascal, progressed to C++ and OWL, in 1996 switch to MFC and never looked back, now focusing on .NET/Mono.

Comments and Discussions