//------------------------------------------------------------------------------
#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));
}
//------------------------------------------------------------------------------