/****************************************************************************
                       Microsoft RPC
                Copyright Microsoft Corp. 1992-1996
                  object call_as Example

    FILE:       server.cxx

    USAGE:      server  - note that this is auto-loaded by the client
                          requesting this server, and exits at completion

    PURPOSE:    Server side of RPC distributed application

    FUNCTIONS:  main() - binds to server and calls remote procedure

    COMMENTS:   This version of the Ole2 distributed application that
                prints "hello, world" (or other string) on the server
                features a non-rpcable custom interface being remoted with
                [call_as].

                It provides a class factory to manufacture a server object,
                and the implementation of the callas server object

****************************************************************************/

#define INC_OLE2
#include <windows.h>
#include <stdio.h>
#include <assert.h>
#include "callas.h"

unsigned long   ObjectCount         = 0;
BOOL            fClassRegistered    = FALSE;
DWORD           dwHandle            = 0;

// this is the CLSID for the class factory for the server

const CLSID CLSID_CallAs
    = {0xf9246030,0x9f33,0x11cd,{0xb2,0x3f,0x00,0xaa,0x00,0x33,0x9c,0xce}};

IClassFactory *GetCallAsFactory();




// here are some convenient inline functions to reduce the casting involved
// with having the RefCount be unsigned instead of signed, and pass by
// reference

inline
unsigned long
IncrementRefCount( unsigned long & RefCount )
    {
    return InterlockedIncrement( (long *) &RefCount );
    }

inline
unsigned long
DecrementRefCount( unsigned long & RefCount )
    {
    return InterlockedDecrement( (long *) &RefCount );
    }







// this is the class factory for the CCallAs server
// it manufactures CCallAs server objects in response to a CreateInstance
// call

class CCallAsFactory : public IClassFactory

{
public:
    // note that the virtual methods must exactly match the set for
    // the IClassFactory in the idl (and callas.h)
    unsigned long    RefCount;

                            CCallAsFactory()
                                {
                                RefCount    = 0;
                                }

        HRESULT STDMETHODCALLTYPE QueryInterface(
                REFIID iid,
            void **ppv);

        ULONG STDMETHODCALLTYPE AddRef();

        ULONG STDMETHODCALLTYPE Release();

        HRESULT STDMETHODCALLTYPE CreateInstance(
            IUnknown *punkOuter,
            REFIID riid,
            void **ppv);

   HRESULT STDMETHODCALLTYPE LockServer(
        BOOL fLock );
};

// allocate a static classfactory to avoid construction races if
// we were multithreaded.
CCallAsFactory       gCallAsClassFactory;





class CCallAs   : public IHelloCallAs

{

    unsigned long    RefCount;

public:

    // remoted functions (virtual)

        HRESULT STDMETHODCALLTYPE QueryInterface(
                REFIID iid,
            void **ppv);

        ULONG STDMETHODCALLTYPE AddRef();

        ULONG STDMETHODCALLTYPE Release();

        MyBoolean __stdcall HelloProc(
            unsigned char *pszString);

        // local functions
        // constructor
                            CCallAs()
                                {
                                RefCount    = 0;
                                }

    void    *   operator new( size_t s )
                                {
                                return CoTaskMemAlloc( s );
                                }

    void        operator delete( void * pv )
                                {
                                CoTaskMemFree( pv );
                                }

};


//+---------------------------------------------------------------------------
//
//  Function:   main
//
//  Synopsis:
//
//  Arguments:
//
//  Returns:    void
//
//  Modifies:   nothing
//
//  Notes:      none
//
//----------------------------------------------------------------------------

void _cdecl main()
{
    HRESULT hr;
    MSG msg;
        IClassFactory *pClassFactory;

    //
    // Initialize OLE before calling any other OLE functions.
    //

    hr = CoInitialize(NULL);

    if( FAILED(hr))
    {
            printf("Server: CoInitialize failed(%x)\n",hr);
        return;
    }

        pClassFactory = GetCallAsFactory();

        hr = CoRegisterClassObject(CLSID_CallAs,
                                               (IUnknown *) pClassFactory,
                                               CLSCTX_LOCAL_SERVER,
                                               REGCLS_SINGLEUSE,
                                               &dwHandle);

        pClassFactory->Release();

        if (FAILED(hr))
            printf("Server: CoRegisterClassObject failed %x\n", hr);
        else
        {
                fClassRegistered = TRUE;

            // message loop
        while (GetMessage(&msg, NULL, 0, 0))
        {
                TranslateMessage(&msg);
                DispatchMessage(&msg);
        }
        }
    CoUninitialize();

    return;
}

//+-------------------------------------------------------------------------
//
//  Method:     GetCallAsFactory
//
//  Synopsis:   Get an IClassFactory * for the CallAs class.
//
//--------------------------------------------------------------------------
IClassFactory *GetCallAsFactory()
{
    // increment the file object count the first time it is created
        if(gCallAsClassFactory.RefCount == 0)
        {
                IncrementRefCount(gCallAsClassFactory.RefCount);
                IncrementRefCount(ObjectCount);
        }
        else
        {
                IncrementRefCount(gCallAsClassFactory.RefCount);
        }
        return &gCallAsClassFactory;
}

