//------------------------------------------------------------------------------
// File: DllSetup.cpp
//
// Desc: DirectShow base classes.
//
// Copyright (c) 1992-2001 Microsoft Corporation.  All rights reserved.
//------------------------------------------------------------------------------


#include <streams.h>

//---------------------------------------------------------------------------
// defines

#define MAX_KEY_LEN  260


//---------------------------------------------------------------------------
// externally defined functions/variable

extern int g_cTemplates;
extern CFactoryTemplate g_Templates[];

//---------------------------------------------------------------------------
//
// EliminateSubKey
//
// Try to enumerate all keys under this one.
// if we find anything, delete it completely.
// Otherwise just delete it.
//
// note - this was pinched/duplicated from
// Filgraph\Mapper.cpp - so should it be in
// a lib somewhere?
//
//---------------------------------------------------------------------------

STDAPI
EliminateSubKey( HKEY hkey, LPTSTR strSubKey ) {
    HKEY hk;
    if(0 == lstrlen(strSubKey)) {
        // defensive approach
        return E_FAIL;
    }

    LONG lreturn = RegOpenKeyEx(hkey
        , strSubKey
        , 0
        , MAXIMUM_ALLOWED
        , &hk);

    ASSERT(lreturn == ERROR_SUCCESS
        || lreturn == ERROR_FILE_NOT_FOUND
        || lreturn == ERROR_INVALID_HANDLE);

    if(ERROR_SUCCESS == lreturn) {
        // Keep on enumerating the first (zero-th)
        // key and deleting that

        for(; ;) {
            TCHAR Buffer[MAX_KEY_LEN];
            DWORD dw = MAX_KEY_LEN;
            FILETIME ft;

            lreturn = RegEnumKeyEx(hk
                , 0
                , Buffer
                , &dw
                , NULL
                , NULL
                , NULL
                , &ft);

            ASSERT(lreturn == ERROR_SUCCESS
                || lreturn == ERROR_NO_MORE_ITEMS);

            if(ERROR_SUCCESS == lreturn) {
                EliminateSubKey(hk, Buffer);
            }
            else {
                break;
            }
        }

        RegCloseKey(hk);
        RegDeleteKey(hkey, strSubKey);
    }

    return NOERROR;
}


//---------------------------------------------------------------------------
//
// AMovieSetupRegisterServer()
//
// registers specfied file "szFileName" as server for
// CLSID "clsServer".  A description is also required.
// The ThreadingModel and ServerType are optional, as
// they default to InprocServer32 (i.e. dll) and Both.
//
//---------------------------------------------------------------------------

STDAPI
AMovieSetupRegisterServer( CLSID   clsServer
                         , LPCWSTR szDescription
                         , LPCWSTR szFileName
                         , LPCWSTR szThreadingModel = L"Both"
                         , LPCWSTR szServerType     = L"InprocServer32" ) {
    // temp buffer
    //
    TCHAR achTemp[MAX_PATH];

    // convert CLSID uuid to string and write
    // out subkey as string - CLSID\{}
    //
    OLECHAR szCLSID[CHARS_IN_GUID];
    HRESULT hr = StringFromGUID2(clsServer
        , szCLSID
        , CHARS_IN_GUID);
    ASSERT(SUCCEEDED(hr));

    // create key
    //
    HKEY hkey;
    wsprintf(achTemp, TEXT("CLSID\\%ls"), szCLSID);
    LONG lreturn = RegCreateKey(HKEY_CLASSES_ROOT
        , (LPCTSTR)achTemp
        , &hkey);
    if(ERROR_SUCCESS != lreturn) {
        return AmHresultFromWin32(lreturn);
    }

    // set description string
    //

    wsprintf(achTemp, TEXT("%ls"), szDescription);
    lreturn = RegSetValue(hkey
        , (LPCTSTR)NULL
        , REG_SZ
        , achTemp
        , sizeof(achTemp)/sizeof(achTemp[0]));
    if(ERROR_SUCCESS != lreturn) {
        RegCloseKey(hkey);
        return AmHresultFromWin32(lreturn);
    }

    // create CLSID\\{"CLSID"}\\"ServerType" key,
    // using key to CLSID\\{"CLSID"} passed back by
    // last call to RegCreateKey().
    //
    HKEY hsubkey;

    wsprintf(achTemp, TEXT("%ls"), szServerType);
    lreturn = RegCreateKey(hkey
        , achTemp
        , &hsubkey);
    if(ERROR_SUCCESS != lreturn) {
        RegCloseKey(hkey);
        return AmHresultFromWin32(lreturn);
    }

    // set Server string
    //
    wsprintf(achTemp, TEXT("%ls"), szFileName);
    lreturn = RegSetValue(hsubkey
        , (LPCTSTR)NULL
        , REG_SZ
        , (LPCTSTR)achTemp
        , sizeof(TCHAR) * (lstrlen(achTemp)+1));
    if(ERROR_SUCCESS != lreturn) {
        RegCloseKey(hkey);
        RegCloseKey(hsubkey);
        return AmHresultFromWin32(lreturn);
    }

    wsprintf(achTemp, TEXT("%ls"), szThreadingModel);
    lreturn = RegSetValueEx(hsubkey
        , TEXT("ThreadingModel")
        , 0L
        , REG_SZ
        , (CONST BYTE *)achTemp
        , sizeof(TCHAR) * (lstrlen(achTemp)+1));

    // close hkeys
    //
    RegCloseKey(hkey);
    RegCloseKey(hsubkey);

    // and return
    //
    return HRESULT_FROM_WIN32(lreturn);

}


