//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// This source code is licensed under Microsoft Shared Source License
// Version 1.0 for Windows CE.
// For a copy of the license visit http://go.microsoft.com/fwlink/?LinkId=3223.
//
//=--------------------------------------------------------------------------=
// ClassFactory.Cpp
//=--------------------------------------------------------------------------=
//
// contains the implementation of the ClassFactory object. we support 
// IClassFactory and IClassFactory2
//
#include "IPServer.H"
#include "LocalSrv.H"

#include "ClassF.H"
#include "Globals.H"
#include "Unknown.H"                    // for CREATEFNOFOBJECT

//=--------------------------------------------------------------------------=
// private module level data
//=--------------------------------------------------------------------------=
//

// ASSERT and FAIL require this
//
SZTHISFILE

// private routines for this file
//
HRESULT   CreateOleObjectFromIndex(IUnknown *, int Index, void **, REFIID);

//=--------------------------------------------------------------------------=
// CClassFactory::CClassFactory
//=--------------------------------------------------------------------------=
// create the object and initialize the refcount
//
// Parameters:
//    int            - [in] index into our global table of objects for this guy
//
// Notes:
//
CClassFactory::CClassFactory
(
    int iIndex
)
: m_iIndex(iIndex)
{
    m_cRefs = 1;
}


//=--------------------------------------------------------------------------=
// CClassFactory::CClassFactory
//=--------------------------------------------------------------------------=
// "Life levels all men.  Death reveals the eminent."
// - George Bernard Shaw (1856 - 1950)
//
// Notes:
//
CClassFactory::~CClassFactory ()
{
    ASSERT(m_cRefs == 0, L"Object being deleted with refs!");
    return;
}

//=--------------------------------------------------------------------------=
// CClassFactory::QueryInterface
//=--------------------------------------------------------------------------=
// the user wants another interface.  we won't give 'em. very many.
//
// Parameters:
//    REFIID        - [in]  interface they want
//    void **       - [out] where they want to put the resulting object ptr.
//
// Output:
//    HRESULT       - S_OK, E_NOINTERFACE
//
// Notes:
//
STDMETHODIMP CClassFactory::QueryInterface
(
    REFIID riid,
    void **ppvObjOut
)
{
    void *pv;

    CHECK_POINTER(ppvObjOut);

    // we support IUnknown, and the two CF interfaces
    //
    if (DO_GUIDS_MATCH(riid, IID_IClassFactory)) {
        pv = (void *)(IClassFactory *)this;
    } else if (DO_GUIDS_MATCH(riid, IID_IClassFactory2)) {
        pv = (void *)(IClassFactory2 *)this;
    } else if (DO_GUIDS_MATCH(riid, IID_IUnknown)) {
        pv = (void *)(IUnknown *)this;
    } else {
        *ppvObjOut = NULL;
        return E_NOINTERFACE;
    }

    ((IUnknown *)pv)->AddRef();
    *ppvObjOut = pv;
    return S_OK;
}




//=--------------------------------------------------------------------------=
// CClassFactory::AddRef
//=--------------------------------------------------------------------------=
// adds a tick to the current reference count.
//
// Output:
//    ULONG        - the new reference count
//
// Notes:
//
ULONG CClassFactory::AddRef
(
    void
)
{
    return ++m_cRefs;
}

//=--------------------------------------------------------------------------=
// CClassFactory::Release
//=--------------------------------------------------------------------------=
// removes a tick from the count, and delets the object if necessary
//
// Output:
//    ULONG         - remaining refs
//
// Notes:
//
ULONG CClassFactory::Release
(
    void
)
{
    ASSERT(m_cRefs, L"No Refs, and we're being released!");
    if(--m_cRefs)
        return m_cRefs;

    delete this;
    return 0;
}

//=--------------------------------------------------------------------------=
// CClassFactory::CreateInstance
//=--------------------------------------------------------------------------=
// create an instance of some sort of object.
//
// Parameters:
//    IUnknown *        - [in]  controlling IUknonwn for aggregation
//    REFIID            - [in]  interface id for new object
//    void **           - [out] pointer to new interface object.
//
// Output:
//    HRESULT           - S_OK, E_NOINTERFACE, E_UNEXPECTED,
//                        E_OUTOFMEMORY, E_INVALIDARG
//
// Notes:
//
STDMETHODIMP CClassFactory::CreateInstance
(
    IUnknown *pUnkOuter,
    REFIID    riid,
    void    **ppvObjOut
)
{
    // check args
    //
    if (!ppvObjOut)
        return E_INVALIDARG;

    // check to see if we've done our licensing work.  we do this as late
    // as possible that people calling CreateInstanceLic don't suffer from
    // a performance hit here.
    //
    // crit sect this for apartment threading, since it's global
    //
    EnterCriticalSection(&g_CriticalSection);
    if (!g_fCheckedForLicense) {
        g_fMachineHasLicense = CheckForLicense();
        g_fCheckedForLicense = TRUE;
    }
    LeaveCriticalSection(&g_CriticalSection);

    // check to see if they have the appropriate license to create this stuff
    //
    if (!g_fMachineHasLicense)
        return CLASS_E_NOTLICENSED;

    // try to create one of the objects that we support
    //
    return CreateOleObjectFromIndex(pUnkOuter, m_iIndex, ppvObjOut, riid);
}

