Introduction
A few days ago, I downloaded and installed the electricsheep screensaver. This is such a cool screensaver, I wanted to watch it while I worked, and I found the SheepWatcher.
So I started thinking: "that's cool, how did they do that, run the screen saver in a window?" Good old Microsoft had the anwser.
So being a Windows developer, I decided I could do better, a Windows Media Player plug-in to run ElectricSheep.scr. The first step was to get the WMP SDK:
It did not work with VS.NET 2005:
Test
OK, create a C++ project for a Windows Media Player Visualization Plug-in, we will call it SheepWMP. Actually, I wrote a quick test dialog application like so:
A small helper function to wrap CreateProcess
:
static HANDLE LaunchProcess ( LPTSTR aProcessName, LPTSTR aArgs )
{
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;
CreateProcess ( aProcessName, aArgs, NULL, NULL, FALSE, 0,
NULL, NULL, & lStartupInfo, & lProcessInfo );
WaitForInputIdle ( lProcessInfo.hProcess, INFINITE ) ;
return lProcessInfo.hProcess ;
}
BOOL CSheepTestDlg::OnInitDialog()
{
CDialog::OnInitDialog();
SetIcon(m_hIcon, TRUE); SetIcon(m_hIcon, FALSE);
TCHAR lCommand [ 1024 ] ;
_stprintf_s ( lCommand, 1024, _T(" /p %u"),
(DWORD) GetSafeHwnd () ) ;
TCHAR lExe [ 1024 ] ;
_stprintf_s ( lExe, 1024,
_T("c:\\windows\\system32\\electricsheep.scr") ) ;
TRACE ( lCommand ) ;
LaunchProcess ( lExe, lCommand ) ;
return TRUE; }
Code
Right, let's get to the Media Player plug-in. VS2005, with the WMPSDK, will create a basic visualization plug-in. The first steps are to remove all the rendering code and simply attach the screensaver to the desired HWND
:
STDMETHODIMP CSheepWMP::RenderWindowed(TimedLevel *pLevels,
BOOL fRequiredRender )
{
....
TCHAR lCommand [ 1024 ] ;
_stprintf_s ( lCommand, 1024, _T(" /p %u"),
(DWORD) m_hwndParent ) ;
TCHAR lExe [ 1024 ] ;
_stprintf_s ( lExe, 1024,
_T("c:\\windows\\system32\\electricsheep.scr") ) ;
TRACE ( lCommand ) ;
LaunchProcess ( lExe, lCommand ) ;
....
}
But what happens when Windows Media Player changes size:
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 ) ) ;
HWND lWnd = FindWindowEx ( gWnd, NULL,
_T("WindowsScreenSaverClass"), NULL ) ;
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 ) ;
}
}
So now, we have the basics working. If you examine my source, you will see that it locates all the Windows screensavers and can run them all inside the Window Media Player. It's done like so:
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 ) ;
}
Now, inside the Windows Media Player, we list all the screensavers using nice names. You have to get the names from the screensaver .scr files, which can be loaded like resource DLLs:
CAtlString CSheepWMP::GetScrName ( CAtlString aScr )
{
CAtlString lName = PathFindFileName ( aScr ) ;
PathRemoveExtension ( lName . GetBufferSetLength ( MAX_PATH ) ) ;
lName . ReleaseBuffer () ;
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 )
{
CAtlString lDesc ;
LoadString ( lInstance, 1,
lDesc.GetBufferSetLength(MAX_PATH), MAX_PATH ) ;
lDesc . ReleaseBuffer () ;
FreeLibrary ( lInstance ) ;
if ( lDesc . IsEmpty () == FALSE )
{
ATLTRACE ( DEFAULT_TRACE_PREFIX
_T("Set scr name to description, %s\n"), lDesc ) ;
lName = lDesc ;
}
}
return lName ;
}
That's all!
History
- 15 July, 2011: Updated source for new version of Electric Sheep. Also included the C# .NET implementation and the Microsoft Windows setup script.