//==========================================================================;
//
//  THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
//  KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
//  IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
//  PURPOSE.
//
//  Copyright (c) 1992 - 1998  Microsoft Corporation.  All Rights Reserved.
//
//--------------------------------------------------------------------------;

// Base class hierachy for creating COM objects, December 1994

/*

a. Derive your COM object from CComBase

b. Make a static CreateInstance function that takes an IUnknown* and an
   HRESULT*. The IUnknown* defines the object to delegate IUnknown calls
   to. The HRESULT * allows error codes to be passed around constructors.

   It is important that constructors only change the HRESULT * if they have
   to set an ERROR code, if it was successful then leave it alone or you may
   overwrite an error code from an object previously created.

c. Have a constructor for your object that passes the IUnknown* and HRESULT*
   to the CComBase constructor. You can set the HRESULT if you have an error,
   or just simply pass it through to the constructor.

   The object creation will fail in the class factory if the HRESULT indicates
   an error (ie FAILED(HRESULT) == TRUE)

d. Create a CComClassTemplate with your object's class id and CreateInstance
   function.

Then (for each interface) either

Multiple inheritance

1. Also derive it from ISomeInterface
2. Include DECLARE_IUNKNOWN in your class definition to declare
   implementations of QueryInterface, AddRef and Release that
   call the outer unknown
3. Override NDQueryInterface to expose ISomeInterface by
   code something like

     if (riid == IID_ISomeInterface) {
         return GetInterface((ISomeInterface *) this, ppv);
     } else {
         return CComBase::NDQueryInterface(riid, ppv);
     }

4. Declare and implement the member functions of ISomeInterface.

or: Nested interfaces

1. Declare a class derived from CComBase
2. Include DECLARE_IUNKNOWN in your class definition
3. Override NDQueryInterface to expose ISomeInterface by
   code something like

     if (riid == IID_ISomeInterface) {
         return GetInterface((ISomeInterface *) this, ppv);
     } else {
         return CComBase::NDQueryInterface(riid, ppv);
     }

4. Implement the member functions of ISomeInterface. Use \() to
   access the COM object class.

And in your COM object class:

5. Make the nested class a friend of the COM object class, and declare
   an instance of the nested class as a member of the COM object class.

   NOTE that because you must always pass the outer unknown and an hResult
   to the CComBase constructor you cannot use a default constructor, in
   other words you will have to make the member variable a pointer to the
   class and make a NEW call in your constructor to actually create it.

6. override the NDQueryInterface with code like this:

     if (riid == IID_ISomeInterface) {
         return m_pImplFilter->
            NDQueryInterface(IID_ISomeInterface, ppv);
     } else {
         return CComBase::NDQueryInterface(riid, ppv);
     }

You can have mixed classes which support some interfaces via multiple
inheritance and some via nested classes

*/

#ifndef __COMBASE__
#define __COMBASE__

#include <windows.h>
#include <basetyps.h>
#include <unknwn.h>

extern int g_cActiveObjects;

STDAPI CreateCLSIDRegKey(REFCLSID clsid, const char *szName);

STDAPI RemoveCLSIDRegKey(REFCLSID clsid);

