// DecalManager.cpp : Implementation of cDecal #include "stdafx.h" #include "Decal.h" #include "DecalManager.h" #include "DecalEnum.h" #include "PluginSite.h" ///////////////////////////////////////////////////////////////////////////// // cDecal HRESULT cDecal::convertToken( std::string &strToken, std::string &strOut ) { USES_CONVERSION; struct cPathRegistry { LPCTSTR szToken, szKey, szValue; }; static cPathRegistry _paths[] = { { _T( "ac" ), _T( "SOFTWARE\\Microsoft\\Microsoft Games\\Asheron's Call\\1.00" ), _T( "Portal Dat" ) }, { _T( "decal" ), _T( "SOFTWARE\\Decal\\Agent" ), _T( "AgentPath" ) } }, *_end_paths = _paths + sizeof( _paths ) / sizeof( cPathRegistry ); // There are two types of tokens, ones that contain a colon (object relative) // and the other kind, which just gets looked up in the registry int nColon = strToken.find_first_of( _T( ':' ) ); if( nColon == std::string::npos ) { // Take the whole token string and look it up for( cPathRegistry *pPath = _paths; pPath != _end_paths; ++ pPath ) { if( strToken.compare( pPath->szToken ) != 0 ) continue; // Found the path now look it up in the registry DWORD dwCount = MAX_PATH; TCHAR szBuffer[ MAX_PATH ]; RegKey key; key.Open( HKEY_LOCAL_MACHINE, pPath->szKey ); key.QueryStringValue (pPath->szValue, szBuffer, &dwCount); strOut += T2A( szBuffer ); return S_OK; } // Token not found _ASSERT( FALSE ); return E_INVALIDARG; } // Attempt to convert the CLSID LPOLESTR strCLSID = A2OLE( strToken.substr( nColon + 1 ).c_str() ); HRESULT hRes; CLSID clsid; if( strCLSID[ 0 ] == OLESTR( '{' ) ) hRes = ::CLSIDFromString( strCLSID, &clsid ); else hRes = ::CLSIDFromProgID( strCLSID, &clsid ); if( FAILED( hRes ) ) { _ASSERT( FALSE ); return E_INVALIDARG; } CComPtr< IDecalEnum > pEnum; if( get_Configuration( _bstr_t( strToken.substr( 0, nColon ).c_str() ), clsid, &pEnum ) != S_OK ) { _ASSERT( FALSE ); return E_INVALIDARG; } CComBSTR strResourcePath; if( FAILED( pEnum->get_ResourcePath( &strResourcePath ) ) ) { _ASSERT( FALSE ); return E_INVALIDARG; } strOut += OLE2A( strResourcePath ); return S_OK; } STDMETHODIMP cDecal::InitGraphics( IUnknown *pDirectDraw, IUnknown *pD3DDevice ) { m_pD = static_cast< IDirectDraw4 * >( pDirectDraw ); m_pD3D = static_cast< IDirect3DDevice3 * >( pD3DDevice ); for( cServiceList::iterator i = m_services.begin(); i != m_services.end(); ++ i ) { if( !( i->m_dwCaps & eServiceRender ) ) continue; CComPtr< IDecalRender > pRender; HRESULT hRes = i->m_p->QueryInterface( &pRender ); _ASSERTE( SUCCEEDED( hRes ) ); pRender->ChangeDirectX(); } return S_OK; } STDMETHODIMP cDecal::get_DirectDraw(REFIID iid, VOID * * ppvItf) { return m_pD->QueryInterface( iid, ppvItf ); } STDMETHODIMP cDecal::get_D3DDevice(REFIID iid, VOID * * ppvItf) { return m_pD3D->QueryInterface( iid, ppvItf ); } STDMETHODIMP cDecal::get_HWND(LONG * pVal) { if (pVal == NULL) return E_POINTER; *pVal = reinterpret_cast< long >( m_hWnd ); return S_OK; } STDMETHODIMP cDecal::put_HWND( LONG newVal ) { HWND newWnd = reinterpret_cast< HWND >( newVal ); if( newWnd != m_hWnd ) { m_hWnd = newWnd; for( cServiceList::iterator i = m_services.begin(); i != m_services.end(); ++ i ) { if( !( i->m_dwCaps & eServiceRender ) ) continue; CComPtr< IDecalRender > pRender; HRESULT hRes = i->m_p->QueryInterface( &pRender ); _ASSERTE( SUCCEEDED( hRes ) ); pRender->ChangeHWND(); } } return S_OK; } STDMETHODIMP cDecal::get_Focus(VARIANT_BOOL * pVal) { if (pVal == NULL) return E_POINTER; *pVal = m_bFocus; return S_OK; } STDMETHODIMP cDecal::put_Focus( VARIANT_BOOL newVal ) { m_bFocus = newVal; return S_OK; } STDMETHODIMP cDecal::get_ScreenSize(long *pWidth, long *pHeight) { if (pWidth == NULL || pHeight == NULL) return E_POINTER; RECT rc; ::GetClientRect( m_hWnd, &rc ); *pWidth = rc.right - rc.left; *pHeight = rc.bottom - rc.top; return S_OK; } STDMETHODIMP cDecal::MapPath(BSTR pPath, BSTR * pMapped) { USES_CONVERSION; if (pMapped == NULL) { _ASSERT( FALSE ); return E_POINTER; } std::string str( OLE2A( pPath ) ), strOut; // If the base implementation, iterate over the %% pairs and make substitutions // where appropriate int nIterate = 0; for( ;; ) { int nStart = str.find_first_of( _T( '%' ), nIterate ); if( nStart == std::string::npos ) { strOut += str.substr( nIterate ); break; } int nEnd = str.find_first_of( _T( '%' ), nStart + 1 ); HRESULT hRes = convertToken( str.substr( nStart + 1, nEnd - ( nStart + 1 ) ), strOut ); if( FAILED( hRes ) ) return hRes; if( nEnd == std::string::npos ) { // Unterminated '%' _ASSERT( FALSE ); return E_INVALIDARG; } nIterate = nEnd + 1; if( nIterate == str.length() ) { // The string ended in a token, break now break; } } // We're done - convert the string *pMapped = A2BSTR( strOut.c_str() ); return S_OK; } STDMETHODIMP cDecal::StartPlugins() { if( !m_bServicesStarted ) { HRESULT hRes = StartServices(); if( FAILED( hRes ) ) return hRes; } if( m_bPluginsStarted ) return S_FALSE; // Walk through the list of services and send the plugin start notification { for( cServiceList::iterator i = m_services.begin(); i != m_services.end(); ++ i ) i->m_p->BeforePlugins(); } CComPtr< IDecalEnum > pEnumPlugins; get_Configuration( _bstr_t( "Plugins" ), GUID_NULL, &pEnumPlugins ); while( pEnumPlugins->Next() == S_OK ) { VARIANT_BOOL bEnabled; pEnumPlugins->get_Enabled( &bEnabled ); if( !bEnabled ) continue; cPlugin plugin; pEnumPlugins->get_ComClass( &plugin.m_clsid ); // Attempt to make an instance of the plugin CComPtr< IPlugin2 > pPlugin; HRESULT hRes = pEnumPlugins->CreateInstance( __uuidof( IPlugin2 ), reinterpret_cast< void ** >( &pPlugin ) ); if( FAILED( hRes ) ) { _ASSERT( FALSE ); continue; } CComObject< cPluginSite > *pSite; CComObject< cPluginSite >::CreateInstance( &pSite ); CComPtr< IPluginSite2 > pSiteRef = pSite; pSite->m_pPlugin = pPlugin; pSite->m_pDecal = this; hRes = pSite->m_pPlugin->Initialize( pSiteRef ); if( FAILED( hRes ) ) { _ASSERT( FALSE ); continue; } plugin.m_pSite = pSite; m_plugins.push_back( plugin ); // NOTE: If the plugin has not stored the IPluginSite2 interface pointer, it // will immediately terminate } m_bPluginsStarted = true; return S_OK; } STDMETHODIMP cDecal::StopPlugins() { if( !m_bPluginsStarted ) return S_FALSE; IPluginSite2 **pSites = reinterpret_cast< IPluginSite2 ** > ( _alloca ( sizeof ( IPluginSite2 * ) * m_plugins.size () ) ); { IPluginSite2 **i2 = pSites; for( cPluginList::iterator i = m_plugins.begin(); i != m_plugins.end(); ++ i, ++ i2 ) *i2 = i->m_pSite; } { IPluginSite2 **end_sites = pSites + m_plugins.size (); for ( IPluginSite2 **i = pSites; i != end_sites; ++ i ) ( *i )->Unload (); } _ASSERTE ( m_plugins.empty () ); m_bPluginsStarted = false; // Walk through the list of services and send the plugin start notification { for( cServiceList::iterator i = m_services.begin(); i != m_services.end(); ++ i ) i->m_p->AfterPlugins(); } return S_OK; } STDMETHODIMP cDecal::get_Object(BSTR strPath, REFIID iid, LPVOID *pVal) { if( pVal == NULL ) { _ASSERT( FALSE ); return E_POINTER; } USES_CONVERSION; std::string str = OLE2A( strPath ); // OK, strPath is formated as such, {plugin|service}\{{clsid|progid}[\text] int nOffset = str.find_first_of( _T( '\\' ) ); if( nOffset == std::string::npos ) { _ASSERT( FALSE ); return E_INVALIDARG; } // Find the next backslash int nclsid = str.find_first_of( _T( '\\' ), nOffset + 1 ); // Attempt to convert it to a CLSID std::string strCLSID = str.substr( nOffset + 1, nclsid - ( nOffset + 1 ) ); CLSID clsid; HRESULT hRes; if( strCLSID[ 0 ] == _T( '{' ) ) hRes = ::CLSIDFromString( A2OLE( strCLSID.c_str() ), &clsid ); else hRes = ::CLSIDFromProgID( A2OLE( strCLSID.c_str() ), &clsid ); if( FAILED( hRes ) ) { // The substring could not be converted to a proper CLSID _ASSERT( FALSE ); return E_INVALIDARG; } std::string collection = str.substr( 0, nOffset ); CComPtr< IUnknown > pObject; if( collection.compare( "plugins" ) == 0 ) { for( cPluginList::iterator i = m_plugins.begin(); i != m_plugins.end(); ++ i ) { if( i->m_clsid == clsid ) { pObject = i->m_pSite->m_pPlugin; break; } } } else if( collection.compare( "services" ) == 0 ) { for( cServiceList::iterator i = m_services.begin(); i != m_services.end(); ++ i ) { if( i->m_clsid == clsid ) { pObject = i->m_p; break; } } } else { // Invalid prefix _ASSERT( FALSE ); return E_INVALIDARG; } if( pObject == NULL ) { // Could not find a match for that clsid _ASSERT( FALSE ); return E_INVALIDARG; } while( nclsid != std::string::npos ) { CComPtr< IDecalDirectory > pDir; HRESULT hRes = pObject->QueryInterface( &pDir ); if( FAILED( hRes ) ) { _ASSERT( FALSE ); return E_INVALIDARG; } int nEndclsid = str.find_first_of( _T( '\\' ), nclsid + 1 ); pObject.Release(); hRes = pDir->Lookup( _bstr_t( str.substr( nclsid + 1, nEndclsid - ( nclsid + 1 ) ).c_str() ), &pObject ); if( FAILED( hRes ) ) return hRes; nclsid = nEndclsid; } return pObject->QueryInterface( iid, pVal ); } struct cItfCaps { const IID *iid; DWORD m_dwMask; }; STDMETHODIMP cDecal::StartServices() { static cItfCaps _service_caps[] = { { &IID_IDecalRender, eServiceRender } }, *_end_service_caps = _service_caps + sizeof( _service_caps ) / sizeof( cItfCaps ); if( m_bServicesStarted ) // They've already been started return S_FALSE; m_pHooks.CoCreateInstance( _bstr_t( "Decal.ACHooks" ), NULL, CLSCTX_INPROC_SERVER ); m_pHooks->SetDecal( reinterpret_cast< IUnknown * >( this ) ); CComPtr< IDecalEnum > pEnumServices; get_Configuration( _bstr_t( "Services" ), GUID_NULL, &pEnumServices ); while( pEnumServices->Next() == S_OK ) { VARIANT_BOOL bEnabled; HRESULT hRes = pEnumServices->get_Enabled ( &bEnabled ); _ASSERTE ( SUCCEEDED ( hRes ) ); if ( !bEnabled ) continue; cService service; pEnumServices->get_ComClass( &service.m_clsid ); hRes = pEnumServices->CreateInstance( __uuidof( IDecalService ), reinterpret_cast< void ** >( &service.m_p ) ); if( FAILED( hRes ) ) { _ASSERT( FALSE ); // Unlike plugins, all services must initialize correctly return hRes; } hRes = service.m_p->Initialize( this ); if( FAILED( hRes ) ) { _ASSERT( FALSE ); // The service could not initialize for some reason return hRes; } service.m_dwCaps = 0; CComPtr< IUnknown > pUnkCaps; for( cItfCaps *i_caps = _service_caps; i_caps != _end_service_caps; ++ i_caps ) { HRESULT hRes = service.m_p->QueryInterface( *i_caps->iid, reinterpret_cast< void ** >( &pUnkCaps ) ); if( SUCCEEDED( hRes ) ) { service.m_dwCaps |= i_caps->m_dwMask; pUnkCaps.Release(); } } m_services.push_back( service ); } m_bServicesStarted = true; return S_OK; } STDMETHODIMP cDecal::get_Configuration(BSTR strType, REFCLSID clsidAdvance, IDecalEnum **pVal) { if (pVal == NULL) { _ASSERT( FALSE ); return E_POINTER; } CComObject< cDecalEnum > *pEnum; HRESULT hRes = CComObject< cDecalEnum >::CreateInstance( &pEnum ); if( FAILED( hRes ) ) return hRes; CComPtr< IUnknown > pUnk = pEnum; if( !pEnum->Initialize( this, strType ) ) return E_FAIL; if( clsidAdvance != GUID_NULL ) { HRESULT hRes = pEnum->Advance( clsidAdvance ); if ( FAILED ( hRes ) ) return hRes; } else { HRESULT hRes = pEnum->Begin(); if ( FAILED ( hRes ) ) return hRes; } return pUnk->QueryInterface( pVal ); } STDMETHODIMP cDecal::StopServices() { if( m_bPluginsStarted ) StopPlugins( ); if( !m_bServicesStarted ) return S_FALSE; try { while( !m_services.empty() ) { cServiceList::iterator i = ( m_services.end() - 1 ); i->m_p->Terminate(); m_services.erase( i ); } } catch( ... ) { } if( m_pHooks.p ) m_pHooks.Release( ); return S_OK; } STDMETHODIMP cDecal::get_Plugin(REFCLSID clsid, REFIID iid, LPVOID *pVal) { for( cPluginList::iterator i = m_plugins.begin(); i != m_plugins.end(); ++ i ) { if( i->m_clsid == clsid ) return i->m_pSite->m_pPlugin->QueryInterface( iid, pVal ); } return E_INVALIDARG; } STDMETHODIMP cDecal::get_Service(REFCLSID clsid, REFIID iid, LPVOID *pVal) { for( cServiceList::iterator i = m_services.begin(); i != m_services.end(); ++ i ) { if( i->m_clsid == clsid ) return i->m_p->QueryInterface( iid, pVal ); } return E_INVALIDARG; } STDMETHODIMP cDecal::Render2D() { for( cServiceList::iterator i = m_services.begin(); i != m_services.end(); ++ i ) { if( !( i->m_dwCaps & eServiceRender ) ) continue; CComPtr< IDecalRender > pRender; HRESULT hRes = i->m_p->QueryInterface( &pRender ); _ASSERTE( SUCCEEDED( hRes ) ); pRender->Render2D(); } return S_OK; } STDMETHODIMP cDecal::Render3D() { for( cServiceList::iterator i = m_services.begin(); i != m_services.end(); ++ i ) { if( !( i->m_dwCaps & eServiceRender ) ) continue; CComPtr< IDecalRender > pRender; HRESULT hRes = i->m_p->QueryInterface( &pRender ); _ASSERTE( SUCCEEDED( hRes ) ); pRender->Render3D(); } return S_OK; } STDMETHODIMP cDecal::get_Hooks(IACHooks** pVal) { if( m_pHooks == NULL ) return E_FAIL; m_pHooks->QueryInterface( pVal ); return S_OK; }