kasajian
5/20/2014 - 2:48 AM

Creating a COM Object in plain C++

Creating a COM Object in plain C++

#ifndef __CCOM_H__
#define __CCOM_H__

//------------------------------------------------------------------------------
// CLASS CFactory
//------------------------------------------------------------------------------

class CFactory : public IClassFactory 
{
public:
                                CFactory( const CLSID& clsid );

    // IUnknown methods
    virtual HRESULT __stdcall   QueryInterface( const IID& riid, void** ppv );
    virtual ULONG __stdcall     AddRef( void );
    virtual ULONG __stdcall     Release( void );

    // IClassFactory methods
    virtual HRESULT __stdcall   CreateInstance( IUnknown* punkOuter, const IID& riid, void** ppv );
    virtual HRESULT __stdcall   LockServer( BOOL fLock );

private:
    ULONG    fReferenceCount;
    CLSID    fClsid;

}; // class CFactory


//------------------------------------------------------------------------------
// CLASS CMath
//------------------------------------------------------------------------------

class CMath : public IDispatch 
{
public:
                                CMath(): fReferenceCount( 0 ) {}

    // IUnknown methods
    virtual HRESULT __stdcall    QueryInterface( const IID& riid, void** ppv );
    virtual ULONG __stdcall      AddRef( void );
    virtual ULONG __stdcall      Release( void );

    // IDispatch methods
    virtual HRESULT __stdcall    GetTypeInfoCount( UINT* );
    virtual HRESULT __stdcall    GetTypeInfo( UINT, LCID, ITypeInfo** );
    virtual HRESULT __stdcall    GetIDsOfNames( REFIID, OLECHAR**, UINT, LCID, DISPID* );
    virtual HRESULT __stdcall    Invoke( DISPID, REFIID, LCID, USHORT, DISPPARAMS*, VARIANT*, EXCEPINFO*, UINT* );

private:
    ULONG    fReferenceCount;

}; // class CMath


#endif // __CCOM_H__
#include <windows.h>
#include "ccom.h"

// In order to use from VBScript, you need to place the progID in the registry so
// that it can be used to obtain the CLSID of this class.
// VB script
/*
DIM X
DIM Y
SET X = CREATEOBJECT( "BrlsMath.Math" )
Y = X.ADD( 5, 6 )
MSGBOX Y
SET X = NOTHING
*/

// ccom.reg file:
/*
REGEDIT
; This .REG file may be used by your SETUP program.

HKEY_CLASSES_ROOT\BrlsMath.Math = Borealis Sample Math class
HKEY_CLASSES_ROOT\BrlsMath.Math\CLSID = {EADAD891-D328-11d1-BEAA-00A02481FB8E}

HKEY_CLASSES_ROOT\CLSID\{0218BDBC-D31A-11D1-BEAA-00A02481FB8E} = Borealis Sample Math class
HKEY_CLASSES_ROOT\CLSID\{0218BDBC-D31A-11D1-BEAA-00A02481FB8E}\ProgId = BrlsMath.Math
*/

// {EADAD891-D328-11d1-BEAA-00A02481FB8E}
static const GUID CLSID_Math = 
{ 0xeadad891, 0xd328, 0x11d1, { 0xbe, 0xaa, 0x0, 0xa0, 0x24, 0x81, 0xfb, 0x8e } };

#define kMemberAdd            1
#define kMemberSubtract        2


//========================================================================================
// CLASS CFactory
//========================================================================================

//------------------------------------------------------------------------------
// CFactory::CFactory
//------------------------------------------------------------------------------

CFactory::CFactory( const CLSID& clsid )
    :    fReferenceCount( 1 ),
        fClsid( clsid )
{
} // CFactory::CFactory 


//------------------------------------------------------------------------------
// CFactory::QueryInterface
// Implementation of IUnknown method
//------------------------------------------------------------------------------

