Page 1 of 1

Microsoft : Working With Component Object Model Objects - Part I An introduction to IUnknown and UUID's Rate Topic: ***** 1 Votes

#1 Martyn.Rae  Icon User is offline

  • The programming dinosaur
  • member icon

Reputation: 540
  • View blog
  • Posts: 1,406
  • Joined: 22-August 09

Posted 02 April 2010 - 09:10 AM

Microsoft : Working With COM Objects - Part I

Introduction

The Component Object Model (COM) provides a strict standard for object classes and instances of a class. More importantly, COM concerns itself with the interfaces provided by a class in order that such objects can be created, interact with each other and destroyed. It is particularly relevant when objects are being shared between threads, processes and even machines. This tutorial is aimed at introducing you to some of the basic concepts of the object model and what benifits it has.

Interfaces

All object interfaces are derived from a pure virtual class known as IUnknown. This interface allows instances of other objects to be obtained, as well as controlling the lifetime of the object itself. Let's delve right in there and create a class known as foo that is derived from the IUnknown interface.

#include <windows.h>

interface IFoo : public IUnknown {
    public:
        virtual HRESULT foobar() = 0;
};

class foo : public IFoo {
    private:
        DWORD reference_count;
    public:
        foo() : reference_count(0) { }
        HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, PVOID * object) {
            *object = NULL;
            if ( riid == IID_IUnknown) {
                *object = this;
                this->AddRef(); 
            }
            if ( *object == NULL ) return E_NOINTERFACE;
            return NO_ERROR;
        }
        ULONG STDMETHODCALLTYPE AddRef() { return reference_count++; }
        ULONG STDMETHODCALLTYPE Release() { 
            if ( --reference_count == 0 ) delete this;
            return reference_count; 
        }
        HRESULT STDMETHODCALLTYPE foobar() { return NO_ERROR; }
};

HRESULT CreateFooObject(IFoo** foo_object) {
    foo * This = new foo;
    return This->QueryInterface(IID_IUnknown, (PVOID *)foo_object);
}

int main() {
    IFoo * foo_object;
    HRESULT hr = CreateFooObject(&foo_object);
    foo_object->foobar();
    foo_object->Release();
    foo_object = NULL;
    return 0;
}



You will notice that I have actually created a interface class known as IFoo, which extends the IUnknown interface with the pure virtual function foobar. No I guess you are wondering why you need to go to all this trouble, when you could just create foo and call it's methods directly. For this example, you are probably correct, but let's just look at waht is going on anyway. Consider the same code below but with the implementation code removed and the CreateFooObject function replaced with a prototype.

#include <windows.h>

interface IFoo : public IUnknown {
    public:
        virtual HRESULT STDMETHODCALLTYPE foobar() = 0;
};

HRESULT hr = CreateFooObject(IFoo ** object);

int main() {
    IFoo * foo_object;
    HRESULT hr = CreateFooObject(&foo_object);
    foo_object->foobar();
    foo_object->Release();
    foo_object = NULL;
    return 0;
}



Is it beginning to make more sense? Probably not!

What we have in that last code snippet it the aquisition of a foo object instance, and it's usage without having to know or see any of the implementation class methods other than those defined on it's interface. In other words, the IFoo interface (and the IUnknown interface from which it is derived) is the only information that is required by the outside world to use an instance of the foo object. That in turn has a very positive impact if you want to share objects between threads, processes and even machines. You will also notice that we cannot directly create a foo object, as we may only have a partial view of the complete class. Similarly, although we can delete the IFoo object, this might be catastrophic if the same object is being shared by other threads, processes or machines.

Beginning to see the light?

Let's make it go dark again shall we!

Uniqueness of object interfaces

All object interfaces should be given a universally unique identifier (UUID). So, our example above is a very poor one, and we would be restricted to using the IFoo interface within this application, on this machine. So how do we allocate a UUID? There is a program called uuidgen that should exist somewhere on your machine. This will generate a UUID that is composed of the MAC address of your network card and the date and time from your machine. All UUID's have the format XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX. The uuid I have just created for the IFoo interface is E2F7AF75-C6CF-4A80-8968-793CB864BCD9.

To declare this in your program, you would modify the IFoo interface to read

interface __declspec(uuid("E2F7AF75-C6CF-4A80-8968-793CB864BCD9")) IFoo : public IUnknown {
    public:
        virtual HRESULT foobar() = 0;
};



Next, we would modify the implementation of the QueryInterface routine as follows

        HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, PVOID * object) {
            *object = NULL;
            if ( riid == IID_IUnknown || riid == __uuidof(IFoo) ) {
                *object = this;
                this->AddRef(); 
            }
            if ( *object == NULL ) return E_NOINTERFACE;
            return NO_ERROR;
        }



What we have done now is to say that when anybody requests either an IUnknown or an IFoo interface, we provide them with a pointer to this instance. Finally, we modify the CreateFooObject to request the IFoo interface rather than the IUnknown interface, as that makes more sense.

#include <windows.h>

interface __declspec(uuid("E2F7AF75-C6CF-4A80-8968-793CB864BCD9")) IFoo : public IUnknown {
    public:
        virtual HRESULT foobar() = 0;
};


class foo : public IFoo {
    private:
        DWORD reference_count;
    public:
        foo() : reference_count(0) { }
        HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, PVOID * object) {
            *object = NULL;
            if ( riid == IID_IUnknown || riid == __uuidof(IFoo) ) {
                *object = this;
                this->AddRef(); 
            }
            if ( *object == NULL ) return E_NOINTERFACE;
            return NO_ERROR;
        }
        ULONG STDMETHODCALLTYPE AddRef() { return reference_count++; }
        ULONG STDMETHODCALLTYPE Release() { 
            if ( --reference_count == 0 ) delete this;
            return reference_count; 
        }
        HRESULT STDMETHODCALLTYPE foobar() { return NO_ERROR; }
};

HRESULT CreateFooObject(IFoo** foo_object) {
    foo * This = new foo;
    return This->QueryInterface(__uuidof(IFoo), (PVOID *)foo_object);
}

int main() {
    IFoo * foo_object;
    HRESULT hr = CreateFooObject(&foo_object);
    foo_object->foobar();
    foo_object->Release();
    foo_object = NULL;
    return 0;
}



Conclusion

This tutorial will most definitely have created more questions than answers, but please bear with me. The next tutorial will look at class factories and the role they play in the COM story. This tutorial may be found here.

This post has been edited by Martyn.Rae: 04 April 2010 - 01:31 PM


Is This A Good Question/Topic? 0
  • +

Page 1 of 1