//---------------------------------------------------------------------------
//
// AMovieSetupUnregisterServer()
//
// default ActiveMovie dll setup function
// - to use must be called from an exported
//   function named DllRegisterServer()
//
//---------------------------------------------------------------------------

STDAPI
AMovieSetupUnregisterServer( CLSID clsServer ) {
    // convert CLSID uuid to string and write
    // out subkey CLSID\{}
    //
    OLECHAR szCLSID[CHARS_IN_GUID];
    HRESULT hr = StringFromGUID2(clsServer
        , szCLSID
        , CHARS_IN_GUID);
    ASSERT(SUCCEEDED(hr));

    TCHAR achBuffer[MAX_KEY_LEN];
    wsprintf(achBuffer, TEXT("CLSID\\%ls"), szCLSID);

    // delete subkey
    //

    hr = EliminateSubKey(HKEY_CLASSES_ROOT, achBuffer);
    ASSERT(SUCCEEDED(hr));

    // return
    //
    return NOERROR;
}


//---------------------------------------------------------------------------
//
// AMovieSetupRegisterFilter through IFilterMapper2
//
//---------------------------------------------------------------------------

STDAPI
AMovieSetupRegisterFilter2( const AMOVIESETUP_FILTER * const psetupdata
                          , IFilterMapper2 *                 pIFM2
                          , BOOL                             bRegister  ) {
    DbgLog((LOG_TRACE, 3, TEXT("= AMovieSetupRegisterFilter")));

    // check we've got data
    //
    if(NULL == psetupdata) return S_FALSE;


    // unregister filter
    // (as pins are subkeys of filter's CLSID key
    // they do not need to be removed separately).
    //
    DbgLog((LOG_TRACE, 3, TEXT("= = unregister filter")));
    HRESULT hr = pIFM2->UnregisterFilter(0,                        // default category
        0,                        // default instance name
        *psetupdata->clsID);


    if(bRegister) {
        REGFILTER2 rf2;
        rf2.dwVersion = 1;
        rf2.dwMerit = psetupdata->dwMerit;
        rf2.cPins = psetupdata->nPins;
        rf2.rgPins = psetupdata->lpPin;

        // register filter
        //
        DbgLog((LOG_TRACE, 3, TEXT("= = register filter")));
        hr = pIFM2->RegisterFilter(*psetupdata->clsID
            , psetupdata->strName
            , 0 // moniker
            , 0 // category
            , NULL // instance
            , &rf2);
    }

    // handle one acceptable "error" - that
    // of filter not being registered!
    // (couldn't find a suitable #define'd
    // name for the error!)
    //
    if(0x80070002 == hr)
        return NOERROR;
    else
        return hr;
}


//---------------------------------------------------------------------------
//
// RegisterAllServers()
//
//---------------------------------------------------------------------------

STDAPI
RegisterAllServers( LPCWSTR szFileName, BOOL bRegister ) {
    HRESULT hr = NOERROR;

    for(int i = 0; i < g_cTemplates; i++) {
        // get i'th template
        //
        const CFactoryTemplate *pT = &g_Templates[i];

        DbgLog((LOG_TRACE, 2, TEXT("- - register %ls"),
            (LPCWSTR)pT->m_Name ));

        // register CLSID and InprocServer32
        //
        if(bRegister) {
            hr = AMovieSetupRegisterServer(*(pT->m_ClsID)
                , (LPCWSTR)pT->m_Name
                , szFileName);
        }
        else {
            hr = AMovieSetupUnregisterServer(*(pT->m_ClsID));
        }

        // check final error for this pass
        // and break loop if we failed
        //
        if(FAILED(hr))
            break;
    }

    return hr;
}