//=--------------------------------------------------------------------------=
// CClassFactory::LockServer
//=--------------------------------------------------------------------------=
// lock the server so we can't unload
//
// Parameters:
//    BOOL        - [in] TRUE means addref, false means release lock count.
//
// Output:
//    HRESULT     - S_OK, E_FAIL, E_OUTOFMEMORY, E_UNEXPECTED
//
// Notes:
//
STDMETHODIMP CClassFactory::LockServer
(
    BOOL fLock
)
{
    // update the lock count.  crit sect these in case of another thread.
    //
    if (fLock)  
        InterlockedIncrement(&g_cLocks);
    else {
        ASSERT(g_cLocks, L"Lock Counting Problem");
        InterlockedDecrement(&g_cLocks);
    }

    return S_OK;
}

//=--------------------------------------------------------------------------=
// CClassFactory::GetLicInfo
//=--------------------------------------------------------------------------=
// IClassFactory2 GetLicInfo
//
// Parameters:
//    LICINFO *          - unclear
//
// Output:
//    HRESULT            - unclear
//
// Notes:
//
STDMETHODIMP CClassFactory::GetLicInfo
(
    LICINFO *pLicInfo
)
{
    CHECK_POINTER(pLicInfo);

    pLicInfo->cbLicInfo = sizeof(LICINFO);

    // This says whether RequestLicKey will work
    //
    pLicInfo->fRuntimeKeyAvail = g_fMachineHasLicense;

    // This says whether the standard CreateInstance will work
    //
    pLicInfo->fLicVerified = g_fMachineHasLicense;

    return S_OK;
}


//=--------------------------------------------------------------------------=
// CClassFactory::RequestLicKey
//=--------------------------------------------------------------------------=
// IClassFactory2 RequestLicKey
//
// Parameters:
//    DWORD             - [in]  reserved
//    BSTR *            - [out] unclear
//
// Output:
//    HRESULT           - unclear
//
// Notes:
//
STDMETHODIMP CClassFactory::RequestLicKey
(
    DWORD  dwReserved,
    BSTR  *pbstr
)
{
    // crit sect this for apartment threading, since it's global
    //
    EnterCriticalSection(&g_CriticalSection);
    if (!g_fCheckedForLicense) {
        g_fMachineHasLicense = CheckForLicense();
        g_fCheckedForLicense = TRUE;
    }
    LeaveCriticalSection(&g_CriticalSection);

    // if the machine isn't licensed, then we're not about to give this to them !
    //
    if (!g_fMachineHasLicense)
        return CLASS_E_NOTLICENSED;

    *pbstr = GetLicenseKey();
    return (*pbstr) ? S_OK : E_OUTOFMEMORY;
}


//=--------------------------------------------------------------------------=
// CClassFactory::CreateInstanceLic
//=--------------------------------------------------------------------------=
// create a new instance given a licensing key, etc ...
//
// Parameters:
//    IUnknown *        - [in]  controlling IUnknown for aggregation
//    IUnknown *        - [in]  reserved, must be NULL
//    REFIID            - [in]  IID We're looking for.
//    BSTR              - [in]  license key
//    void **           - [out] where to put the new object.
//
// Output:
//    HRESULT           - unclear
//
// Notes:
//
STDMETHODIMP CClassFactory::CreateInstanceLic
(
    IUnknown *pUnkOuter,
    IUnknown *pUnkReserved,
    REFIID    riid,
    BSTR      bstrKey,
    void    **ppvObjOut
)
{
    *ppvObjOut = NULL;

    // crit sect this for apartment threading, since it's global
    //
    EnterCriticalSection(&g_CriticalSection);
    if (!g_fCheckedForLicense) {
        g_fMachineHasLicense = CheckForLicense();
        g_fCheckedForLicense = TRUE;
    }
    LeaveCriticalSection(&g_CriticalSection);

    // go and see if the key they gave us matches.
    //
    if (!CheckLicenseKey(bstrKey))
        return CLASS_E_NOTLICENSED;

    // if it does, then go and create the object.
    //
    return CreateOleObjectFromIndex(pUnkOuter, m_iIndex, ppvObjOut, riid);
}

//=--------------------------------------------------------------------------=
// CreateOleObjectFromIndex
//=--------------------------------------------------------------------------=
// given an index in our object table, create an object from it.
//
// Parameters:
//    IUnknown *       - [in]  Controlling Unknown, if any, for aggregation
//    int              - [in]  index into our global table
//    void **          - [out] where to put resulting object.
//    REFIID           - [in]  the interface they want resulting object to be.
//
// Output:
//    HRESULT          - S_OK, E_OUTOFMEMORY, E_NOINTERFACE
//
// Notes:
//
HRESULT CreateOleObjectFromIndex
(
    IUnknown *pUnkOuter,
    int       iIndex,
    void    **ppvObjOut,
    REFIID    riid
)
{
    IUnknown *pUnk = NULL;
    HRESULT   hr;

    // go and create the object
    //
    ASSERT(CREATEFNOFOBJECT(iIndex), L"All creatable objects must have creation fn!");
    pUnk = CREATEFNOFOBJECT(iIndex)(pUnkOuter);

    // sanity check and make sure the object actually got allocated.
    //
    RETURN_ON_NULLALLOC(pUnk);

    // make sure we support aggregation here properly -- if they gave us
    // a controlling unknown, then they -must- ask for IUnknown, and we'll
    // give them the private unknown the object gave us.
    //
    if (pUnkOuter) {
        if (!DO_GUIDS_MATCH(riid, IID_IUnknown)) {
            pUnk->Release();
            return E_INVALIDARG;
        }

        *ppvObjOut = (void *)pUnk;
        hr = S_OK;
    } else {

        // QI for whatever the user wants.
        //
        hr = pUnk->QueryInterface(riid, ppvObjOut);
        pUnk->Release();
        RETURN_ON_FAILURE(hr);
    }

    return hr;
}

