Standard and Custom Interfaces

 < Free Open Study > 



COM is all about interfaces. The only way in which clients and servers can communicate is through COM interfaces. In the previous chapter, we examined interface-based programming from a C++ perspective. Each and every interface developed in that chapter (IDraw, IShapeEdit, and so forth) was developed from scratch to specify a possible behavior some class (C3DRect, C3DRectEx) may support. You discovered that C++ classes may support custom interfaces using standard multiple inheritance (MI), and through hand-rolled APIs we were able to retrieve interface pointers for the client. We defined an interface to be the following:

An interface is a collection of semantically related functions, which describe a single and unique behavior that may be supported by a class.

This same definition works almost perfectly in the world of COM. However, COM interfaces fall under two distinct categories. Under the hood, each type of interface is exactly the same: a collection of semantically related functions—no state, no implementation.

Furthermore, every COM interface (standard or custom) must ultimately derive from the core COM interface: IUnknown. We will examine IUnknown in detail in just a moment, but for now just regard this interface as the top-most node in any interface hierarchy which defines exactly three methods: QueryInterface(), AddRef(), and Release().

As any master node in a hierarchy, IUnknown provides basic COM-related functionality to all interface descendents, and thus all implementing classes. With this said, a COM interface may fall into one of two camps:

  1. Custom Interfaces: These would be interfaces developed to solve an individual programming problem. We may create custom interfaces as C++ abstract classes, and extend them through numerical versioning. Every custom interface must directly or indirectly derive from IUnknown. For example, IDraw would be an official COM interface if and only if it was specified as so:

    // Could also derive from another IUnknown derived interface as well. interface IDraw : public IUnknown      {      // Brings in pure virtual definitions of QueryInterface(), AddRef() and Release().      virtual void Draw() = 0; };

    The above code suggests that the vTable of IDraw is colored by the methods defined in IUnknown. This is very true indeed. Any COM-based interface will always have the methods of IUnknown as the first three members; thus, any class implementing a COM interface will always have to contend with these three methods of IUnknown. Be aware that in interface inheritance, you are not required to explicitly relist inherited pure virtual methods in the derived interface. You may choose to relist the inherited pure virtual methods of IUnknown in your IDraw interface if you wish to be extremely clear; however, the compiler could care less. The class implementing IDraw would (of course) need to provide definitions for all four virtual functions.

    Given that IUnknown is a mandatory requirement of any COM object, it holds a special place in COM notation. The IUnknown interface is always represented as the lollipop extending from the top of a COM class (see Figure 3-7).

    click to expand
    Figure 3-7: A COM object must support IUnknown.

  2. Standard Interfaces: These are predesigned and well-known interfaces useful in COM development (such as IUnknown). Every standard interface must also derive from IUnknown or another IUnknown-derived interface.

    Having a set of standard interfaces is useful, as they provide a large surface area of behaviors necessary for a component-based distributed architecture such as COM. In fact, what makes one COM-based technology different from another is the set of standard interfaces supported by the given object. ActiveX controls support a number of interfaces not found in automation servers. ActiveX documents have a number of interfaces not found in COM drag and drop. This does not mean all aforementioned COM technologies are mutually exclusive. As you learn more about the set of standard COM interfaces, you will see sets of interfaces providing rendering, persistence, and other standard behaviors that tend to be implemented by numerous COM technologies.

Understanding Interface Identifiers (IIDs)

Interfaces must be named. In the previous chapter, we created a custom enumeration called INTERFACEID, which served as a way to uniquely identify the interfaces used in our mini-system. This technique worked just fine for us, as the interfaces developed were only used in the context of a single C++ application. In real life, COM objects and their interfaces may be remoted around the world (quite literally). Simple numerical identifiers such as {0, 1, 2, 3} are bound to introduce name clashes (as would string names—how many developers out there think IDataBase sounds like a good name for an interface?).

In COM development, every interface (standard or custom) is tagged with a Globally Unique Identifier (GUID, pronounced goo-wid), which is guaranteed to mark an interface as an individual entity for all space and time. The GUID is a 128-bit number, generated based on a unique network address coupled with the exact time (down to 100 nanoseconds!) it was requested. When a GUID is referring to a COM interface, we call this number an interface identifier, or IID. COM uses the GUID to uniquely identify COM classes (CLSIDs), type libraries (LIBIDs), COM executables (AppIDs), and numerous other COM items. A GUID is, in reality, a four-field structure:

// The GUID is represented as a structure defined in <winnt.h>. typedef struct_GUID{     unsigned long Data1;                     unsigned short Data2;                     unsigned short Data3;                     unsigned char Data4[8];} GUID; 

The COM library provides a set of useful functions and types for working with GUIDs programmatically. Many COM library functions take GUIDs as parameters, and given that a 128-bit number might be a bit hefty to pass by value, a number of system defines (found in <wtypes.h>) are provided to pass these structures around by reference:

// <wtypes.h> lists a number of defines to work with GUIDs programmatically. #define REFGUID           const GUID * const #define REFIID            const IID * const #define REFCLSID          const CLSID * const

We are also given a set of COM library functions to do comparisons of two existing GUIDs:

// Defined in <objbase.h> BOOL IsEqualGUID(REFGUID g1, REFGUID g2); BOOL IsEqualIID(REFIID i1, REFIID i2); BOOL IsEqualCLSID(CLSID c1, CLSID c2);

