FANDOM


Creating a COM object in plain C is supported by Microsoft tools. However this article shows how to do it without any additional tools, i.e. no MIDL step -- just plain ol' C. The intent is for someone with the knowledge of C can get an idea of what the heck is going on in COM because they see everything right there in C. The objects created using this technique are actual COM objects and can be called from VBScript.

See also Creating a COM Object in plain C Plus Plus

#include <windows.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( "ZumaMath.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\ZumaMath.Math = Zuma Sample Math class
HKEY_CLASSES_ROOT\ZumaMath.Math\CLSID = {EADAD891-D328-11d1-BEAA-00A02481FB8E}

HKEY_CLASSES_ROOT\CLSID\{EADAD891-D328-11d1-BEAA-00A02481FB8E} = Zuma Sample Math class
HKEY_CLASSES_ROOT\CLSID\{EADAD891-D328-11d1-BEAA-00A02481FB8E}\ProgId = ZumaMath.Math
==============================================================================*/


//==============================================================================
// CLASS CMath
//==============================================================================

typedef struct _tMath
{
    void**        fVTable;

    ULONG        fReferenceCount;

} tMath; // struct tMath


//------------------------------------------------------------------------------
// Class method count
//------------------------------------------------------------------------------

#define kMathMethodCount    7


//------------------------------------------------------------------------------
// Class method prototypes
//------------------------------------------------------------------------------

HRESULT        __stdcall Math_QueryInterface( tMath* this, const IID* riid, void** ppv );

ULONG          __stdcall Math_AddRef( tMath* this );

ULONG          __stdcall Math_Release( tMath* this );

HRESULT        __stdcall Math_GetTypeInfoCount( tMath* this, UINT* pctInfo );

HRESULT        __stdcall Math_GetTypeInfo( tMath* this, UINT, LCID, ITypeInfo** pptInfo );

HRESULT        __stdcall Math_GetIDsOfNames( tMath* this, REFIID,
                                             OLECHAR** rgszNames,
                                             UINT cNames, LCID,
                                             DISPID* rgDispID );

HRESULT        __stdcall Math_Invoke( tMath* this, DISPID dispIDMember,
                                      REFIID, LCID, USHORT wFlags,
                                      DISPPARAMS* dispParams,
                                      VARIANT* methodRetVal, EXCEPINFO*,
                                      UINT* errArg );


//==============================================================================
// Class defines
//==============================================================================

// {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


//------------------------------------------------------------------------------
// Math_QueryInterface
// IUnknown method
// Implementation of IUnknown method
//------------------------------------------------------------------------------

HRESULT __stdcall Math_QueryInterface( tMath* this, const IID* riid, void** ppv )
{
    *ppv = NULL;

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

} // Math_QueryInterface


//------------------------------------------------------------------------------
// Math_AddRef
// IUnknown method
// Implementation of IUnknown method
//------------------------------------------------------------------------------

ULONG __stdcall Math_AddRef( tMath* this )
{
    return ++this->fReferenceCount;

} // Math_AddRef


//------------------------------------------------------------------------------
// Math_Release
// IUnknown method
// Implementation of IUnknown method
//------------------------------------------------------------------------------

ULONG __stdcall Math_Release( tMath* this )
{
    if( 0 == --this->fReferenceCount )
    {
        CoTaskMemFree( this );
        return 0;
    }

    return this->fReferenceCount;

} // Math_Release()


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

HRESULT __stdcall Math_GetTypeInfoCount( tMath* this, UINT* pctInfo )
{
    // We don't have a type info.
    *pctInfo=0;
    return S_OK;

} // Math_GetTypeInfoCount


//------------------------------------------------------------------------------
// Math_GetTypeInfo
// IDispatch method
// Retrieves type information for the automation interface.
//------------------------------------------------------------------------------

HRESULT __stdcall Math_GetTypeInfo( tMath* this, UINT iTInfo, LCID lcid, ITypeInfo** pptInfo )
{
    // Since we don't have a type info, nothing to return.
    *pptInfo = NULL;
    return E_NOTIMPL;

} // Math_GetTypeInfo


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

HRESULT __stdcall Math_GetIDsOfNames(   tMath*      this,
                                        REFIID      riid,
                                        OLECHAR**   rgszNames,
                                        UINT        cNames,
                                        LCID        lcid,
                                        DISPID*     rgDispID )
{
    UINT        index;
    HRESULT        hresult = S_OK;

    for ( index = 0; index < cNames; index++ )
    {
        if ( 0 == lstrcmpiW( rgszNames[index], L"Add" ) )
        {
            rgDispID[index] = kMemberAdd;
        }
        else if ( 0 == lstrcmpiW( rgszNames[index], L"Subtract" ) )
        {
            rgDispID[index] = kMemberSubtract;
        }
        else
        {
            hresult = DISP_E_UNKNOWNNAME;
            break;
        }
    }

    return hresult;

} // Math_GetIDsOfNames