#ifdef DEBUG
    // We chose a common name for our ASSERT macro, MFC also uses this name
    // So long as the implementation evaluates the condition and handles it
    // then we will be ok. Rather than override the behaviour expected we
    // will leave whatever first defines ASSERT as the handler (i.e. MFC)
    #ifndef ASSERT
        #define ASSERT(_x_) if (!(_x_))         \
            DebugAssert(TEXT(#_x_),TEXT(__FILE__),__LINE__)
    #endif
    #define EXECUTE_ASSERT(_x_) ASSERT(_x_)

    #define ValidateReadPtr(p,cb) \
        {if(IsBadReadPtr((PVOID)p,cb) == TRUE) \
            DebugBreak("Invalid read pointer");}

    #define ValidateWritePtr(p,cb) \
        {if(IsBadWritePtr((PVOID)p,cb) == TRUE) \
            DebugBreak("Invalid write pointer");}

    #define ValidateReadWritePtr(p,cb) \
        {ValidateReadPtr(p,cb) ValidateWritePtr(p,cb)}
#else
    #ifndef ASSERT
       #define ASSERT(_x_) ((void)0)
    #endif
    #define EXECUTE_ASSERT(_x_) ((void)(_x_))

    #define ValidateReadPtr(p,cb) 0
    #define ValidateWritePtr(p,cb) 0
    #define ValidateReadWritePtr(p,cb) 0
#endif

/* The DLLENTRY module initialises the module handle on loading */

extern HINSTANCE g_hInst;

/* On DLL load remember which platform we are running on */

/* Version of IUnknown that is renamed to allow a class to support both
   non delegating and delegating IUnknowns in the same COM object */

#ifndef INDUNKNOWN_DEFINED
DECLARE_INTERFACE(INDUnknown)
{
    STDMETHOD(NDQueryInterface) (THIS_ REFIID, LPVOID *) PURE;
    STDMETHOD_(ULONG, NDAddRef)(THIS) PURE;
    STDMETHOD_(ULONG, NDRelease)(THIS) PURE;
};
#define INDUNKNOWN_DEFINED
#endif

class CBaseObject {
public:
   CBaseObject() {g_cActiveObjects++;}
   ~CBaseObject() {g_cActiveObjects--;}
};

/* An object that supports one or more COM interfaces will be based on
   this class. It supports counting of total objects for DLLCanUnloadNow
   support, and an implementation of the core non delegating IUnknown */

class CComBase : public INDUnknown,
                 CBaseObject
{
private:
    IUnknown* m_pUnknown; /* Owner of this object */

protected:                      /* So we can override NDRelease() */
    volatile LONG m_cRef;       /* Number of reference counts */

public:

    CComBase(IUnknown* pUnk);
    virtual ~CComBase() {};

    // This is redundant, just use the other constructor
    //   as we never touch the HRESULT in this anyway
    CComBase(IUnknown* pUnk,HRESULT *phr);

    /* Return the owner of this object */

    IUnknown* GetOwner() const {
        return m_pUnknown;
    };

    /* Called from the class factory to create a new instance, it is
       pure virtual so it must be overriden in your derived class */

    /* static CComBase *CreateInstance(IUnknown*, HRESULT *) */

    /* Non delegating unknown implementation */

    STDMETHODIMP NDQueryInterface(REFIID, void **);
    STDMETHODIMP_(ULONG) NDAddRef();
    STDMETHODIMP_(ULONG) NDRelease();
};

/* Return an interface pointer to a requesting client
   performing a thread safe AddRef as necessary */

STDAPI GetInterface(IUnknown* pUnk, void **ppv);

/* A function that can create a new COM object */

typedef CComBase *(CALLBACK *LPFNNewCOMObject)(IUnknown* pUnkOuter, HRESULT *phr);

/*  A function (can be NULL) which is called from the DLL entrypoint
    routine for each factory template:

    bLoading - TRUE on DLL load, FALSE on DLL unload
    rclsid   - the m_ClsID of the entry
*/
typedef void (CALLBACK *LPFNInitRoutine)(BOOL bLoading, const CLSID *rclsid);

#define CheckPointer(p,ret) {if((p)==NULL) return (ret);}

/* Create one of these per object class in an array so that
   the default class factory code can create new instances */

struct CComClassTemplate {
    const CLSID *              m_ClsID;
    LPFNNewCOMObject           m_lpfnNew;
};


/* You must override the (pure virtual) NDQueryInterface to return
   interface pointers (using GetInterface) to the interfaces your derived
   class supports (the default implementation only supports IUnknown) */

#define DECLARE_IUNKNOWN                                        \
    STDMETHODIMP QueryInterface(REFIID riid, void **ppv) {      \
        return GetOwner()->QueryInterface(riid,ppv);            \
    };                                                          \
    STDMETHODIMP_(ULONG) AddRef() {                             \
        return GetOwner()->AddRef();                            \
    };                                                          \
    STDMETHODIMP_(ULONG) Release() {                            \
        return GetOwner()->Release();                           \
    };



HINSTANCE	LoadOLEAut32();


#endif /* __COMBASE__ */