Each function performs a memcmp() of the two structures, and returns a Boolean as the result of the comparison. Here is the implementation of IsEqualGUID:

// IsEqualGUID can be used to determine if two GUIDs are identical. BOOL IsEqualGUID(REFGUID rguid1, REFGUID rguid2) {      return !memcmp(&rguid1, &rguid2, sizeof(GUID)); }

Note 

An inlined version of IsEqualGUID() is also provided: InlineIsEqualGUID(). Use with care, as you know that inlined functions can bloat your code.

In addition to IsEqualGUID(), the COM library has overridden the C++ equality operator (==) and the not equal operator (!=), allowing you to compare two GUIDs as the following:

// We may also use == and != with two existing GUIDs, as // we have overloaded operators at our disposal. if(g1 == g2)     {...}      // GUIDs are the same, so do something. if(g1 != g2)     {...}      // GUIDs are different, so do something else.

The implementation of the == operator function calls IsEqualGUID(), while the != operator implementation simply leverages the == operator:

// The overloaded operators simply call IsEqualGUID() BOOL operator == (const GUID& guidOne, const GUID& guidOther) {      return IsEqualGUID(guidOne,guidOther); } BOOL operator != (const GUID& guidOne, const GUID& guidOther) {      return !(guidOne == guidOther); }

Creating Custom GUIDs

Every standard COM interface already has been assigned a unique IID. For example, IUnknown has the IID of {00000000-0000-0000-C000-000000000046}. When you develop custom interfaces in C++, the job of associating a new GUID to your interface is up to you. Visual Studio provides a helpful utility, guidgen.exe, which creates new GUIDs through a friendly user interface (Figure 3-8).

You will find guidgen.exe installed under your <drive>:\ Program Files\Microsoft Visual Studio\Common\Tools directory (provided you used the default install paths):

click to expand
Figure 3-8: The guidgen.exe utility.

Note 

For those among you who still love the DOS prompt, a command line version of this tool is also provided: uuidgen.exe. Run this tool from a command prompt with the -? switch for a full description of options.

Guidgen.exe defines four possible formats for a new GUID. The first format is an MFC-specific macro, which defines a GUID for an MFC-based class factory (we don't need that format here, so forget it even exists). Format 4 is helpful when writing Interface Definition Language (IDL) files (as we will see in the next chapter). Format 3 is what the DEFINE_GUID macro basically expands to, which leaves the final format, the DEFINE_GUID macro. This macro allows you to associate a human readable constant to the newly generated 128-bit number, much like our custom enumeration (INTERFACEID) from the previous chapter. When you paste the new GUID from the clipboard using the DEFINE_GUID format, you will see the following:

// {4B475690-DE06-11d2-AAF4-00A0C9312D57} DEFINE_GUID(<<name>>, 0x4b475690, 0xde06, 0x11d2, 0xaa, 0xf4, 0x0, 0xa0, 0xc9, 0x31, 0x2d, 0x57);

You select the <<name>> parameter, and type in the constant used to refer to the underlying GUID. For example, here is a GUID and defined constant for the IDraw interface:

// {4B475690-DE06-11d2-AAF4-00A0C9312D57} DEFINE_GUID(IID_IDraw, 0x4b475690, 0xde06, 0x11d2, 0xaa, 0xf4, 0x0, 0xa0, 0xc9, 0x31, 0x2d, 0x57);

Note 

By convention, the human readable interface constant assigned to a GUID is prefixed with IID_ followed by the actual name of the interface.

Now, even if another developer has created an interface named IDraw, this particular version will be unique in the COM universe, given the unique IID.

DEFINE_GUID expands to the following constant. Note how the long, word, and byte parameters are simply sent into the GUID structure fields. Also note that the first parameter to this macro becomes the name of the new structure:

// The DEFINE_GUID macro creates a new GUID structure of some name. #define DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \  EXTERN_C const GUID name \          = { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } }

In this chapter we will use the DEFINE_GUID macro when we create new GUIDs to identify our COM items. To do so, your projects must make a preprocessor include to the <initguid.h> header file in order to obtain the DEFINE_GUID macro definition. We will be creating interface header files in the upcoming labs, but just remember you can't use DEFINE_GUID without including <initguid.h> in your project.

On a final GUID-related note, the COM library function CoCreateGuid() may be used to create a GUID programmatically. Just send in a reference to a GUID, and it will stuff the fields:

// If you ever need to create a GUID on the fly... HRESULT CoCreateGuid(GUID *pguid);
Note 

Because the use of guidgen.exe is so common in COM and ATL programming, you may wish to create a custom menu item in the Visual C++ IDE to access it. To do so, select the Tools | Customize... menu selection and from the resulting dialog box, select the Tools tab. Type in a name for this custom menu item using the Menu Contents list box and map a path to guidgen.exe from the Command edit box (i.e., C:\Program Files\Microsoft Visual Studio\Common\Tools\Guidgen.exe). Select the Close button and examine your Tools menu. You will see your custom menu item is now visible and can be selected to run guidgen.exe.



 < Free Open Study > 



Developer's Workshop to COM and ATL 3.0
Developers Workshop to COM and ATL 3.0
ISBN: 1556227043
EAN: 2147483647
Year: 2000
Pages: 171

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net