//------------------------------------------------------------------------------
// Math_Invoke
// IDispatch method
// Calls a method in the dispatch interface or manipulates a
// property.
//------------------------------------------------------------------------------

HRESULT __stdcall Math_Invoke( tMath*        this,
                               DISPID        dispIDMember,
                               REFIID        riid,
                               LCID          lcid,
                               USHORT        wFlags,
                               DISPPARAMS*   dispParams,
                               VARIANT*      methodRetVal,
                               EXCEPINFO*    pExcepInfo,
                               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;

} // Math_Invoke


//------------------------------------------------------------------------------
// NewMath
//------------------------------------------------------------------------------

tMath* NewMath( )
{
    tMath*    math;

    math = (tMath*) CoTaskMemAlloc( sizeof( tMath ) );
    math->fVTable = (void**) CoTaskMemAlloc( kMathMethodCount * sizeof( void* ) );

    math->fVTable[0] = Math_QueryInterface;
    math->fVTable[1] = Math_AddRef;
    math->fVTable[2] = Math_Release;
    math->fVTable[3] = Math_GetTypeInfoCount;
    math->fVTable[4] = Math_GetTypeInfo;
    math->fVTable[5] = Math_GetIDsOfNames;
    math->fVTable[6] = Math_Invoke;

    math->fReferenceCount = 1;

    return math;

} // NewMath


//==============================================================================
// CLASS tFactory
//==============================================================================

typedef struct _tFactory
{
    void**       fVTable;

    ULONG        fReferenceCount;
    CLSID        fClsid;

} tFactory; // struct tFactory


//------------------------------------------------------------------------------
// Class method count
//------------------------------------------------------------------------------

#define kFactoryMethodCount        5


//------------------------------------------------------------------------------
// Class method prototypes
//------------------------------------------------------------------------------

HRESULT   __stdcall Factory_QueryInterface( tFactory* this, const IID* riid, void ** ppv );

ULONG     __stdcall Factory_AddRef( tFactory* this );

ULONG     __stdcall Factory_Release( tFactory* this );

ULONG     __stdcall Factory_Release( tFactory* this );

HRESULT   __stdcall Factory_CreateInstance( tFactory* this, IUnknown* punkOuter, const IID* riid, void** ppv );

HRESULT   __stdcall Factory_LockServer( tFactory* this, BOOL fLock );


//------------------------------------------------------------------------------
// Factory_QueryInterface
// Implementation of IUnknown method
//------------------------------------------------------------------------------

HRESULT __stdcall Factory_QueryInterface( tFactory* this, const IID* riid, void ** ppv )
{
    if (    IsEqualIID( riid, &IID_IUnknown ) ||
            IsEqualIID( riid, &IID_IClassFactory ) )
    {
        Factory_AddRef( this );
        *ppv = this;
        return NOERROR;
    }

    *ppv = NULL;

    return ResultFromScode( E_NOINTERFACE );

} // Factory_QueryInterface


//------------------------------------------------------------------------------
// Factory_AddRef
// Implementation of IUnknown method
//------------------------------------------------------------------------------

ULONG __stdcall Factory_AddRef( tFactory* this )
{
    return ++this->fReferenceCount;

} // Factory_AddRef


//------------------------------------------------------------------------------
// Factory_Release
// Implementation of IUnknown method
//------------------------------------------------------------------------------

ULONG __stdcall Factory_Release( tFactory* this )
{
    if( 0 == --this->fReferenceCount )
    {
        CoTaskMemFree( this );
        return 0;
    }

    return this->fReferenceCount;

} // Factory_Release()


//------------------------------------------------------------------------------
// Factory_CreateInstance
// Implementation of IClassFactory method.
//------------------------------------------------------------------------------

HRESULT __stdcall Factory_CreateInstance( tFactory*  this,
                                          IUnknown*  punkOuter,
                                          const IID* riid,
                                          void**     ppv)
{
    HRESULT hresult = E_NOINTERFACE;

    *ppv = NULL;

    if ( IsEqualCLSID( &CLSID_Math, &this->fClsid  ) )
    {
        tMath*    math = NewMath( );

        hresult = Math_QueryInterface( math, riid, ppv );
        if ( FAILED( hresult ) )
            Math_Release( math );
    }

    return hresult;

    punkOuter;

} // Factory_CreateInstance


