// DecalEnum.cpp : Implementation of cDecalEnum #include "stdafx.h" #include "Decal.h" #include "DecalEnum.h" ///////////////////////////////////////////////////////////////////////////// // cDecalEnum cDecalEnum::~cDecalEnum() { // Write out the order if there were changes if ( !m_bOrderChanged ) return; USES_CONVERSION; std::string strOrder; strOrder.reserve ( m_keys.size () ); for ( cKeyList::iterator i = m_keys.begin (); i != m_keys.end(); ++ i ) strOrder += i->m_letter; // Compile the new order from the RegKey hkGroup; if( hkGroup.Create( HKEY_LOCAL_MACHINE, m_strGroupKey.c_str() ) != ERROR_SUCCESS ) { _ASSERTE ( FALSE ); return; } hkGroup.SetStringValue (_T("Order"), strOrder.c_str ()); } bool cDecalEnum::Initialize( IDecal *pDecal, BSTR strKey ) { _ASSERTE( pDecal != NULL ); USES_CONVERSION; m_strGroupKey = "SOFTWARE\\Decal\\"; m_strGroupKey += OLE2A( strKey ); m_strGroup = strKey; m_pDecal = pDecal; m_nIndex = -1; m_bOrderChanged = false; return true; } HRESULT cDecalEnum::Advance( REFCLSID clsid ) { // Assign a dummy letter cKeyEntry ke; ke.m_clsid = clsid; ke.m_letter = _T( 'A' ); m_keys.reserve ( 1 ); m_keys.push_back ( ke ); return openKeyAt ( 0 ); } HRESULT cDecalEnum::Begin() { USES_CONVERSION; // Load and sort the list RegKey hkGroup; if( hkGroup.Create( HKEY_LOCAL_MACHINE, m_strGroupKey.c_str() ) != ERROR_SUCCESS ) return Error ( IDE_GROUPKEYNOTOPEN ); // Load the order string and prepare a new item string std::string strIdentifiers( "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789,./;'[]-=`<>?:{}|_+`!@#$%^&*()" ); DWORD nMaxOrder = strIdentifiers.length (); LPTSTR szOrder = reinterpret_cast< LPTSTR > ( _alloca ( sizeof ( TCHAR ) * nMaxOrder ) ); // Count the number of subkeys DWORD dwSubkeys; if ( ::RegQueryInfoKey ( hkGroup, NULL, NULL, NULL, &dwSubkeys, NULL, NULL, NULL, NULL, NULL, NULL, NULL ) != ERROR_SUCCESS ) return Error ( IDE_INVALIDSUBKEYS ); m_keys.reserve ( dwSubkeys ); LPTSTR szEndOrder; // Load an existing order if ( hkGroup.QueryStringValue (_T("Order"), szOrder, &nMaxOrder) == ERROR_SUCCESS ) { szEndOrder = szOrder + ( nMaxOrder - 1 ); // Sort and merge the differences std::string strOrder ( szOrder, szEndOrder ); std::sort ( strOrder.begin (), strOrder.end () ); std::string strMerged; strMerged.reserve ( strIdentifiers.length () - ( nMaxOrder - 1 ) ); std::set_difference ( strIdentifiers.begin (), strIdentifiers.end (), strOrder.begin (), strOrder.end (), std::inserter ( strMerged, strMerged.end() ) ); // Reset the identifiers to exclude the currently used ones strIdentifiers.swap ( strMerged ); } else { szOrder[ 0 ] = _T( '\0' ); szEndOrder = szOrder; } std::string::iterator iNextID = strIdentifiers.begin (); // Now we walk through the subkeys and insert in order TCHAR szCLSID[ 64 ]; for ( int i = 0; ::RegEnumKey ( hkGroup, i, szCLSID, sizeof ( szCLSID ) ) == ERROR_SUCCESS; ++ i ) { CLSID clsid; HRESULT hRes = ::CLSIDFromString ( T2OLE ( szCLSID ), &clsid ); if ( FAILED ( hRes ) ) // Skip non-CLSID keys continue; RegKey hkItem; if ( hkItem.Open ( hkGroup, szCLSID ) != ERROR_SUCCESS ) continue; // Attempt to read the sequence indicator TCHAR szSequence[ 4 ]; DWORD dwSequence = sizeof ( szSequence ); cKeyEntry ke; ke.m_clsid = clsid; if ( hkItem.QueryStringValue ( _T( "Order" ), szSequence, &dwSequence ) == ERROR_SUCCESS ) { _ASSERTE ( szSequence[ 1 ] == _T( '\0' ) ); ke.m_letter = *szSequence; insertKey ( ke, szOrder, szEndOrder ); continue; } // There's no sequence current, add on to the end _ASSERTE ( iNextID != strIdentifiers.end () ); ke.m_letter = *( iNextID ++ ); m_keys.push_back ( ke ); TCHAR szVal[ 2 ] = { ke.m_letter, _T( '\0' ) }; hkItem.SetStringValue ( _T( "Order" ), szVal ); // The sequence is changed by adding items m_bOrderChanged = true; } return S_OK; } void cDecalEnum::insertKey ( cKeyEntry &ke, TCHAR *szOrder, TCHAR *szEndOrder ) { // Insert the item in the correct order into the list - first // find the sequence number in the list for ( TCHAR *szOrderIndex = szOrder; szOrderIndex != szEndOrder; ++ szOrderIndex ) if ( *szOrderIndex == ke.m_letter ) break; if ( szOrderIndex == szEndOrder ) { // This item is last, don't search for anything after m_keys.push_back ( ke ); return; } // We have an insert, search for the member to insert before for ( szOrderIndex = szOrderIndex + 1; szOrderIndex != szEndOrder; ++ szOrderIndex ) { // Search for a matchinf item in the array for ( cKeyList::iterator j = m_keys.begin(); j != m_keys.end(); ++ j ) { if ( j->m_letter == *szOrderIndex ) { m_keys.insert ( j, ke ); return; } } } // The next entry most likely hasn't been loaded yet - just append m_keys.push_back ( ke ); } HRESULT cDecalEnum::openKeyAt ( int nIndex ) { USES_CONVERSION; if ( m_key.m_hKey != NULL ) m_key.Close (); LPOLESTR strClsid; ::StringFromCLSID( m_keys[ nIndex ].m_clsid, &strClsid ); LPCTSTR szClsid = OLE2T( strClsid ); ::CoTaskMemFree( strClsid ); std::string strItemKey = m_strGroupKey + _T( "\\" ); strItemKey += szClsid; if ( m_key.Open ( HKEY_LOCAL_MACHINE, strItemKey.c_str () ) != ERROR_SUCCESS ) return Error ( IDE_CLASSKEYNOTFOUND ); m_nIndex = nIndex; return S_OK; } STDMETHODIMP cDecalEnum::get_FriendlyName(BSTR *pVal) { if( pVal == NULL ) { _ASSERT( FALSE ); return E_POINTER; } if( m_key.m_hKey == NULL ) { _ASSERT( FALSE ); return Error ( IDE_BADITERATOR ); } USES_CONVERSION; TCHAR szName[ MAX_PATH ]; DWORD dwSize = MAX_PATH; if( m_key.QueryStringValue( NULL, szName, &dwSize ) != ERROR_SUCCESS ) return E_FAIL; *pVal = T2BSTR( szName ); return S_OK; } STDMETHODIMP cDecalEnum::get_ComClass(CLSID *pVal) { if( pVal == NULL ) { _ASSERT( FALSE ); return E_POINTER; } if( m_key.m_hKey == NULL ) { _ASSERT( FALSE ); return Error ( IDE_BADITERATOR ); } USES_CONVERSION; *pVal = m_keys[ m_nIndex ].m_clsid; return S_OK; } STDMETHODIMP cDecalEnum::get_Enabled(VARIANT_BOOL *pVal) { if( pVal == NULL ) { _ASSERT( FALSE ); return E_POINTER; } if( m_key.m_hKey == NULL ) { _ASSERT( FALSE ); return Error ( IDE_BADITERATOR ); } DWORD dwEnabled; if( m_key.QueryDWORDValue( _T("Enabled"), dwEnabled ) != ERROR_SUCCESS ) // Plugins are enabled when this value is ommitted dwEnabled = TRUE; *pVal = ( dwEnabled ) ? VARIANT_TRUE : VARIANT_FALSE; return S_OK; } STDMETHODIMP cDecalEnum::put_Enabled(VARIANT_BOOL newVal) { if( m_key.m_hKey == NULL ) { _ASSERT( FALSE ); return Error ( IDE_BADITERATOR ); } if( m_key.SetDWORDValue( _T( "Enabled" ), ( ( newVal ) ? 1 : 0 ) ) != ERROR_SUCCESS ) return E_FAIL; return S_OK; } STDMETHODIMP cDecalEnum::CreateInstance(REFIID iid, LPVOID *ppvItf) { CLSID clsidSurrogate; if( FAILED( get_SurrogateClass( &clsidSurrogate ) ) ) { _ASSERT( FALSE ); return E_FAIL; } if( clsidSurrogate == GUID_NULL ) { // Create the object without a surrogate CLSID clsidObject; HRESULT hRes = get_ComClass( &clsidObject ); if( FAILED( hRes ) ) return hRes; return ::CoCreateInstance( clsidObject, NULL, CLSCTX_INPROC_SERVER, iid, ppvItf ); } // Create the surrogate object CComPtr< IDecalEnum > pEnum; static _bstr_t _strSurrogates ( _T( "Surrogates" ) ); HRESULT hRes = m_pDecal->get_Configuration ( _strSurrogates, clsidSurrogate, &pEnum ); if ( !SUCCEEDED ( hRes ) ) return hRes; VARIANT_BOOL bEnabled; hRes = pEnum->get_Enabled ( &bEnabled ); _ASSERTE ( SUCCEEDED ( hRes ) ); if ( !bEnabled ) return Error ( IDE_SURROGATEDISABLED ); CComPtr< IDecalSurrogate > pSurrogate; hRes = pEnum->CreateInstance ( IID_IDecalSurrogate, reinterpret_cast< LPVOID * > ( &pSurrogate ) ); if( FAILED( hRes ) ) return hRes; return pSurrogate->CreateInstance( this, iid, ppvItf ); } STDMETHODIMP cDecalEnum::Next() { if ( m_nIndex == m_keys.size () - 1 ) { // End of iteration if ( m_key.m_hKey != NULL ) m_key.Close (); return S_FALSE; } return openKeyAt ( m_nIndex + 1 ); } STDMETHODIMP cDecalEnum::get_SurrogateClass(CLSID *pVal) { if( pVal == NULL ) { _ASSERT( FALSE ); return E_POINTER; } if( m_key.m_hKey == NULL ) { _ASSERT( FALSE ); return Error ( IDE_BADITERATOR ); } USES_CONVERSION; TCHAR szName[ MAX_PATH + 1 ]; DWORD dwSize = MAX_PATH + 1; if( m_key.QueryStringValue( _T("Surrogate"), szName, &dwSize ) != ERROR_SUCCESS ) { // If the surrogate is not found, return the NULL guid *pVal = GUID_NULL; return S_OK; } return ::CLSIDFromString( T2OLE( szName ), pVal ); } STDMETHODIMP cDecalEnum::get_ResourcePath(BSTR *pVal) { if( pVal == NULL ) { _ASSERT( FALSE ); return E_POINTER; } if( m_key.m_hKey == NULL ) { _ASSERT( FALSE ); return Error ( IDE_BADITERATOR ); } USES_CONVERSION; TCHAR szName[ MAX_PATH ]; DWORD dwSize = MAX_PATH; if( m_key.QueryStringValue( NULL, szName, &dwSize ) != ERROR_SUCCESS ) return E_FAIL; *pVal = T2BSTR( szName ); return S_OK; } STDMETHODIMP cDecalEnum::get_Property(BSTR Name, VARIANT *pVal) { if( pVal == NULL ) { _ASSERT( FALSE ); return E_POINTER; } if( m_key.m_hKey == NULL ) { _ASSERT( FALSE ); return Error ( IDE_BADITERATOR ); } USES_CONVERSION; LPCTSTR szValueName = OLE2T( Name ); // Get some info about the property DWORD dwDataType; if( ::RegQueryValueEx( m_key.m_hKey, szValueName, NULL, &dwDataType, NULL, NULL ) != ERROR_SUCCESS ) // We'll assume there's no value return E_INVALIDARG; switch( dwDataType ) { case REG_DWORD: { DWORD dwValue; if( m_key.QueryDWORDValue( szValueName, dwValue ) != ERROR_SUCCESS ) return E_FAIL; pVal->vt = VT_I4; pVal->lVal = dwValue; return S_OK; } break; case REG_EXPAND_SZ: case REG_SZ: { TCHAR szValue[ MAX_PATH ]; DWORD dwSize = MAX_PATH; if( m_key.QueryStringValue( szValueName, szValue, &dwSize ) != ERROR_SUCCESS ) return E_FAIL; pVal->vt = VT_BSTR; pVal->bstrVal = T2BSTR( szValue ); return S_OK; } break; } return E_INVALIDARG; } STDMETHODIMP cDecalEnum::get_Group(BSTR *pVal) { if( pVal == NULL ) { _ASSERT( FALSE ); return E_POINTER; } *pVal = OLE2BSTR( m_strGroup ); return S_OK; } STDMETHODIMP cDecalEnum::Skip (REFCLSID clsid) { // Search our loaded list for the clsid for ( cKeyList::iterator i = m_keys.begin (); i != m_keys.end(); ++ i ) { if ( clsid == i->m_clsid ) return openKeyAt ( i - m_keys.begin() ); } return Error ( IDE_CLASSNOTFOUND ); } STDMETHODIMP cDecalEnum::MoveBefore(REFCLSID clsidBefore) { if( m_key.m_hKey == NULL ) { _ASSERT( FALSE ); return Error ( IDE_BADITERATOR ); } // Find the entry we're inserting before for ( cKeyList::iterator i = m_keys.begin (); i != m_keys.end(); ++ i ) { if ( i->m_clsid == clsidBefore ) break; } if ( i == m_keys.end () ) return Error ( IDE_CLASSNOTFOUND ); int nFoundIndex = i - m_keys.begin (); if ( nFoundIndex == m_nIndex ) // No op return S_FALSE; if ( nFoundIndex > m_nIndex ) -- nFoundIndex; cKeyEntry eRemoved = m_keys[ m_nIndex ]; m_keys.erase ( m_keys.begin () + m_nIndex ); m_keys.insert ( m_keys.begin () + nFoundIndex, eRemoved ); // Reset so we're still on the same item m_nIndex = nFoundIndex; m_bOrderChanged = true; return S_OK; }