/*************************************************************************
**
**    OLE 2 Sample Code
**
**    classfac.c
**
**    This file contains the implementation for IClassFactory for both the
**    server and the client version of the OUTLINE app.
**
**    (c) Copyright Microsoft Corp. 1992 - 1996 All Rights Reserved
**
*************************************************************************/

#include "outline.h"

OLEDBGDATA

extern LPOUTLINEAPP             g_lpApp;


/* NOTE: this object illustrates the manner in which to statically
**    (compile-time) initialize an interface VTBL.
*/
static IClassFactoryVtbl g_AppClassFactoryVtbl = {
        AppClassFactory_QueryInterface,
        AppClassFactory_AddRef,
        AppClassFactory_Release,
        AppClassFactory_CreateInstance,
        AppClassFactory_LockServer
};


/* AppClassFactory_Create
** ----------------------
**    create an instance of APPCLASSFACTORY.
**    NOTE: type of pointer returned is an IClassFactory* interface ptr.
**          the returned pointer can be directly passed to
**          CoRegisterClassObject and released later by calling the
**          Release method of the interface.
*/
LPCLASSFACTORY WINAPI AppClassFactory_Create(void)
{
        LPAPPCLASSFACTORY lpAppClassFactory;
        LPMALLOC lpMalloc;

        if (CoGetMalloc(MEMCTX_TASK, (LPMALLOC FAR*)&lpMalloc) != NOERROR)
                return NULL;

        lpAppClassFactory = (LPAPPCLASSFACTORY)lpMalloc->lpVtbl->Alloc(
                        lpMalloc, (sizeof(APPCLASSFACTORY)));
        lpMalloc->lpVtbl->Release(lpMalloc);
        if (! lpAppClassFactory) return NULL;

        lpAppClassFactory->m_lpVtbl = &g_AppClassFactoryVtbl;
        lpAppClassFactory->m_cRef   = 1;
#if defined( _DEBUG )
        lpAppClassFactory->m_cSvrLock = 0;
#endif
        return (LPCLASSFACTORY)lpAppClassFactory;
}


/*************************************************************************
** OleApp::IClassFactory interface implementation
*************************************************************************/

STDMETHODIMP AppClassFactory_QueryInterface(
                LPCLASSFACTORY lpThis, REFIID riid, LPVOID FAR* ppvObj)
{
        LPAPPCLASSFACTORY lpAppClassFactory = (LPAPPCLASSFACTORY)lpThis;
        SCODE scode;

        // Two interfaces supported: IUnknown, IClassFactory

        if (IsEqualIID(riid, &IID_IClassFactory) ||
                        IsEqualIID(riid, &IID_IUnknown)) {
                lpAppClassFactory->m_cRef++;   // A pointer to this object is returned
                *ppvObj = lpThis;
                scode = S_OK;
        }
        else {                 // unsupported interface
                *ppvObj = NULL;
                scode = E_NOINTERFACE;
        }

        return ResultFromScode(scode);
}


STDMETHODIMP_(ULONG) AppClassFactory_AddRef(LPCLASSFACTORY lpThis)
{
        LPAPPCLASSFACTORY lpAppClassFactory = (LPAPPCLASSFACTORY)lpThis;
        return ++lpAppClassFactory->m_cRef;
}

STDMETHODIMP_(ULONG) AppClassFactory_Release(LPCLASSFACTORY lpThis)
{
        LPAPPCLASSFACTORY lpAppClassFactory = (LPAPPCLASSFACTORY)lpThis;
        LPMALLOC lpMalloc;

        if (--lpAppClassFactory->m_cRef != 0) // Still used by others
                return lpAppClassFactory->m_cRef;

        // Free storage
        if (CoGetMalloc(MEMCTX_TASK, (LPMALLOC FAR*)&lpMalloc) != NOERROR)
                return 0;

        lpMalloc->lpVtbl->Free(lpMalloc, lpAppClassFactory);
        lpMalloc->lpVtbl->Release(lpMalloc);
        return 0;
}


