openDecal/Native/Include/DecalImpl.h
erik d1442e3747 Initial commit: Complete open-source Decal rebuild
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>
2026-02-08 18:27:56 +01:00

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