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>
386 lines
9 KiB
C++
386 lines
9 KiB
C++
// DecalImpl.h
|
|
// Declaration of default implementations for Decal interfaces
|
|
|
|
#ifndef __DECALIMPL_H
|
|
#define __DECALIMPL_H
|
|
|
|
//C Library includes
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
|
|
#include <Decal.h>
|
|
#import <msxml.dll>
|
|
|
|
#include "../Include/Helpers.h"
|
|
|
|
template< class ImplT >
|
|
class ATL_NO_VTABLE IDecalServiceImpl
|
|
: public IDecalService
|
|
{
|
|
public:
|
|
CComPtr< IDecal > m_pDecal;
|
|
|
|
HRESULT onInitialize()
|
|
{
|
|
return S_OK;
|
|
}
|
|
|
|
void onTerminate()
|
|
{
|
|
}
|
|
|
|
STDMETHOD(Initialize)( IDecal *pDecal )
|
|
{
|
|
m_pDecal = pDecal;
|
|
|
|
HRESULT hRes = static_cast< ImplT * >( this )->onInitialize();
|
|
|
|
if( FAILED( hRes ) )
|
|
m_pDecal.Release();
|
|
|
|
return hRes;
|
|
}
|
|
|
|
STDMETHOD(Terminate)()
|
|
{
|
|
static_cast< ImplT * >( this )->onTerminate();
|
|
m_pDecal.Release();
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHOD(BeforePlugins)()
|
|
{
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHOD(AfterPlugins)()
|
|
{
|
|
return S_OK;
|
|
}
|
|
};
|
|
|
|
class IDecalRenderImpl
|
|
: public IDecalRender
|
|
{
|
|
public:
|
|
STDMETHOD(Render2D)()
|
|
{
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHOD(Render3D)()
|
|
{
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHOD(ChangeHWND)()
|
|
{
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHOD(ChangeDirectX)()
|
|
{
|
|
return S_OK;
|
|
}
|
|
};
|
|
|
|
template< class ImplT, const CLSID *pClsid >
|
|
class IDecalFileSurrogateXMLImpl
|
|
: public IDecalFileSurrogate
|
|
{
|
|
public:
|
|
static LPCTSTR getConfigGroup()
|
|
{
|
|
// This function must be overridden
|
|
_ASSERT( FALSE );
|
|
return _T( "Error: Bad config group" );
|
|
}
|
|
|
|
STDMETHOD(Register)(BSTR strFilename)
|
|
{
|
|
USES_CONVERSION;
|
|
|
|
MSXML::IXMLDOMDocumentPtr pDoc;
|
|
|
|
pDoc.CreateInstance( __uuidof( MSXML::DOMDocument ), NULL, CLSCTX_INPROC_SERVER );
|
|
|
|
if( !pDoc->load( strFilename ) )
|
|
{
|
|
_ASSERT( FALSE );
|
|
return E_FAIL;
|
|
}
|
|
|
|
MSXML::IXMLDOMElementPtr pRoot = pDoc->selectSingleNode( _T( "/*" ) );
|
|
_variant_t vCLSID = pRoot->getAttribute( _T( "clsid" ) ),
|
|
vProgID = pRoot->getAttribute( _T( "progid" ) ),
|
|
vName = pRoot->getAttribute( _T( "name" ) );
|
|
|
|
if( vCLSID.vt != VT_BSTR )
|
|
{
|
|
_ASSERT( FALSE );
|
|
return E_FAIL;
|
|
}
|
|
|
|
// Convert the clsid
|
|
CLSID clsid;
|
|
HRESULT hRes = ::CLSIDFromString( vCLSID.bstrVal, &clsid );
|
|
if( FAILED( hRes ) )
|
|
{
|
|
// Improperly formatted CLSID
|
|
_ASSERT( FALSE );
|
|
return hRes;
|
|
}
|
|
|
|
// Everything we need now, make the registry entries
|
|
LPOLESTR strKey, strSurrogate, strUninstall;
|
|
::StringFromCLSID( clsid, &strKey );
|
|
::StringFromCLSID( *pClsid, &strSurrogate );
|
|
::StringFromCLSID( __uuidof( SurrogateRemove ), &strUninstall );
|
|
|
|
LPTSTR szKey = OLE2T( strKey );
|
|
|
|
RegKey rk;
|
|
TCHAR szPluginKey[ 255 ];
|
|
::_stprintf( szPluginKey, _T( "Software\\Decal\\%s\\%s" ), static_cast< ImplT * >( this )->getConfigGroup(), szKey );
|
|
|
|
// Open it up
|
|
rk.Create( HKEY_LOCAL_MACHINE, szPluginKey );
|
|
|
|
if( vName.vt == VT_BSTR )
|
|
rk.SetStringValue (NULL, OLE2T( vName.bstrVal ));
|
|
|
|
LPTSTR szSurrogate = OLE2T( strSurrogate );
|
|
|
|
rk.SetDWORDValue (_T("Enabled"), 1);
|
|
rk.SetStringValue (_T("Surrogate"), szSurrogate);
|
|
|
|
rk.SetStringValue ( _T("Uninstall"), OLE2T( strUninstall ));
|
|
rk.SetStringValue( _T("File"), OLE2T( strFilename ) );
|
|
|
|
if( vProgID.vt == VT_BSTR )
|
|
{
|
|
LPTSTR szProgID = OLE2T( vProgID.bstrVal );
|
|
RegKey rkProgID;
|
|
rkProgID.Create( HKEY_CLASSES_ROOT, szProgID );
|
|
|
|
rkProgID.SetKeyValue( _T( "CLSID" ), szKey );
|
|
|
|
rk.SetStringValue( _T("ProgID"), szProgID );
|
|
}
|
|
|
|
::CoTaskMemFree( strUninstall );
|
|
::CoTaskMemFree( strSurrogate );
|
|
::CoTaskMemFree( strKey );
|
|
|
|
return S_OK;
|
|
}
|
|
};
|
|
|
|
// Disp event dynamic is a hybrid that uses type info from a generated
|
|
// typelib (at runtime) to dispatch messages. Unlike IDispEventImpl which
|
|
// requires a registered typelib. Still, much code is copied from IDispEventImpl.
|
|
template< UINT nID, class T >
|
|
class ATL_NO_VTABLE IDispEventDynamicImpl
|
|
: public IDispEventSimpleImpl< nID, T, &GUID_NULL >
|
|
{
|
|
public:
|
|
typedef IDispEventSimpleImpl< nID, T, &GUID_NULL > _base;
|
|
|
|
CComPtr< ITypeInfo2 > m_pTI;
|
|
|
|
HRESULT GetSourceInterfaceInfo( IUnknown *pUnk, ITypeInfo **ppTI )
|
|
{
|
|
// Get provide class info
|
|
CComPtr< IProvideClassInfo > pPCI;
|
|
HRESULT hRes = pUnk->QueryInterface( &pPCI );
|
|
if( FAILED( hRes ) )
|
|
// Does not support IProvideClassInfo, bad
|
|
return hRes;
|
|
|
|
CComPtr< ITypeInfo > pClassInfo;
|
|
hRes = pPCI->GetClassInfo( &pClassInfo );
|
|
|
|
if( FAILED( hRes ) )
|
|
// Failed to return any class info
|
|
return hRes;
|
|
|
|
// Iterate through the Impl types to find the default source interface
|
|
int nImplFlags;
|
|
for( UINT i = 0; SUCCEEDED( pClassInfo->GetImplTypeFlags() ); ++ i )
|
|
{
|
|
if( nImplFlags == ( IMPLTYPEFLAG_FSOURCE | IMPLTYPEFLAG_FDEFAULT ) )
|
|
{
|
|
// Found it - locate our type info
|
|
HREFTYPE href;
|
|
hRes = pClassInfo->GetRefTypeOfImplType( i, &href );
|
|
|
|
if( FAILED( hRes ) )
|
|
// Could not access the ref type (referenced typelib not available?)
|
|
return hRes;
|
|
|
|
return pClassInfo->GetRefTypeInfo( href, ppTI );
|
|
}
|
|
}
|
|
|
|
// Did not find a suitable interface
|
|
return E_FAIL;
|
|
}
|
|
|
|
HRESULT GetSourceIID( ITypeInfo *pTI, IID *piid )
|
|
{
|
|
TYPEATTR *pTA;
|
|
HRESULT hRes = pTI->GetTypeAttr( &pTA );
|
|
|
|
if( FAILED( hRes ) )
|
|
return hRes;
|
|
|
|
*piid = pTA->guid;
|
|
pTI->ReleaseTypeAttr( pTA );
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT DispEventAdvise( IUnknown *pUnk )
|
|
{
|
|
CComPtr< ITypeInfo > pSourceInfo;
|
|
HRESULT hRes = GetSourceInterfaceInfo( pUnk, &pSourceInfo );
|
|
if( FAILED( hRes ) )
|
|
return hRes;
|
|
|
|
IID iid;
|
|
hRes = GetSourceIID( pSourceInfo, &iid );
|
|
if( FAILED( hRes ) )
|
|
return hRes;
|
|
|
|
hRes = _base::DispEventAdvise( pUnk, iid );
|
|
if( SUCCEEDED( hRes ) )
|
|
m_pTI = pSourceInfo;
|
|
|
|
return hRes;
|
|
}
|
|
|
|
HRESULT DispEventUnadvise( IUnknown *pUnk )
|
|
{
|
|
IID iid;
|
|
HRESULT hRes = GetSourceIID( pSourceInfo, &iid );
|
|
_ASSERTE( SUCCEEDED( hRes ) );
|
|
|
|
_base::DispEventUnadvise( pUnk, iid );
|
|
m_pTI.Release();
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
// Functions copied from IDispEventImpl
|
|
STDMETHOD(GetTypeInfoCount)(UINT* pctinfo)
|
|
{
|
|
if( pctinfo == NULL )
|
|
return E_POINTER;
|
|
|
|
*pctinfo = 1;
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHOD(GetTypeInfo)(UINT itinfo, LCID lcid, ITypeInfo** pptinfo)
|
|
{
|
|
if( itinfo > 0 )
|
|
return E_INVALIDARG;
|
|
|
|
if( pptinfo == NULL )
|
|
return E_POINTER;
|
|
|
|
return m_pTI->QueryIterface( pptinfo );
|
|
}
|
|
|
|
STDMETHOD(GetIDsOfNames)(REFIID riid, LPOLESTR* rgszNames, UINT cNames,
|
|
LCID, DISPID* rgdispid)
|
|
{
|
|
if( !::InlineIsEqualGUID( riid, IID_NULL ) )
|
|
// By spec
|
|
return DISP_E_UNKNOWNINTERFACE;
|
|
|
|
return m_pTI->GetIDsOfNames( rgszNames, cNames, rgdispid );
|
|
}
|
|
|
|
// Helper for finding the function index for a DISPID
|
|
HRESULT GetFuncInfoFromId(const IID& /*iid*/, DISPID dispidMember, LCID lcid, _ATL_FUNC_INFO& info)
|
|
{
|
|
FUNCDESC* pFuncDesc = NULL;
|
|
UINT nIndex;
|
|
HRESULT hr = m_pTI->GetFuncIndexOfMemId(dispidMember, INVOKE_FUNC, &nIndex);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
hr = m_pTI->GetFuncDesc(nIndex, &pFuncDesc);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
// If this assert occurs, then add a #define _ATL_MAX_VARTYPES nnnn
|
|
// before including atlcom.h
|
|
ATLASSERT(pFuncDesc->cParams <= _ATL_MAX_VARTYPES);
|
|
if (pFuncDesc->cParams > _ATL_MAX_VARTYPES)
|
|
return E_FAIL;
|
|
|
|
for (int i=0; i<pFuncDesc->cParams; i++)
|
|
{
|
|
info.pVarTypes[i] = pFuncDesc->lprgelemdescParam[pFuncDesc->cParams - i - 1].tdesc.vt;
|
|
if (info.pVarTypes[i] == VT_PTR)
|
|
info.pVarTypes[i] = pFuncDesc->lprgelemdescParam[pFuncDesc->cParams - i - 1].tdesc.lptdesc->vt | VT_BYREF;
|
|
if (info.pVarTypes[i] == VT_USERDEFINED)
|
|
info.pVarTypes[i] = GetUserDefinedType(pFuncDesc->lprgelemdescParam[pFuncDesc->cParams-i-1].tdesc.hreftype);
|
|
}
|
|
|
|
VARTYPE vtReturn = pFuncDesc->elemdescFunc.tdesc.vt;
|
|
switch(vtReturn)
|
|
{
|
|
case VT_INT:
|
|
vtReturn = VT_I4;
|
|
break;
|
|
case VT_UINT:
|
|
vtReturn = VT_UI4;
|
|
break;
|
|
case VT_VOID:
|
|
vtReturn = VT_EMPTY; // this is how DispCallFunc() represents void
|
|
break;
|
|
case VT_HRESULT:
|
|
vtReturn = VT_ERROR;
|
|
break;
|
|
}
|
|
info.vtReturn = vtReturn;
|
|
info.cc = pFuncDesc->callconv;
|
|
info.nParams = pFuncDesc->cParams;
|
|
m_pTI->ReleaseFuncDesc(pFuncDesc);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
VARTYPE GetUserDefinedType(HREFTYPE hrt)
|
|
{
|
|
CComPtr<ITypeInfo> spTypeInfo;
|
|
VARTYPE vt = VT_USERDEFINED;
|
|
HRESULT hr = E_FAIL;
|
|
hr = m_pTI->GetRefTypeInfo(hrt, &spTypeInfo);
|
|
|
|
if(FAILED(hr))
|
|
return vt;
|
|
|
|
TYPEATTR *pta=NULL;
|
|
|
|
m_pTI->GetTypeAttr(&pta);
|
|
|
|
if(pta && pta->typekind == TKIND_ALIAS)
|
|
{
|
|
if (pta->tdescAlias.vt == VT_USERDEFINED)
|
|
GetUserDefinedType(spTypeInfo,pta->tdescAlias.hreftype);
|
|
else
|
|
vt = pta->tdescAlias.vt;
|
|
}
|
|
|
|
if(pta)
|
|
spTypeInfo->ReleaseTypeAttr(pta);
|
|
|
|
return vt;
|
|
}
|
|
};
|
|
|
|
#endif
|