Tricks of the Windows Game Programming Gurus:DirectX Fundamentals and the Dreaded COM

All ITKnowledge Source Code Search Tips Advanced Search |

| Tricks of the Windows Game Programming Gurus by Andre LaMothe Sams, Macmillan Computer Publishing ISBN: 0672313618 Pub Date: 09/27/99

Search this book:

Previous | Table of Contents | Next

#### A Working COM Program

As a complete example of creating a COM object and a couple of interfaces, I have created `DEMO5_1.CPP` for you. The program implements a COM object called `CCOM_OJBECT` that is composed of two interfaces, `IX` and `IY`. The program is a decent implementation of a COM object, but of course it’s missing some of the high-level details like being a DLL, loading dynamically, and so on. But the COM object is fully implemented as far as all the methods and the `IUnknown` class are concerned.

What I want you to do is look at it very carefully, play with the code, and see how it works. Listing 5.1 contains the entire source for the COM object and a simple C/C++ `main()` test bed to run it in.

Listing 5.1 A Complete COM Object Program

    // DEMO5_1.CPP - A ultra minimal working COM example

    // NOTE: not fully COM compliant

    // INCLUDES ///////////////////////////////////////////////////

    #include <stdio.h>

    #include <malloc.h>

    #include <iostream.h>

    #include <objbase.h> // note: you must include this header it

                         // contains important constants

                         // you must use in COM programs

    // GUIDS //////////////////////////////////////////////////////

    // these were all generated with GUIDGEN.EXE

    // {B9B8ACE1-CE14-11d0-AE58-444553540000}

    const IID IID_IX =

    { 0xb9b8ace1, 0xce14, 0x11d0,

    { 0xae, 0x58, 0x44, 0x45, 0x53, 0x54, 0x0, 0x0 } };

    // {B9B8ACE2-CE14-11d0-AE58-444553540000}

    const IID IID_IY =

    { 0xb9b8ace2, 0xce14, 0x11d0,

    { 0xae, 0x58, 0x44, 0x45, 0x53, 0x54, 0x0, 0x0 } };

    // {B9B8ACE3-CE14-11d0-AE58-444553540000}

    const IID IID_IZ =

    { 0xb9b8ace3, 0xce14, 0x11d0,

    { 0xae, 0x58, 0x44, 0x45, 0x53, 0x54, 0x0, 0x0 } };

    // INTERFACES /////////////////////////////////////////////////

    // define the IX interface

    interface IX: IUnknown

    {

    virtual void __stdcall fx(void)=0;

    };

    // define the IY interface

    interface IY: IUnknown

    {

    virtual void __stdcall fy(void)=0;

    };

    // CLASSES AND COMPONENTS /////////////////////////////////////

    // define the COM object

    class CCOM_OBJECT :    public IX,

                        public IY

    {

    public:

        CCOM_OBJECT() : ref_count(0) {}

        ∼CCOM_OBJECT() {}

    private:

    virtual HRESULT __stdcall QueryInterface(const IID &iid, void **iface);

    virtual ULONG __stdcall AddRef();

    virtual ULONG __stdcall Release();

    virtual    void __stdcall fx(void)

                  {cout << "Function fx has been called." << endl; }

    virtual void __stdcall fy(void)

                  {cout << "Function fy has been called." << endl; }

    int ref_count;

    };

    // CLASS METHODS //////////////////////////////////////////////

    HRESULT __stdcall CCOM_OBJECT::QueryInterface(const IID &iid,

                                                  void **iface)

    {

    // this function basically casts the this pointer or the IUnknown

    // pointer into the interface requested, notice the comparison with

    // the GUIDs generated and defined in the beginning of the program

    // requesting the IUnknown base interface

    if (iid==IID_IUnknown)

        {

        cout << "Requesting IUnknown interface" << endl;

        *iface = (IX*)this;

        } // end if

    // maybe IX?

    if (iid==IID_IX)

        {

        cout << "Requesting IX interface" << endl;

        *iface = (IX*)this;

        } // end if

    else // maybe IY

    if (iid==IID_IY)

        {

        cout << "Requesting IY interface" << endl;

        *iface = (IY*)this;

        } // end if

    else

        { // cant find it!

        cout << "Requesting unknown interaface!" << endl;

        *iface = NULL;

        return(E_NOINTERFACE);

        } // end else

    // if everything went well cast pointer to

    // IUnknown and call addref()

    ((IUnknown *)(*iface))->AddRef();

    return(S_OK);

    } // end QueryInterface

    ///////////////////////////////////////////////////////////////

    ULONG __stdcall CCOM_OBJECT::AddRef()

    {

    // increments reference count

    cout << "Adding a reference" << endl;

    return(++ref_count);

    } // end AddRef

    ///////////////////////////////////////////////////////////////

    ULONG __stdcall CCOM_OBJECT::Release()

    {

    // decrements reference count

    cout << "Deleting a reference" << endl;

    if (--ref_count==0)

        {

        delete this;

        return(0);

        } // end if

    else

        return(ref_count);

    } // end Release

    ///////////////////////////////////////////////////////////////

    IUnknown *CoCreateInstance(void)

    {

    // this is a very basic implementation of CoCreateInstance()

    // it creates an instance of the COM object, in this case

    // I decided to start with a pointer to IX -- IY would have

    // done just as well

    IUnknown *comm_obj = (IX *)new(CCOM_OBJECT);

    cout << "Creating Comm object" << endl;

    // update reference count

    comm_obj->AddRef();

    return(comm_obj);

    } // end CoCreateInstance

    //////////////////////////////////////////////////////////////

    void main(void)

    {

    // create the main COM object

    IUnknown *punknown = CoCreateInstance();

    // create two NULL pointers the IX and IY interfaces

    IX *pix=NULL;

    IY *piy=NULL;

    // from the original COM object query for interface IX

    punknown->QueryInterface(IID_IX, (void **)&pix);

    // try some of the methods of IX

    pix->fx();

    // release the interface

    pix->Release();

    // now query for the IY interface

    punknown->QueryInterface(IID_IY, (void **)&piy);

    // try some of the methods

    piy->fy();

    // release the interface

    piy->Release();

    // release the COM object itself

    punknown->Release();

    } // end main

I have already precompiled the program for you into the executable `DEMO5_1.EXE`. However, if you want to experiment and compile `DEMO5_1.CPP`, remember to create a Win32 Console Application because the demo uses `main()` rather than `WinMain()` and is, of course, a text-based program.

Previous | Table of Contents | Next

Copyright © Macmillan Computer Publishing, Inc.

Products | Contact Us | About Us | Privacy | Ad Info | Home
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc. All rights reserved. Reproduction in whole or in part in any form or medium without express written permission of EarthWeb is prohibited. Read EarthWeb's privacy statement.