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>
337 lines
8.6 KiB
C++
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_
|