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

194 lines
6.1 KiB
C++

// ActiveXSurrogate.cpp : Implementation of cActiveXSurrogate
#include "stdafx.h"
#include "Decal.h"
#include "ActiveXSurrogate.h"
// Duplicated from Inject.h to prevent circular dependency
class IPluginSite;
class __declspec(uuid("{BA3E677F-8E44-4829-982E-58BBBC5C5F9B}")) IPlugin
: public IUnknown
{
public:
STDMETHOD(Initialize)(IPluginSite *pSite, long) = 0;
STDMETHOD(Terminate)() = 0;
STDMETHOD(get_FriendlyName)(BSTR *pstrName) = 0;
};
/////////////////////////////////////////////////////////////////////////////
// cActiveXSurrogate
typedef HRESULT (__stdcall *pfnDllRegisterServer)();
typedef HRESULT (__stdcall *pfnDllUnregisterServer)();
STDMETHODIMP cActiveXSurrogate::Register(BSTR strFilename)
{
USES_CONVERSION;
LPCTSTR szFilename = OLE2T( strFilename );
// First, register the library
HMODULE hLib = ::LoadLibrary( szFilename );
if( hLib == NULL )
return E_FAIL;
pfnDllRegisterServer pfnReg = reinterpret_cast< pfnDllRegisterServer >( ::GetProcAddress( hLib, _T( "DllRegisterServer" ) ) );
HRESULT hRes = E_FAIL;
if( pfnReg != NULL )
hRes = pfnReg();
::FreeLibrary( hLib );
if( FAILED( hRes ) )
return hRes;
// Next open up the type library and look for suitable classes, they have
// to be creatable - then we make one and see if it implements IPlugin2
CComPtr< ITypeLib > pTypeLib;
hRes = ::LoadTypeLib( strFilename, &pTypeLib );
if( FAILED( hRes ) )
return hRes;
UINT nTypeInfoCount = pTypeLib->GetTypeInfoCount();
int nFound = 0;
for( UINT i = 0; i < nTypeInfoCount; ++ i )
{
TYPEKIND tk;
pTypeLib->GetTypeInfoType( i, &tk );
if( tk != TKIND_COCLASS )
// Only coclasses
continue;
CComPtr< ITypeInfo > pClass1;
pTypeLib->GetTypeInfo( i, &pClass1 );
TYPEATTR *pta;
pClass1->GetTypeAttr( &pta );
if( pta->wTypeFlags & TYPEFLAG_FCANCREATE )
{
// Create an instance of the plugin and see if it implements IPlugin
CComPtr< IUnknown > pUnkPlugin;
HRESULT hRes = pClass1->CreateInstance ( NULL, IID_IUnknown, reinterpret_cast< LPVOID * > ( &pUnkPlugin ) );
if ( SUCCEEDED ( hRes ) )
{
// Check for the IPlugin2 interface
CComPtr< IPlugin2 > pPlugin2;
if ( SUCCEEDED ( pUnkPlugin->QueryInterface ( &pPlugin2 ) ) )
{
// This is a plugin (anything that implements IPlugin2 is a plugin afterall, make the registry key
LPCSTR szCLSID;
LPOLESTR strCLSID;
::StringFromCLSID( pta->guid, &strCLSID );
szCLSID = OLE2T( strCLSID );
::CoTaskMemFree( strCLSID );
TCHAR szRegKey[ 255 ];
::_tcscat ( ::_tcscpy ( szRegKey, _T( "Software\\Decal\\Plugins\\" ) ), szCLSID );
RegKey key;
key.Create( HKEY_LOCAL_MACHINE, szRegKey );
CComBSTR strDescription;
pClass1->GetDocumentation( MEMBERID_NIL, NULL, &strDescription, NULL, NULL );
key.SetStringValue( NULL, OLE2T( strDescription ) );
key.SetStringValue( _T("File"), szFilename );
key.SetDWORDValue( _T( "Enabled" ), 1 );
key.SetStringValue( _T("Uninstall"), _T( "{7559F22F-C56F-4621-AE08-9C354D799D4B}" ) );
++ nFound;
}
else
{
CComPtr< IPlugin > pPlugin;
if ( SUCCEEDED( pUnkPlugin->QueryInterface ( &pPlugin ) ) )
{
// This is a V1 plugin - same registration, but use a surrogate
LPCSTR szCLSID;
LPOLESTR strCLSID;
::StringFromCLSID( pta->guid, &strCLSID );
szCLSID = OLE2T( strCLSID );
::CoTaskMemFree( strCLSID );
TCHAR szRegKey[ 255 ];
::_tcscat ( ::_tcscpy ( szRegKey, _T( "Software\\Decal\\Plugins\\" ) ), szCLSID );
RegKey key;
key.Create( HKEY_LOCAL_MACHINE, szRegKey );
CComBSTR strDescription;
pPlugin->get_FriendlyName( &strDescription );
// Friendly name
key.SetStringValue( NULL, OLE2T( strDescription ) );
// File for uninstalling
key.SetStringValue( _T( "File" ), szFilename );
// Enabled by default
key.SetDWORDValue( _T( "Enabled" ), 1 );
// This object uninstalls
key.SetStringValue( _T("Uninstall"), _T( "{7559F22F-C56F-4621-AE08-9C354D799D4B}" ));
// The V1 surrogate
key.SetStringValue( _T( "Surrogate" ), _T( "{3D837F6E-B5CA-4604-885F-7AB45FCFA62A}" ) );
++ nFound;
}
}
}
}
pClass1->ReleaseTypeAttr( pta );
}
return ( nFound > 0 ) ? S_OK : E_FAIL;
}
STDMETHODIMP cActiveXSurrogate::Uninstall()
{
USES_CONVERSION;
TCHAR szParent[ 255 ];
::_tcscat ( ::_tcscpy ( szParent, _T( "Software\\Decal\\Plugins\\" ) ), OLE2T( m_strGroup ) );
RegKey rk;
if( rk.Open( HKEY_LOCAL_MACHINE, szParent ) != ERROR_SUCCESS )
{
_ASSERT( FALSE );
return E_FAIL;
}
LPOLESTR strCLSID;
::StringFromCLSID( m_clsid, &strCLSID );
LPTSTR szCLSID = OLE2T( strCLSID );
::CoTaskMemFree( strCLSID );
if( rk.RecurseDeleteKey( szCLSID ) != ERROR_SUCCESS )
{
_ASSERT( FALSE );
return E_FAIL;
}
// Load the library and call DLL unreg server
LPCTSTR szFilename = OLE2T( m_strFile );
// First, register the library
HMODULE hLib = ::LoadLibrary( szFilename );
if( hLib == NULL )
return E_FAIL;
pfnDllUnregisterServer pfnReg = reinterpret_cast< pfnDllUnregisterServer >( ::GetProcAddress( hLib, _T( "DllUnregisterServer" ) ) );
HRESULT hRes = E_FAIL;
if( pfnReg != NULL )
hRes = pfnReg();
::FreeLibrary( hLib );
// Just call this to make sure the library isn't locked so the user
// can delete it after this point.
::CoFreeUnusedLibraries();
return hRes;
}