HRESULT CFactory::QueryInterface( const IID& riid, void ** ppv )
{
    if (    IsEqualIID( riid, IID_IUnknown ) ||
            IsEqualIID( riid, IID_IClassFactory ) )
    {
        AddRef();
        *ppv = this;
        return NOERROR;
    }

    *ppv = NULL;

    return ResultFromScode( E_NOINTERFACE );

} // CFactory::QueryInterface


//------------------------------------------------------------------------------
// CFactory::AddRef
// Implementation of IUnknown method
//------------------------------------------------------------------------------

ULONG CFactory::AddRef( )
{
    return ++fReferenceCount;

} // CFactory::AddRef


//------------------------------------------------------------------------------
// CFactory::Release
// Implementation of IUnknown method
//------------------------------------------------------------------------------

ULONG CFactory::Release()
{
    if( 0 == --fReferenceCount )
    {
        delete this;
        return 0;
    }

    return fReferenceCount;

} // CFactory::Release()


//------------------------------------------------------------------------------
// CFactory::CreateInstance
// Implementation of IClassFactory method.
//------------------------------------------------------------------------------

HRESULT CFactory::CreateInstance(    IUnknown*        punkOuter,
                                    const IID&        riid,
                                    void**            ppv)
{
    HRESULT        hresult = E_NOINTERFACE;

    *ppv = NULL;

    if ( CLSID_Math == fClsid  )
    {
        CMath*    math = new CMath;

        hresult = math->QueryInterface( riid, ppv );
        if ( FAILED( hresult ) )
            math->Release( );
    }

    return hresult;

    punkOuter;

} // CFactory::CreateInstance


//------------------------------------------------------------------------------
// CFactory::LockServer
// Implementation of IClassFactory method.
//------------------------------------------------------------------------------

HRESULT CFactory::LockServer( BOOL fLock )
{
    return NOERROR;

    fLock;

} // CFactory::LockServer


//------------------------------------------------------------------------------
// CMath::QueryInterface
// IUnknown method
// Implementation of IUnknown method
//------------------------------------------------------------------------------

HRESULT CMath::QueryInterface( const IID& riid, void** ppv )
{
    *ppv = NULL;

    if (    IsEqualIID( riid, IID_IUnknown ) ||
            IsEqualIID( riid, IID_IDispatch ) )
    {
        this->AddRef();
        *ppv = (IDispatch*) this;
        return S_OK;
    }
    else
    {
        return E_NOINTERFACE;
    }

} // CMath::QueryInterface


//------------------------------------------------------------------------------
// CMath::AddRef
// IUnknown method
// Implementation of IUnknown method
//------------------------------------------------------------------------------

ULONG CMath::AddRef( )
{
    return ++fReferenceCount;

} // CMath::AddRef


//------------------------------------------------------------------------------
// CMath::Release
// IUnknown method
// Implementation of IUnknown method
//------------------------------------------------------------------------------

ULONG CMath::Release()
{
    if( 0 == --fReferenceCount )
    {
        delete this;
        return 0;
    }

    return fReferenceCount;

} // CMath::Release()


//------------------------------------------------------------------------------
// CMath::GetTypeInfoCount
// IDispatch method
// Returns the number of type information (ITypeInfo) interfaces
// that the object provides (0 or 1).
//------------------------------------------------------------------------------

HRESULT CMath::GetTypeInfoCount( UINT* pctInfo )
{
    // We don't have a type info.
    *pctInfo=0;
    return S_OK;

} // CMath::GetTypeInfoCount


//------------------------------------------------------------------------------
// CMath::GetTypeInfo
// IDispatch method
// Retrieves type information for the automation interface.
//------------------------------------------------------------------------------

HRESULT CMath::GetTypeInfo(UINT, LCID, ITypeInfo** pptInfo)
{
    // Since we don't have a type info, nothing to return.
    *pptInfo = NULL;
    return E_NOTIMPL;

} // CMath::GetTypeInfo