//+-------------------------------------------------------------------------
//
//  Method:     CCallAsFactory::QueryInterface, public
//
//  Synopsis:   Query for an interface on the class factory.
//
//  Derivation: IUnknown
//
//--------------------------------------------------------------------------
HRESULT STDMETHODCALLTYPE
CCallAsFactory::QueryInterface (
    REFIID iid,
    void **ppv )
{
    HRESULT hr = E_NOINTERFACE;

        assert(ppv);

    *ppv = 0;
    if ( IsEqualGUID( iid, IID_IUnknown) ||
         IsEqualGUID( iid, IID_IClassFactory ) )
    {
        *ppv = this;
                AddRef();
            hr = S_OK;
    }

    return hr;
}
//+-------------------------------------------------------------------------
//
//  Method:     CCallAsFactory::AddRef, public
//
//  Synopsis:   Increment DLL reference counts
//
//  Derivation: IUnknown
//
//      Notes: We have a single instance of the CCallAsFactory.
//
//--------------------------------------------------------------------------
ULONG STDMETHODCALLTYPE
CCallAsFactory::AddRef()
{
    IncrementRefCount(RefCount);
        return RefCount;
}

//+-------------------------------------------------------------------------
//
//  Method:     CCallAsFactory::Release, public
//
//  Synopsis:   Decrement DLL reference count
//
//  Derivation: IUnknown
//
//--------------------------------------------------------------------------
ULONG STDMETHODCALLTYPE
CCallAsFactory::Release()
{
        long t;
        unsigned long count;

    t = DecrementRefCount(RefCount);

        if(t == 0)
        {
                count = 0;
                //decrement the object count
                if(DecrementRefCount(ObjectCount) == 0)
                {
                        //The last object has been destroyed.
                        PostQuitMessage(0);
                }
        }
        else
                count = RefCount;

    return count;
}

//+-------------------------------------------------------------------------
//
//  Method:     CCallAsFactory::CreateInstance, public
//
//  Synopsis:   Create an instance of CCallAs.
//
//--------------------------------------------------------------------------
HRESULT STDMETHODCALLTYPE
CCallAsFactory::CreateInstance
(
    IUnknown *punkOuter,
    REFIID riid,
    void **ppv
)
{
    HRESULT hr = E_OUTOFMEMORY;
        CCallAs *pCallAs;

        assert(ppv);

        *ppv = 0;

        if(fClassRegistered == TRUE)
        {
                fClassRegistered = FALSE;
                CoRevokeClassObject(dwHandle);
        }

        pCallAs = new CCallAs();
        if(pCallAs)
        {
            // invoke the constructor
                *pCallAs = CCallAs();

                //increment the object count.
                //The object count will keep this process alive until all
                //objects are released.
                IncrementRefCount(ObjectCount);
                hr = pCallAs->QueryInterface( riid, ppv);
        }

    return hr;
}

//+-------------------------------------------------------------------------
//
//  Method:     CCallAsFactory::LockServer, public
//
//  Synopsis:   Lock the server in memory (by adding an extra reference)
//
//--------------------------------------------------------------------------
HRESULT STDMETHODCALLTYPE
CCallAsFactory::LockServer(
        BOOL fLock )
{
    if ( fLock )
            IncrementRefCount( ObjectCount );
        else
            DecrementRefCount( ObjectCount );

        return S_OK;

}

//+-------------------------------------------------------------------------
//
//  Method:     CCallAs::QueryInterface, public
//
//  Synopsis:   Query for an interface on the class factory.
//
//  Derivation: IUnknown
//
//--------------------------------------------------------------------------
HRESULT STDMETHODCALLTYPE
CCallAs::QueryInterface (
    REFIID iid,
    void **ppv )
{
    HRESULT hr = E_NOINTERFACE;

        assert(ppv);

    *ppv = 0;
    if ( IsEqualGUID( iid, IID_IUnknown ) ||
         IsEqualGUID( iid, IID_IHelloCallAs ) )
    {
        *ppv = this;
                AddRef();
            hr = S_OK;
    }

    return hr;
}
//+-------------------------------------------------------------------------
//
//  Method:     CCallAs::AddRef, public
//
//  Synopsis:   Increment reference count
//
//  Derivation: IUnknown
//
//--------------------------------------------------------------------------
ULONG STDMETHODCALLTYPE
CCallAs::AddRef()
{
    IncrementRefCount(RefCount);
        return RefCount;
}

//+-------------------------------------------------------------------------
//
//  Method:     CCallAs::Release, public
//
//  Synopsis:   Decrement DLL reference count
//
//  Derivation: IUnknown
//
//--------------------------------------------------------------------------
ULONG STDMETHODCALLTYPE
CCallAs::Release()
{
        long t;
        unsigned long count;

    t = DecrementRefCount(RefCount);

        if(t == 0)
        {
                count = 0;

                //decrement the object count
                if(DecrementRefCount(ObjectCount) == 0)
                {
                        //The last object has been destroyed.
                        PostQuitMessage(0);
                }
                delete this;
        }
        else
                count = RefCount;

    return count;
}

//+---------------------------------------------------------------------------
//
//  Function:   CCallAs::HelloProc()
//
//  Synopsis:   Displays the specified string
//
//  Arguments:  void
//
//  Returns:    Success.
//
//  Modifies:   nothing
//
//  Notes:      none
//
//----------------------------------------------------------------------------
MyBoolean __stdcall
CCallAs::HelloProc(unsigned char *pszString)
{
    printf("HelloProc: %s \n", pszString);
    printf(" <pausing for 5 sec>\n");
    // pretend to be busy computing for a bit
    Sleep( 5000 ); // 5000 msec = 5 sec

    return TRUE;
}