STDMETHODIMP AppClassFactory_CreateInstance (
                LPCLASSFACTORY      lpThis,
                LPUNKNOWN           lpUnkOuter,
                REFIID              riid,
                LPVOID FAR*         lplpvObj
)
{
        LPOUTLINEAPP    lpOutlineApp = (LPOUTLINEAPP)g_lpApp;
        LPOLEDOC        lpOleDoc;
        HRESULT         hrErr;

        OLEDBG_BEGIN2("AppClassFactory_CreateInstance\r\n")

        /* NOTE: we must make sure to set all out parameters to NULL. */
        *lplpvObj = NULL;

        /*********************************************************************
        ** NOTE: this is an SDI app; it can only create and support one
        **    instance. After the instance is created, the OLE libraries
        **    should not call CreateInstance again. it is a good practise
        **    to specifically guard against this.
        *********************************************************************/

        if (lpOutlineApp->m_lpDoc != NULL)
                return ResultFromScode(E_UNEXPECTED);

        /* NOTE: create a new document instance. by the time we return
        **    from this method the document's refcnt must be 1.
        */
        lpOutlineApp->m_lpDoc = OutlineApp_CreateDoc(lpOutlineApp, FALSE);
        lpOleDoc = (LPOLEDOC)lpOutlineApp->m_lpDoc;
        if (! lpOleDoc) {
                OLEDBG_END2
                return ResultFromScode(E_OUTOFMEMORY);
        }

        /* NOTE: retrieve pointer to requested interface. the ref cnt
        **    of the object after OutlineApp_CreateDoc is 0. this call to
        **    QueryInterface will increment the refcnt to 1. the object
        **    returned from IClassFactory::CreateInstance should have a
        **    refcnt of 1 and be controlled by the caller. If the caller
        **    releases the document, the document should be destroyed.
        */
        hrErr = OleDoc_QueryInterface(lpOleDoc, riid, lplpvObj);

        OLEDBG_END2
        return hrErr;
}


STDMETHODIMP AppClassFactory_LockServer (
                LPCLASSFACTORY      lpThis,
                BOOL                fLock
)
{
        LPAPPCLASSFACTORY lpAppClassFactory = (LPAPPCLASSFACTORY)lpThis;
        LPOLEAPP lpOleApp = (LPOLEAPP)g_lpApp;
        HRESULT hrErr;
        OLEDBG_BEGIN2("AppClassFactory_LockServer\r\n")

#if defined( _DEBUG )
        if (fLock) {
                ++lpAppClassFactory->m_cSvrLock;
                OleDbgOutRefCnt3(
                                "AppClassFactory_LockServer: cLock++\r\n",
                                lpAppClassFactory, lpAppClassFactory->m_cSvrLock);
        } else {

                /* NOTE: when there are no open documents and the app is not
                **    under the control of the user and there are no outstanding
                **    locks on the app, then revoke our ClassFactory to enable the
                **    app to shut down.
                */
                --lpAppClassFactory->m_cSvrLock;
                OleDbgAssertSz (lpAppClassFactory->m_cSvrLock >= 0,
                                "AppClassFactory_LockServer(FALSE) called with cLock == 0"
                );

                if (lpAppClassFactory->m_cSvrLock == 0) {
                        OleDbgOutRefCnt2(
                                        "AppClassFactory_LockServer: UNLOCKED\r\n",
                                        lpAppClassFactory, lpAppClassFactory->m_cSvrLock);
                } else {
                        OleDbgOutRefCnt3(
                                        "AppClassFactory_LockServer: cLock--\r\n",
                                        lpAppClassFactory, lpAppClassFactory->m_cSvrLock);
                }
        }
#endif  // _DEBUG
        /* NOTE: in order to hold the application alive we call
        **    CoLockObjectExternal to add a strong reference to our app
        **    object. this will keep the app alive when all other external
        **    references release us. if the user issues File.Exit the
        **    application will shut down in any case ignoring any
        **    outstanding LockServer locks because CoDisconnectObject is
        **    called in OleApp_CloseAllDocsAndExitCommand. this will
        **    forceably break any existing strong reference counts
        **    including counts that we add ourselves by calling
        **    CoLockObjectExternal and guarantee that the App object gets
        **    its final release (ie. cRefs goes to 0).
        */
        hrErr = OleApp_Lock(lpOleApp, fLock, TRUE /* fLastUnlockReleases */);

        OLEDBG_END2
        return hrErr;
}
