openDecal/Native/Decal/DecalRes.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

337 lines
8.6 KiB
C++

// DecalRes.h : Declaration of the cDecalRes
#ifndef __DECALRES_H_
#define __DECALRES_H_
#include "resource.h" // main symbols
/////////////////////////////////////////////////////////////////////////////
// cDecalRes
class ATL_NO_VTABLE cDecalRes :
public CComObjectRootEx<CComMultiThreadModel>,
public CComCoClass<cDecalRes, &CLSID_DecalRes>,
public IDispatchImpl<IDecalRes, &IID_IDecalRes, &LIBID_Decal>,
public IConnectionPoint,
public IConnectionPointContainer,
public IProvideClassInfo2
{
public:
cDecalRes()
: m_dwNextCookie( 1 )
{
}
// This object iterates over our one connection
// point.
class cEnumRes
: public CComObjectRootEx<CComMultiThreadModel>,
public IEnumConnectionPoints
{
public:
BEGIN_COM_MAP(cEnumRes)
COM_INTERFACE_ENTRY(IEnumConnectionPoints)
END_COM_MAP()
bool m_bEnd;
CComPtr< IConnectionPoint > m_pCP;
void init( IConnectionPoint *pCP )
{
m_pCP = pCP;
m_bEnd = false;
}
public:
// IEnumConnectionPoints
STDMETHOD(Next)( ULONG cConnections, IConnectionPoint **rgpcn, ULONG *pcFetched )
{
if( m_bEnd )
{
*pcFetched = 0;
return S_FALSE;
}
if( cConnections == 0 )
{
*pcFetched = 0;
return S_OK;
}
m_bEnd = true;
*pcFetched = 1;
return m_pCP->QueryInterface( rgpcn );
}
STDMETHOD(Skip)( ULONG cConnections )
{
if( cConnections == 0 )
return S_OK;
m_bEnd = true;
return S_OK;
}
STDMETHOD(Reset)()
{
m_bEnd = false;
return S_OK;
}
STDMETHOD(Clone)(IEnumConnectionPoints **ppNew )
{
CComObject< cEnumRes > *pEnum;
CComObject< cEnumRes >::CreateInstance( &pEnum );
pEnum->m_pCP = m_pCP;
pEnum->m_bEnd = m_bEnd;
return pEnum->QueryInterface( IID_IEnumConnectionPoints, reinterpret_cast< void ** >( ppNew ) );
}
};
// This object implements the event object - Fire resolves
// to dispatching an event to our parent. This object is added
// into the namespace of the resource.
class cResEvent
: public CComObjectRootEx<CComMultiThreadModel>,
public IDispatch
{
public:
BEGIN_COM_MAP(cResEvent)
COM_INTERFACE_ENTRY(IDispatch)
END_COM_MAP()
CComPtr< ITypeInfo > m_pItf;
cDecalRes *m_pRes;
DISPID m_dispid;
HRESULT load( MSXML::IXMLDOMElementPtr &pElement )
{
// TODO: load the dispid and Fire paramters and create the temporary typeinfo
}
public:
// IDispatch
STDMETHOD(GetTypeInfoCount)( UINT *pctinfo )
{
if( pctinfo == NULL )
return E_POINTER;
*pctinfo = 1;
return S_OK;
}
STDMETHOD(GetTypeInfo)(UINT iTInfo, LCID, ITypeInfo **ppTI )
{
if( iTInfo != 0 )
return E_INVALIDARG;
if( ppTI == NULL )
return E_POINTER;
return m_pItf->QueryInterface( ppTI );
}
STDMETHOD(GetIDsOfNames)(REFIID riid, OLECHAR **rgszNames, UINT cNames, LCID, DISPID *rgDispID)
{
if( rgDispID == NULL || rgszNames == NULL )
// By spec
return E_POINTER;
if( !::InlineIsEqualGUID( riid, IID_NULL ) )
// By spec
return DISP_E_UNKNOWNINTERFACE;
static LPOLESTR _szFire = OLESTR( "Fire" );
for( UINT i = 0; i < cNames; ++ i )
{
if( ::wcscmp( _szFire, rgszNames[ i ] ) == 0 )
rgDispID[ i ] = 1;
else
rgDispID[ i ] = -1;
}
return S_OK;
}
STDMETHOD(Invoke)(DISPID dispIdMember, REFIID riid, LCID, WORD wFlags, DISPPARAMS *pDispParams,
VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *pArgError )
{
// Do all the checks in here
if( dispIdMember != 1 )
return DISP_E_MEMBERNOTFOUND;
if( !::InlineIsEqualGUID( riid, IID_NULL ) )
return DISP_E_UNKNOWNINTERFACE;
if( wFlags != DISPATCH_METHOD )
return DISP_E_MEMBERNOTFOUND;
return m_pRes->dispatchEvent( m_dispid, pDispParams, pVarResult, pExcepInfo, pArgError );
}
};
// Generated TypeInfo
CComPtr< ICreateTypeLib2 > m_pLib;
CComPtr< ITypeInfo > m_pCoClass;
CComPtr< ICreateTypeInfo > m_pSource;
HRESULT GetSourceIID( IID *pIID )
{
if( !m_pSource.p )
// Not yet initialized - bad
return E_FAIL;
CComPtr< ITypeInfo > pTI;
HRESULT hRes = m_pSource->QueryInterface( &pTI );
if( FAILED( hRes ) )
return hRes;
TYPEATTR *pTA;
pTI->GetTypeAttr( &pTA );
*pIID = pTA->guid;
pTI->ReleaseTypeAttr( pTA );
return S_OK;
}
// Connection point members
typedef std::deque< std::pair< DWORD, CComPtr< IDispatch > > > cCPList;
DWORD m_dwNextCookie;
cCPList m_cp;
HRESULT dispatchEvent( DISPID nID, DISPPARAMS *pDispParams, VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr )
{
// We simply pass the invoke on, the event object calling us
// will correctly remap the ID.
for( cCPList::iterator i = m_cp.begin(); i != m_cp.end(); ++ i )
i->second->Invoke( nID, IID_NULL, 0, DISPATCH_METHOD, pDispParams, pVarResult, pExcepInfo, puArgErr );
return S_OK;
}
// Series of methods to create the type library
HRESULT getLibID( MSXML::IXMLDOMDocumentPtr &pdoc, GUID *pGUID );
bool getTypelibFilename( MSXML::IXMLDOMDocumentPtr &pdoc, BSTR *pbstrFilename );
HRESULT initTypelib( MSXML::IXMLDOMDocumentPtr &pdoc, BSTR strFilename );
HRESULT scanTemplate( MSXML::IXMLDOMElementPtr &ptemp, ITypeInfo **ppCoClass, ITypeInfo **ppSource );
DECLARE_REGISTRY_RESOURCEID(IDR_DECALRES)
DECLARE_PROTECT_FINAL_CONSTRUCT()
BEGIN_COM_MAP(cDecalRes)
COM_INTERFACE_ENTRY(IDecalRes)
COM_INTERFACE_ENTRY(IDispatch)
COM_INTERFACE_ENTRY(IConnectionPoint)
COM_INTERFACE_ENTRY(IConnectionPointContainer)
COM_INTERFACE_ENTRY(IProvideClassInfo)
COM_INTERFACE_ENTRY(IProvideClassInfo2)
END_COM_MAP()
// IDecalRes
public:
// IConnectionPointContainer
STDMETHOD(EnumConnectionPoints)( IEnumConnectionPoints **ppEnum )
{
CComObject< cEnumRes > *pEnum;
CComObject< cEnumRes >::CreateInstance( &pEnum );
pEnum->init( this );
return pEnum->QueryInterface( IID_IEnumConnectionPoints, reinterpret_cast< void ** >( ppEnum ) );
}
STDMETHOD(FindConnectionPoint)( REFIID iid, IConnectionPoint **ppCP )
{
if( ppCP == NULL )
return E_POINTER;
IID iidSource;
HRESULT hRes = GetSourceIID( &iidSource );
if( FAILED( hRes ) )
return hRes;
if( ::InlineIsEqualGUID( iid, GUID_NULL ) ||
::InlineIsEqualGUID( iid, iidSource ) )
{
// Return the connection point
return static_cast< IConnectionPoint * >( this )->QueryInterface( ppCP );
}
// Not found (duh, it's not the only connection)
return CONNECT_E_NOCONNECTION;
}
// IConnectionPoint
STDMETHOD(GetConnectionInterface)( IID *piid )
{
if( piid == NULL )
return E_POINTER;
return GetSourceIID( piid );
}
STDMETHOD(GetConnectionPointContainer)( IConnectionPointContainer **ppCPC )
{
return static_cast< IConnectionPoint * >( this )->QueryInterface( ppCPC );
}
STDMETHOD(Advise)(IUnknown *pUnk, DWORD *pdwCookie)
{
if( pdwCookie == NULL || pUnk == NULL )
return E_POINTER;
CComPtr< IDispatch > pDisp;
HRESULT hRes = pUnk->QueryInterface( &pDisp );
if( FAILED( hRes ) )
return hRes;
m_cp.push_back( cCPList::value_type( m_dwNextCookie, pDisp ) );
*pdwCookie = ( m_dwNextCookie ++ );
return S_OK;
}
STDMETHOD(Unadvise)( DWORD dwCookie )
{
// Find the matching cookie
for( cCPList::iterator i = m_cp.begin(); i != m_cp.end(); ++ i )
{
if( i->first == dwCookie )
{
m_cp.erase( i );
return S_OK;
}
}
return CONNECT_E_NOCONNECTION;
}
STDMETHOD(EnumConnections)(IEnumConnections **)
{
// We don't support enum'ing connections
return E_NOTIMPL;
}
// IProvideClassInfo
STDMETHOD(GetClassInfo)(ITypeInfo **ppTI)
{
return m_pCoClass->QueryInterface( ppTI );
}
// IProvideClassInfo2
STDMETHOD(GetGUID)(DWORD dwGuidKind, GUID *pGUID)
{
if( dwGuidKind != GUIDKIND_DEFAULT_SOURCE_DISP_IID )
return E_INVALIDARG;
if( pGUID == NULL )
return E_POINTER;
return GetSourceIID( pGUID );
}
};
#endif //__DECALRES_H_