All 5 phases of the open-source Decal rebuild: Phase 1: 14 decompiled .NET projects (Interop.*, Adapter, FileService, DecalUtil) Phase 2: 10 native DLLs rewritten as C# COM servers with matching GUIDs - DecalDat, DHS, SpellFilter, DecalInput, DecalNet, DecalFilters - Decal.Core, DecalControls, DecalRender, D3DService Phase 3: C++ shims for Inject.DLL (D3D9 hooking) and LauncherHook.DLL Phase 4: DenAgent WinForms tray application Phase 5: WiX installer and build script 25 C# projects building with 0 errors. Native C++ projects require VS 2022 + Windows SDK (x86). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
519 lines
12 KiB
C++
519 lines
12 KiB
C++
// 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;
|
|
}
|