// 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;
}