//---------------------------------------------------------------------------
//
// AMovieDllRegisterServer2()
//
// default ActiveMovie dll setup function
// - to use must be called from an exported
//   function named DllRegisterServer()
//
// this function is table driven using the
// static members of the CFactoryTemplate
// class defined in the dll.
//
// it registers the Dll as the InprocServer32
// and then calls the IAMovieSetup.Register
// method.
//
//---------------------------------------------------------------------------

STDAPI
AMovieDllRegisterServer2( BOOL bRegister ) {
    HRESULT hr = NOERROR;

    DbgLog((LOG_TRACE, 2, TEXT("AMovieDllRegisterServer2()")));

    // get file name (where g_hInst is the
    // instance handle of the filter dll)
    //
    WCHAR achFileName[MAX_PATH];

    // WIN95 doesn't support GetModuleFileNameW
    //
    {
        char achTemp[MAX_PATH];

        DbgLog((LOG_TRACE, 2, TEXT("- get module file name")));

        // g_hInst handle is set in our dll entry point. Make sure
        // DllEntryPoint in dllentry.cpp is called
        ASSERT(g_hInst != 0);

        if(0 == GetModuleFileNameA(g_hInst
            , achTemp
            , sizeof(achTemp))) {
            // we've failed!
            DWORD dwerr = GetLastError();
            return AmHresultFromWin32(dwerr);
        }

        MultiByteToWideChar(CP_ACP
            , 0L
            , achTemp
            , lstrlenA(achTemp) + 1
            , achFileName
            , sizeof(achFileName)/sizeof(TCHAR));
    }

    //
    // first registering, register all OLE servers
    //
    if(bRegister) {
        DbgLog((LOG_TRACE, 2, TEXT("- register OLE Servers")));
        hr = RegisterAllServers(achFileName, TRUE);
    }

    //
    // next, register/unregister all filters
    //

    if(SUCCEEDED(hr)) {
        // init is ref counted so call just in case
        // we're being called cold.
        //
        DbgLog((LOG_TRACE, 2, TEXT("- CoInitialize")));
        hr = CoInitialize((LPVOID)NULL);
        ASSERT(SUCCEEDED(hr));

        // get hold of IFilterMapper2
        //
        DbgLog((LOG_TRACE, 2, TEXT("- obtain IFilterMapper2")));
        IFilterMapper2 *pIFM2 = 0;
        IFilterMapper *pIFM = 0;
        hr = CoCreateInstance(CLSID_FilterMapper2
            , NULL
            , CLSCTX_INPROC_SERVER
            , IID_IFilterMapper2
            , (void **)&pIFM2);
        if(FAILED(hr)) {
            DbgLog((LOG_TRACE, 2, TEXT("- trying IFilterMapper instead")));

            hr = CoCreateInstance(CLSID_FilterMapper,
                NULL,
                CLSCTX_INPROC_SERVER,
                IID_IFilterMapper,
                (void **)&pIFM);
        }
        if(SUCCEEDED(hr)) {
            // scan through array of CFactoryTemplates
            // registering servers and filters.
            //
            DbgLog((LOG_TRACE, 2, TEXT("- register Filters")));
            for(int i = 0; i < g_cTemplates; i++) {
                // get i'th template
                //
                const CFactoryTemplate *pT = &g_Templates[i];

                if(NULL != pT->m_pAMovieSetup_Filter) {
                    DbgLog((LOG_TRACE, 2, TEXT("- - register %ls"), (LPCWSTR)pT->m_Name ));

                    if(pIFM2) {
                        hr = AMovieSetupRegisterFilter2(pT->m_pAMovieSetup_Filter, pIFM2, bRegister);
                    }
                    else {
                        hr = AMovieSetupRegisterFilter(pT->m_pAMovieSetup_Filter, pIFM, bRegister);
                    }
                }

                // check final error for this pass
                // and break loop if we failed
                //
                if(FAILED(hr))
                    break;
            }

            // release interface
            //
            if(pIFM2)
                pIFM2->Release();
            else
                pIFM->Release();

        }

        // and clear up
        //
        CoFreeUnusedLibraries();
        CoUninitialize();
    }

    //
    // if unregistering, unregister all OLE servers
    //
    if(SUCCEEDED(hr) && !bRegister) {
        DbgLog((LOG_TRACE, 2, TEXT("- register OLE Servers")));
        hr = RegisterAllServers(achFileName, FALSE);
    }

    DbgLog((LOG_TRACE, 2, TEXT("- return %0x"), hr));
    return hr;
}