//------------------------------------------------------------------------------
// Factory_LockServer
// Implementation of IClassFactory method.
//------------------------------------------------------------------------------

HRESULT __stdcall Factory_LockServer( tFactory* this, BOOL fLock )
{
    return NOERROR;

    fLock;

} // Factory_LockServer


//------------------------------------------------------------------------------
// NewFactory
//------------------------------------------------------------------------------

tFactory* NewFactory( const    CLSID* clsid )
{
    tFactory*    factory;

    factory = (tFactory*) CoTaskMemAlloc( sizeof( tFactory ) );
    factory->fVTable = (void**) CoTaskMemAlloc( kFactoryMethodCount * sizeof( void* ) );

    factory->fVTable[0] = Factory_QueryInterface;
    factory->fVTable[1] = Factory_AddRef;
    factory->fVTable[2] = Factory_Release;
    factory->fVTable[3] = Factory_CreateInstance;
    factory->fVTable[4] = Factory_LockServer;

    factory->fReferenceCount = 1;
    factory->fClsid = *clsid;

    return factory;

} // NewFactory


//------------------------------------------------------------------------------
// RegisterProgID
//------------------------------------------------------------------------------

void RegisterProgID( const char* progID, const char* clsidAsString )
{
    char            classKey[256];
    char            classKeyProgID[256];
    char            progKeyClsid[256];

    // Create the CLSID key, example:
    // "CLSID\\{EADAD891-D328-11d1-BEAA-00A02481FB8E}"
    lstrcpy( classKey, "CLSID\\" );
    lstrcat( classKey, clsidAsString );

    // Create the ProgID key, example:
    // "CLSID\\{EADAD891-D328-11d1-BEAA-00A02481FB8E}\ProgID"
    lstrcpy( classKeyProgID, classKey );
    lstrcat( classKeyProgID, "\\ProgID" );
    
    // Create the progID key with CLSID, example: "Dispcalc.Application\\CLSID"
    lstrcpy( progKeyClsid, progID );
    lstrcat( progKeyClsid, "\\CLSID" );

    // Register the class and prog ID keys.
    RegSetValue( HKEY_CLASSES_ROOT, classKey, REG_SZ, progID, 0);
    RegSetValue( HKEY_CLASSES_ROOT, classKeyProgID, REG_SZ, progID, 0);
    RegSetValue( HKEY_CLASSES_ROOT, progKeyClsid, REG_SZ, clsidAsString, 0);

} // RegisterProgID


//------------------------------------------------------------------------------
// main
//------------------------------------------------------------------------------

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

    LPOLESTR    lpszClassID;

    hresult = StringFromCLSID( (IID*) &IID_IUnknown, &lpszClassID );
    if ( SUCCEEDED( hresult ) )
    {
        OutputDebugStringW( L"IID_IUnknown: " );
        OutputDebugStringW( lpszClassID );
        OutputDebugStringW( L"\n" );
        CoTaskMemFree( lpszClassID );
    }

    hresult = StringFromCLSID( (IID*) &IID_IClassFactory, &lpszClassID );
    if ( SUCCEEDED( hresult ) )
    {
        OutputDebugStringW( L"IID_IClassFactory: " );
        OutputDebugStringW( lpszClassID );
        OutputDebugStringW( L"\n" );
        CoTaskMemFree( lpszClassID );
    }

    hresult = StringFromCLSID( (IID*) &IID_IDispatch, &lpszClassID );
    if ( SUCCEEDED( hresult ) )
    {
        OutputDebugStringW( L"IID_IDispatch: " );
        OutputDebugStringW( lpszClassID );
        OutputDebugStringW( L"\n" );
        CoTaskMemFree( lpszClassID );
    }

    RegisterProgID( "ZumaMath.Math", "{EADAD891-D328-11d1-BEAA-00A02481FB8E}" );

    hresult = CoInitialize( NULL );
    if ( SUCCEEDED( hresult ) )
    {
        classFactory = NewFactory( &CLSID_Math );
        hresult = CoRegisterClassObject(    &CLSID_Math,
                                            (IUnknown*)classFactory,
                                            CLSCTX_LOCAL_SERVER,
                                            REGCLS_MULTIPLEUSE,
                                            &factoryHandle );

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

            CoRevokeClassObject( factoryHandle );
        }

        Factory_Release( classFactory );

        CoUninitialize( );
    }

} // main

Ad blocker interference detected!


Wikia is a free-to-use site that makes money from advertising. We have a modified experience for viewers using ad blockers

Wikia is not accessible if you’ve made further modifications. Remove the custom ad blocker rule(s) and the page will load as expected.