//------------------------------------------------------------------------------
// CMath::GetIDsOfNames
// IDispatch method
// Returns the IDs corresponding to given named in our dispatch
// interface.
//------------------------------------------------------------------------------

HRESULT CMath::GetIDsOfNames( REFIID, OLECHAR** rgszNames, UINT cNames, LCID, DISPID* rgDispID )
{
    if ( 1 == cNames )
    {
        if ( 0 == lstrcmpiW( *rgszNames, L"Add" ) )
        {
            *rgDispID = kMemberAdd;
            return S_OK;
        }
        else if ( 0 == lstrcmpiW( *rgszNames, L"Subtract" ) )
        {
            *rgDispID = kMemberSubtract;
            return S_OK;
        }
    }

    return DISP_E_UNKNOWNNAME;

} // CMath::GetIDsOfNames


//------------------------------------------------------------------------------
// CMath::Invoke
// IDispatch method
// Calls a method in the dispatch interface or manipulates a
// property.
//------------------------------------------------------------------------------

HRESULT CMath::Invoke( DISPID dispIDMember, REFIID, LCID, USHORT wFlags, DISPPARAMS* dispParams, VARIANT* methodRetVal, EXCEPINFO*, UINT* errArg )
{
    HRESULT     hresult;
    VARIANT        arg1;
    VARIANT        arg2;

    // If there's not return variant, use our own, which should always be the case, really.
    if ( NULL == methodRetVal )
        return E_NOTIMPL;

    VariantInit( methodRetVal );

    // We only support property gets.  So bail otherwise.
    if ( DISPATCH_METHOD & wFlags )
    {
        switch ( dispIDMember )
        {
            case kMemberAdd:
                ::VariantInit( &arg1 );
                ::VariantInit( &arg2 );
                hresult = ::DispGetParam( dispParams, dispParams->cArgs - 1, VT_R8, &arg1, errArg );
                hresult = ::DispGetParam( dispParams, dispParams->cArgs - 2, VT_R8, &arg2, errArg );
                if ( SUCCEEDED( hresult ) )
                {
                    V_VT( methodRetVal ) = VT_R8;
                    V_R8( methodRetVal ) = arg1.dblVal + arg2.dblVal;
                }
                ::VariantClear( &arg1 );
                ::VariantClear( &arg2 );
            break;

            case kMemberSubtract:
                ::VariantInit( &arg1 );
                ::VariantInit( &arg2 );
                hresult = ::DispGetParam( dispParams, dispParams->cArgs - 1, VT_R8, &arg1, errArg );
                hresult = ::DispGetParam( dispParams, dispParams->cArgs - 2, VT_R8, &arg2, errArg );
                if ( SUCCEEDED( hresult ) )
                {
                    V_VT( methodRetVal ) = VT_R8;
                    V_R8( methodRetVal ) = arg1.dblVal - arg2.dblVal;
                }
                ::VariantClear( &arg1 );
                ::VariantClear( &arg2 );
            break;

            default:
                hresult = DISP_E_MEMBERNOTFOUND;
                break;
        }
    }
    else
    {
        hresult = DISP_E_MEMBERNOTFOUND;
    }

    return hresult;

} // CMath::Invoke


void main( void )
{
    DWORD        factoryHandle;
    HRESULT        hresult;
    CFactory*    classFactory;

    hresult = CoInitialize( NULL );
    if ( SUCCEEDED( hresult ) )
    {
        classFactory = new CFactory( CLSID_Math );
        hresult = CoRegisterClassObject(    CLSID_Math,
                                            classFactory,
                                            CLSCTX_LOCAL_SERVER,
                                            REGCLS_MULTIPLEUSE,
                                            &factoryHandle );

        if ( SUCCEEDED( hresult ) )
        {
            MessageBox( 0, "ccom", "", 0 );

            CoRevokeClassObject( factoryHandle );
        }

        classFactory->Release( );

        CoUninitialize( );
    }

} // main