//---------------------------------------------------------------------------
//
// AMovieDllRegisterServer()
//
// default ActiveMovie dll setup function
// - to use must be called from an exported
//   function named DllRegisterServer()
//
// this function is table driven using the
// static members of the CFactoryTemplate
// class defined in the dll.
//
// it registers the Dll as the InprocServer32
// and then calls the IAMovieSetup.Register
// method.
//
//---------------------------------------------------------------------------


STDAPI
AMovieDllRegisterServer( void ) {
    HRESULT hr = NOERROR;

    // get file name (where g_hInst is the
    // instance handle of the filter dll)
    //
    WCHAR achFileName[MAX_PATH];

    {
        // WIN95 doesn't support GetModuleFileNameW
        //
        char achTemp[MAX_PATH];

        if(0 == GetModuleFileNameA(g_hInst
            , achTemp
            , sizeof(achTemp))) {
            // we've failed!
            DWORD dwerr = GetLastError();
            return AmHresultFromWin32(dwerr);
        }

        MultiByteToWideChar(CP_ACP
            , 0L
            , achTemp
            , lstrlenA(achTemp) + 1
            , achFileName
            , sizeof(achFileName)/sizeof(TCHAR));
    }

    // scan through array of CFactoryTemplates
    // registering servers and filters.
    //
    for(int i = 0; i < g_cTemplates; i++) {
        // get i'th template
        //
        const CFactoryTemplate *pT = &g_Templates[i];

        // register CLSID and InprocServer32
        //
        hr = AMovieSetupRegisterServer(*(pT->m_ClsID)
            , (LPCWSTR)pT->m_Name
            , achFileName);

        // instantiate all servers and get hold of
        // IAMovieSetup, if implemented, and call
        // IAMovieSetup.Register() method
        //
        if(SUCCEEDED(hr) && (NULL != pT->m_lpfnNew)) {
            // instantiate object
            //
            PAMOVIESETUP psetup;
            hr = CoCreateInstance(*(pT->m_ClsID)
                , 0
                , CLSCTX_INPROC_SERVER
                , IID_IAMovieSetup
                , reinterpret_cast<void**>(&psetup));
            if(SUCCEEDED(hr)) {
                hr = psetup->Unregister();
                if(SUCCEEDED(hr))
                    hr = psetup->Register();
                psetup->Release();
            }
            else {
                if((E_NOINTERFACE == hr )
                    || (VFW_E_NEED_OWNER == hr ))
                    hr = NOERROR;
            }
        }

        // check final error for this pass
        // and break loop if we failed
        //
        if(FAILED(hr))
            break;

    } // end-for

    return hr;
}


//---------------------------------------------------------------------------
//
// AMovieDllUnregisterServer()
//
// default ActiveMovie dll uninstall function
// - to use must be called from an exported
//   function named DllRegisterServer()
//
// this function is table driven using the
// static members of the CFactoryTemplate
// class defined in the dll.
//
// it calls the IAMovieSetup.Unregister
// method and then unregisters the Dll
// as the InprocServer32
//
//---------------------------------------------------------------------------

STDAPI
AMovieDllUnregisterServer() {
    // initialize return code
    //
    HRESULT hr = NOERROR;

    // scan through CFactory template and unregister
    // all OLE servers and filters.
    //
    for(int i = g_cTemplates; i--;) {
        // get i'th template
        //
        const CFactoryTemplate *pT = &g_Templates[i];

        // check method exists
        //
        if(NULL != pT->m_lpfnNew) {
            // instantiate object
            //
            PAMOVIESETUP psetup;
            hr = CoCreateInstance(*(pT->m_ClsID)
                , 0
                , CLSCTX_INPROC_SERVER
                , IID_IAMovieSetup
                , reinterpret_cast<void**>(&psetup));
            if(SUCCEEDED(hr)) {
                hr = psetup->Unregister();
                psetup->Release();
            }
            else {
                if((E_NOINTERFACE      == hr )
                    || (VFW_E_NEED_OWNER == hr ))
                    hr = NOERROR;
            }
        }

        // unregister CLSID and InprocServer32
        //
        if(SUCCEEDED(hr)) {
            hr = AMovieSetupUnregisterServer(*(pT->m_ClsID));
        }

        // check final error for this pass
        // and break loop if we failed
        //
        if(FAILED(hr))
            break;
    }

    return hr;
}

