ms_com3

Upload: shrihn

Post on 14-Apr-2018

218 views

Category:

Documents


0 download

TRANSCRIPT

  • 7/30/2019 MS_COM3

    1/18

    Chapter 3: Creating a COM ObjectThis chapter introduces many of the programming fundamentals of COM.

    To see an animation that introduces the COM activation process, click this icon.

    (CD-ROM plays the animation, "COM Activation.")

    In this chapter, you will learn about the fundamentals of COM and how to create a basic COM object.

    ObjectivesAfter completing this chapter, you will be able to:

    Explain the concept of immutability in relation to interfaces.

    Describe the purpose of the IUnknown interface.

    List and describe the three methods of the IUnknown interface.

    Explain reference counting and its implications.

    Explain, generate, and use a GUID (globally unique identifier).

    Derive a C++ class from an interface and implement all required methods. Create a class factory for a COM object.

    Explain the difference between a COM object and a COM object server.

    Write code for the WinMain function of a simple COM object.

    Register a COM object, which includes:

    y Adding code to the registry functions for COM to support self-registration.

    y Creating a registry (.reg) file and manually registering an object.

  • 7/30/2019 MS_COM3

    2/18

    24 Chapter 3: Creating a COM Object

    Notes

    COM InterfacesIn this section, you will learn more about how a COM object exposes its functionality through interfaces.You will also learn about the implications of exposing functionality through interfaces and how to uniquelyidentify them.

    This section includes the following topics:

    Interface Fundamentals

    Identifying Interfaces

    Using GUIDs in C++

    Interface FundamentalsInterfaces are the fundamental way of exposing the functionality of a COM object. An interface is acollection of methods that provide a service. This collection is a logical definition that must remainimmutable.

    Interfaces are a Logical DefinitionAlthough an interface specifies a service, it is only a logical definition. The physical implementation of aninterface is not part of the interface definition.

    The only part of the definition that has a bearing on physical implementation is the definition of the VTBLordering for the methods on an interface.

    Interfaces are ImmutableCOM interfaces go beyond simply specifying the methods of an interface and their signatures. Once aninterface is published, the signature of an interface can never change, nor can the VTBL size or methodordering be changed.

    The documented semantics of an interface are also immutable. If you implement an interface, the methodsmust behave as specified, without any side effects. There are no interface-conformance tests; however,breaking the immutability of interfaces is likely to make your interface unusable by others.

  • 7/30/2019 MS_COM3

    3/18

    Chapter 3: Creating a COM Object 25

    Notes

    Interface NotationThe following illustration shows a typical representation of a COM object and interfaces:

    Each COM object has an IUnknown, which by convention is shown extending from the top of a COMobject.

    The IUnknown interface will be discussed later in this chapter.

    Identifying InterfacesAn interface provides a set of services, so you need some way to uniquely identify the interface.

    Readable text strings cannot provide this uniqueness because name collision can easily occur. Uniqueidentification of interfaces also requires a mechanism that can provide indirection so that moduledependencies in clients does not need to be hard coded.

    Fortunately, an algorithm devised by the Open Software Foundation has already solved the problem ofuniquely identifying interfaces. Their Distributed Computing Environment defines universally uniqueidentifiers (UUIDs) as 128-bit numbers, and provides an algorithm to generate them.

    For information about the Open Software Foundation, go to The Open Group Web site by clicking thisicon.

    (CD-ROM links to the Web site: www.osf.org.)

    In COM, UUIDs are called globally unique identifiers (GUIDs). COM uses GUIDs to name interfaces,classes, and libraries. GUIDs are statistically guaranteed to be unique, which means that they can be usedto eliminate name collisions.

    Using GUIDs in C++In C++, GUIDs are represented as structures, because C++ does not support a 128-bit data type.

  • 7/30/2019 MS_COM3

    4/18

    26 Chapter 3: Creating a COM Object

    Notes

    Using a GUIDThe following example code shows how to use the DEFINE_GUID macro to associate a class with aGUID, in this case a hexadecimal number:

    // {A4B4C132-E33E-11d0-890D-0020AFF661D0}

    DEFINE_GUID(CLSID_CSimple,

    0xa4b4c132, 0xe33e, 0x11d0, 0x89, 0xd, 0x0, 0x20, 0xaf, 0xf6, 0x61, 0xd0);

    To use the DEFINE_GUID macro, you must #include the initguid.h file in at least one of the modules thatuse the macro. Storage is allocated for the GUID only in the module in which the initguid.h file is included.In all other modules, the macro evaluates to an extern reference.

    A preferred method of using GUIDs is to use the GUID data type directly. The following example codeshows how to use the GUID data type to associate a class with a GUID:

    // {A4B4C132-E33E-11d0-890D-0020AFF661D0}

    [static] const GUID CLSID_CSimple =

    { 0xa4b4c132, 0xe33e, 0x11d0, { 0x89, 0xd, 0x0, 0x20, 0xaf, 0xf6, 0x61, 0xd0 }

    };

    Generating GUIDsVisual C++ provides the GUIDGEN application that enables you to generate GUIDs in a variety of formatsfor your code. The Win32 SDK provides UUIDGEN, a console utility program that can generate a largenumber of UUIDs in one operation.

    To see a demonstration of the GUIDGEN application, click this icon.

    (CD-ROM plays the demonstration, "Using GUIDGEN.")

    The IUnknown InterfaceIn this section, you will learn about the IUnknown interface, the interface from which all other interfaces inCOM are derived. You will learn about the functionality provided by the IUnknown interface.

    This section includes the following topics:

    Overview of IUnknown

    Reference Counting

    Discovering the Functionality of a COM Object

    Overview of IUnknownAll COM interfaces derive from the IUnknown interface. IUnknown has only three methods: AddRef,Release, and QueryInterface. These three methods control the lifetime of COM objects, and also enablea client to safely discover functionality of a COM object at run-time.

  • 7/30/2019 MS_COM3

    5/18

    Chapter 3: Creating a COM Object 27

    Notes

    The following example code shows the definition for the IUnknown interface:

    struct IUnknown

    {

    virtual HRESULT _stdcall QueryInterface (REFIID riid,

    void **ppv) = 0;

    virtual ULONG _stdcall AddRef() = 0;

    virtual ULONG _stdcall Release() = 0;};

    Note All methods of a COM interface must return an HRESULT, except the AddRef and Releasemethods, which return a ULONG.

    Reference CountingCOM does not automatically remove a COM object from memory when the COM object is no longer beingused. The COM object must provide for its removal programmatically based on its reference count.

    Each AddRef call increments, and each Release call decrements, a counter variable inside the COM

    object. When the count returns to zero, the interface no longer has any users, and is therefore free toremove itself from memory.

    To manage the reference count of a COM object's interfaces, use the AddRef and Release methods. Thegeneral rules for calling these methods are:

    Functions that return an interface pointer, either with their return value or with an out parameter, shouldalways call the AddRef method on that interface.

    When you assign an interface pointer to another interface pointer, call the AddRef method on theinterface.

    When a client is finished using an interface pointer, call the Release method on the interface.

    When an interface pointer is an in-out parameter, the function should call the Release method on theparameter before assigning a new interface pointer to that parameter. The function should then call theAddRef method on the new interface.

    You can also implement reference counting so that each reference to a COM object (not to an individualinterface) is counted. In this case, each AddRef and Release call delegates to a central implementationon the COM object. When the reference count reaches zero, Release frees the entire COM object.

    Note The return value for AddRef and Release are required by the COM specification to contain thecurrent reference count, but you should not depend on all interfaces meeting this requirement. Therefore,you cannot rely on the return values of these methods for debugging reference-counting bugs.

  • 7/30/2019 MS_COM3

    6/18

    28 Chapter 3: Creating a COM Object

    Notes

    Discovering the Functionality of a COM ObjectTo determine if a COM object supports a requested interface, use the QueryInterface method.

    If a COM object supports the requested interface, the interface pointer is placed in the location pointed toby the second argument of the QueryInterface method. QueryInterface calls the AddRef method andreturns S_OK.

    If a COM object does not support the requested interface, QueryInterface returns the E_NOINTERFACEerror.

    COM Objects and COM Object ServersThere is an important distinction between a COM object and the COM object server, the file that hosts theCOM object.

    A COM object is a unit of functionality that implements one or more interfaces. The interfaces expose thefunctionality of the COM object.

    A COM object server is either an executable (.exe) file or DLL that hosts and can create one or more COMobjects.

    To a client, it does not matter if the location of the COM object is in-process (DLL), out-of-process (.exefile), or remote (.exe file on a remote computer). The location of the COM object could also change withoutaffecting the client. Therefore, client code should never contain explicit references to specific COM objectservers.

    Creating a COM ObjectIn this section, you will learn how to create a COM object by using C++.

    This section includes the following topics:

    Deriving a C++ Class from an Interface

    Implementing the Methods of an Interface

    Controlling the Lifetime of a COM Object

    Deriving a C++ Class from an InterfaceThe first step in creating a COM object is to derive a C++ class from an interface.

    Because an interface is defined in C++ as a structure that contains only pure virtual functions, you canderive a class from an interface in the same manner as you would from a structure.

  • 7/30/2019 MS_COM3

    7/18

    Chapter 3: Creating a COM Object 29

    Notes

    The following example code shows how to derive a class from the IStream interface, which is an interfacepublished by Microsoft:

    class CSimple : public IStream

    {

    // IUnknown methods

    // IStream methods

    };

    The VTBL of the class that you derive is identical to that of the interface. The first piece of data in aninstance of a class that has virtual functions is the VTBL pointer. Therefore, the pointer is also the addressof the VPTR. Logically, it is a pointer to a pointer to a virtual function table, which is the definition of aninterface pointer. This concept is shown in the following illustration.

    Implementing the Methods of an InterfaceAll of the methods on COM interfaces return an HRESULT, except for the methods AddRef and Release,which return a ULONG.

    AddRef and Release MethodsThe following example code shows how to implement the AddRef method:

    ULONG _stdcall CSimpleObject::AddRef(void)

    {return ++m_cRef;

    }

    To prevent more than one thread from using the same variable simultaneously, use theInterlockedIncrement function to increment the reference count, as shown in the following example code:

    ULONG _stdcall CSimpleObject::AddRef(void)

    {

    return InterlockedIncrement(&m_cRef);

    }

  • 7/30/2019 MS_COM3

    8/18

    30 Chapter 3: Creating a COM Object

    Notes

    The following example code shows how to implement the Release method:

    ULONG _stdcall CSimpleObject::Release(void)

    {

    if (0 == --m_cRef)

    {

    delete this;

    return 0;}

    return m_cRef;

    }

    To prevent more than one thread from using the same variable simultaneously, use theInterlockedDecrement function to decrement the reference count, as shown in this example code:

    ULONG _stdcall CSimpleObject::Release(void)

    {

    if (0 == InterlockedDecrement(&m_cRef))

    {

    delete this;

    return 0;

    }return m_cRef;

    }

    QueryInterface MethodThe QueryInterface method checks to make sure that the incoming pointer to a pointer is not NULL.Then, it tests to see if the requested interface is a supported interface. If so, it typecasts the this pointer tothe appropriate interface pointer. Then the AddRef method is called before returning the pointer.

  • 7/30/2019 MS_COM3

    9/18

    Chapter 3: Creating a COM Object 31

    Notes

    To see sample code that shows an implementation of QueryInterface for an object that supportsIUnknown and IStream, click this icon.

    (CD-ROM displays the following sample code.)

    HRESULT _stdcall CSimpleObject::QueryInterface (REFIID riid,

    void **ppv)

    {

    if (NULL == ppv)return E_INVALIDARG;

    *ppv = NULL;

    if (IID_IStream == riid)

    *ppv = (IStream *) this;

    else

    if (IID_IUnknown == riid)

    *ppv = static_cast( this );

    if (*ppv)

    {

    AddRef();

    return S_OK;

    }

    return E_NOINTERFACE;

    }

    For more information about implementing QueryInterface, see Chapter 4: Implementing MultipleInterfaces.

    Other Methods of InterfacesYou should implement all other methods of any interface as specified in the interface documentation.

    The following example code shows the Read method of the IStream interface:

    HRESULT _stdcall CSimpleObject::Read(void *pv, ULONG cb, ULONG *pcbRead)

    {

    if (NULL == pv)

    return E_INVALIDARG;

    if (0 == cb)

    return S_FALSE;

    // your implementation here

    return S_OK;

    }

  • 7/30/2019 MS_COM3

    10/18

    32 Chapter 3: Creating a COM Object

    Notes

    Controlling the Lifetime of a COM ObjectIt is up to the creator of the COM object to determine exactly when a server shuts down and what methodshould be used to signal the server to shut down.

    The simplest strategy for controlling the lifetime of an object is for the object to maintain a centralreference count. When the reference count reaches zero, the object deletes itself. If a COM object server

    contains only one COM object, the COM object server can shut down when the reference count of theCOM object reaches zero.

    However, if a COM object server contains more than one COM object, the COM object server should shutdown only when the reference count of all of its COM objects reaches zero. There is not, however, anyrequirement that a server shut down when there are no active objects.

    Creating a COM Object ServerIn this section you will learn how to create a COM object server. You will also learn how to create anexecutable or DLL that can host and create COM objects.

    This section includes the following topics:

    Overview of Class Factories

    Implementing a Class Factory

    COM Object Servers as Executable Files

    COM Object Servers as DLLs

    Overview of Class FactoriesTo create a COM object server, you must provide a mechanism that enables a client to create an instanceof a COM object contained within your COM object server. Because COM must be language independent,any sort of dynamic allocation, such as having the client use operator new, is not an option. What isneeded is a class that wraps the operator new. COM refers to this wrapper class as the class factory.

    To enable a client to create an instance of a COM object contained within a COM object server, you createa class factory.

    You use the IClassFactory interface to enable object creation. In addition to the IUnknown interfacemethods, the IClassFactory interface has two methods: CreateInstance and LockServer.

    The CreateInstance method creates an uninitialized COM object of a specified CLSID. The LockServermethod keeps the COM object server in memory, and enhances performance if you create more than oneobject of the specified class.

  • 7/30/2019 MS_COM3

    11/18

    Chapter 3: Creating a COM Object 33

    Notes

    The following example code shows the definition of the IClassFactory interface:

    struct IClassFactory : public IUnknown

    {

    // IUnknown methods

    virtual HRESULT _stdcall QueryInterface (REFIID riid,

    void **ppv) = 0;

    virtual ULONG _stdcall AddRef() = 0;virtual ULONG _stdcall Release() = 0;

    // IClassFactory methods

    virtual HRESULT CreateInstance(IUnknown *pUnkOuter,

    REFIID riid, void **ppv) = 0;

    virtual HRESULT LockServer(BOOL bLock) = 0;

    };

    Implementing a Class FactoryTo implement a class factory in a COM object server, you first create a class that derives from theIClassFactory interface, and then implement the methods of the IUnknown and IClassFactory

    interfaces.

    To see sample code that shows the class declaration for a class factory, click this icon.

    (CD-ROM displays the following sample code.)

    class CSimpleClassFactory : public IClassFactory

    {

    public:

    // IUnknown methods

    HRESULT _stdcall QueryInterface (REFIID riid,

    void **ppv);

    ULONG _stdcall AddRef();

    ULONG _stdcall Release();

    // IClassFactory methods

    HRESULT _stdcall CreateInstance(IUnknown *pUnkOuter,REFIID riid, void **ppv);

    HRESULT _stdcall LockServer(IUnknown *pUnkOuter,

    REFIID riid, void **ppv);

    CSimpleClassFactory();

    ~CSimpleClassFactory();

    };

  • 7/30/2019 MS_COM3

    12/18

    34 Chapter 3: Creating a COM Object

    Notes

    To see sample code that shows the implementation of the CreateInstance method, click this icon.

    (CD-ROM displays the following sample code.)

    HRESULT _stdcall CSimpleClassFactory CreateInstance(

    IUnknown *pUnkOuter,

    REFIID riid, void **ppv)

    {

    if (NULL == ppv)return E_INVALIDARG;

    *ppv = NULL;

    if (0 != pUnkOuter)

    return CLASS_E_NOAGGREGATION;

    CSimple *pObj = new CSimple;

    if (!pObj)

    return E_OUTOFMEMORY;

    // Query the newly created object for the requested interface.

    HRESULT hr = pObj->QueryInterface(riid, ppv);

    // If the call fails, the object must be deleted

    if (FAILED(hr))

    delete pObj;

    return hr;

    }

    COM Object Servers as Executable FilesThe WinMain function is the entry point for all Win32-based Windows applications, including out-of-process COM object servers.

    You need to add code to the WinMain function to do the following:

    1. Initialize the COM libraries by using the CoInitialize function.

    This notifies the COM libraries that COM will be used on this thread. At this point, you could also re-

    register the COM object server to ensure that it has been properly registered.2. Register each class factory in the COM object server with the CoRegisterClassObject function.

    3. Enter the message loop as you would for any Windows application.

    This keeps the server in memory and provides an entry point to the application to service COMrequests. Only one client request can be serviced between calls to GetMessage. This approach tohandling messages will not be sufficient for high-performance COM object servers that support a heavyclient load.

    4. After exiting the message loop, call the CoRevokeClassObject function for each class factory that youregistered with the CoRegisterClassObject function in Step 2.

    5. To indicate that the thread no longer needs the COM libraries, call the CoUninitialize function.

  • 7/30/2019 MS_COM3

    13/18

    Chapter 3: Creating a COM Object 35

    Notes

    COM Object Servers as DLLsIf a COM object server is implemented in a DLL, it must export the following five functions:

    DllMain

    The entry point for all DLLs.

    DllCanUnloadNowDetermines if the DLL that implements the COM object server is in use. If it is not, the caller can safelyunload it.

    DllGetClassObject

    Retrieves the requested class factory from the DLL.

    DllRegisterServer

    Instructs an in-process server to create its registry entries for all classes supported by the COM objectserver module.

    DllUnregisterServer

    Instructs an in-process server to remove only those registry entries created with DllRegisterServer.

    Registering a COM Object ServerIn this section, you will learn how to register a COM object server.

    This section includes the following topics:

    Registering CLSIDs

    Using the .Reg File

    Registering CLSIDsRegistration of a COM object server is the process of making entries in the registry to expose the CLSIDsof all the COM objects that a COM object server can create.

    There are specific entries in the registry that should be made to allow a COM object to be created by usingthe CoCreateInstance function.

    Registry Requirements for COM ObjectsThe minimum entry in the registry for a COM object enables a client to locate the COM object. To do this,you create a subkey for the CLSID of the COM object under HKEY_CLASSES_ROOT\CLSID. Under theCLSID subkey, you must also specify a subkey with one of the four following values:

    InprocServer32

    InprocHandler32

    LocalServer32

    RemoteServerName

  • 7/30/2019 MS_COM3

    14/18

    36 Chapter 3: Creating a COM Object

    Notes

    The value of this subkey is the path of the server. The name of this subkey corresponds to the thirdargument of the CoCreateInstance function.

    For more information about minimum registry entries, see online Help in Microsoft Developer Studio.

    To see an illustration of the minimum requirements you need to make in the registry to properly register aCOM object, click this icon.

    (CD-ROM displays the following illustration.)

    To enable clients to use the ProgID of a COM object, rather than the less readable CLSID, create asubkey for the ProgID of the COM object under HKEY_CLASSES_ROOT. Under this ProgID, specifyanother subkey with the CLSID value.

    To see an illustration of the entries you need to make in the registry to enable clients to use the ProgID,click this icon.

    (CD-ROM displays the following illustration.)

    In the registry, you can add additional entries under HKEY_CLASSES_ROOT to specify interface and typelibrary information. For more information about adding entries to the system registry, see the online Help in

    Microsoft Developer Studio.

  • 7/30/2019 MS_COM3

    15/18

    Chapter 3: Creating a COM Object 37

    Notes

    Self RegistrationYou should ensure that your COM objects register themselves to avoid problems that can occur frommanual registration. You enable a COM object to self-register by adding a resource to the COM objectserver.

    For more information about using a resource to enable self-registration, see Registering an ATL COMObject in Chapter 5: Introduction to ATL.

    The method that the COM object server uses to access the registry information depends on whether it isan out-of-process or in-process COM object server.

    (CD-ROM displays the following tip.)

    COM object servers that are self-registering should update the VERSIONINFO resource. For moreinformation about the VERSIONINFO resource, see the online Help in Microsoft Developer Studio.

    Out-of-Process ServersThe convention used for self-registration of an out-of-process COM object server is for the WinMainfunction to search the command line for the switches /regserver or /unregserver, and then take the

    appropriate action. The COM object server is registered or unregistered by running the associatedexecutable file, and then providing the appropriate switch.

    In-Process ServersIn-process servers cannot register themselves because they are hosted in DLLs. Therefore, in-processCOM object servers must export the entry points DllRegisterServer and DllUnregisterServer. When youregister or unregister an in-process server with the system utility Regsvr32.exe, the utility calls one ofthese entry-point functions.

  • 7/30/2019 MS_COM3

    16/18

    38 Chapter 3: Creating a COM Object

    Notes

    Using the .Reg FileIf you want to register a COM object manually, use registry (.reg) files.

    The system recognizes .reg files as the native file type of the regedit.exe application. When you double-click an .reg file, its contents are merged with the registry. An .reg file contains fully qualified registry keysand values.

    Note Because .reg files are text files, they are prone to corruption and should not be relied on forproduction servers.

    The following code shows an example of an .reg file:

    REGEDIT

    HKEY_CLASSES_ROOT\Simple.Object\CLSID = {F845EE21-BB3F-11cf-8CDD-444553540000}

    HKEY_CLASSES_ROOT\CLSID\{F845EE21-BB3F-11cf-8CDD-444553540000} = Simple Object

    Server

    HKEY_CLASSES_ROOT\CLSID\{F845EE21-BB3F-11cf-8CDD-444553540000}\LocalServer32 =Labs\C03\Ex01\SimpleServer\Release\SimpleServer.exe

  • 7/30/2019 MS_COM3

    17/18

    Chapter 3: Creating a COM Object 39

    Notes

    Self-Check Questions1. Which one of the following lists the three methods of the IUnknown interface?

    A. QueryInterface, Add, and Release.

    B. GetInterface, Add, and Release.

    C. QueryInterface, AddRef, and Release.

    D. GetInterface, AddRef, and Release.

    2. Reference counting is best described as:

    A. The mechanism that COM uses to keep track of which clients havecreated instances of a COM object.

    B. The mechanism that COM uses to determine when a COM object canbe removed from memory.

    C. The mechanism that COM uses to determine if a COM object supportsa specified interface.

    D. The mechanism that COM uses to enable a client to create an instance

    of a COM object contained within a COM object server.

    3. A class factory is best described as:

    A. The mechanism that COM uses to keep track of which clients havecreated instances of a COM object.

    B. The mechanism that COM uses to determine when a COM object canbe removed from memory.

    C. The mechanism that COM uses to determine if a COM object supportsa specified interface.

    D. The mechanism that COM uses to enable a client to create an instanceof a COM object contained within a COM object server.

    4. Which one of the following lists the two methods of the IClassFactory interface?

    A. CreateInstance and LockServer.

    B. CoCreateInstance and LockServer.

    C. CreateInstance and CoInitialize.

    D. CoCreateInstance and CoInitialize.

  • 7/30/2019 MS_COM3

    18/18

    40 Chapter 3: Creating a COM Object

    Notes

    5. Which one of the following is the minimum requirement for registering a COM object server inthe registry?

    A. A key added under HKEY_CLASSES_ROOT with the name of theProgID of the COM object server. Also, a subkey added that containsthe value of the CLSID of the COM object.

    B. A subkey added under HKEY_CLASSES_ROOT\AppID with the name

    of the objects CLSID.C. A key added under HKEY_CLASSES_ROOT with the interface

    identifier (IID) of the COM object server's class factory.

    D. Under the CLSID, either the subkey LocalServer32 (out-of-process)added or the subkey InprocServer32 (in-process) added with the valueof the full path name of the COM object server.