/****************************************************************************
                       Microsoft RPC 
                Copyright Microsoft Corp. 1992
                  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;
}
