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

### Working with DirectX COM Objects

Now that you have an idea what DirectX is and how COM works, let’s take a closer look at how they actually work together. Like I said, there are a number of COM objects that make up DirectX. These COM objects are contained within your system as DLLs when you load the run-time version of DirectX. When you run a third-party DirectX game, what happens is that one or more of these DLLs are loaded by the DirectX application, and then interfaces are requested and the methods (functions) of the interfaces are used to get the work done. That’s the run-time side of things.

The compile-time angle is a little different. The designers of DirectX knew that they were dealing with us game programmers, and assumed that most of us hate Windows programming—very true. Alas, they knew that they better keep the COM stuff to a minimum, or else game programmers would really hate using DirectX. Thus, 90% of the DirectX COM objects are wrapped in nice little function calls that take care of the COM stuff. So, you don’t have to call `CoCreateInstance()`, do COM initialization, and stuff like that. However, you may have to query for a new interface with `QueryInterface()`, but we’ll get to that in a bit. The point is, DirectX really tries to hide the tedium of working with COM from you so you can work with the core functionality of DirectX.

With all that said, to compile a DirectX program, you must include a number of import libraries that have the COM wrappers within them so you can make calls to DirectX using those wrapper functions to create the COM objects. For the most part, the libraries you need are

    DDRAW.LIB

    DSOUND.LIB

    DINPUT.LIB

    DSETUP.LIB

    DPLAYX.LIB

    D3DIM.LIB

    D3DRM.LIB

But remember, these libraries don’t contain the COM objects themselves. These are only wrapper libraries and hooks that make calls to load the DirectX DLLs themselves, which are the COM objects. Finally, when you do call one of the DirectX COM objects, the result is usually just an interface pointer. This is where are the action occurs. Just like in the example of `DEMO5_1.CPP`, once you have the interface pointer, you’re free to make function calls—or more correctly in C++ speak, method calls. However, if you’re a C programmer, take a quick look at the next section if you feel uncomfortable with function pointers. If you’re a C++ programmer, you can skip ahead to the next section if you want.

#### COM and Function Pointers

Once you have created a COM object and retrieved an interface pointer, what you really have is a VTABLE (Virtual Function Table) pointer. Take a look at Figure 5.7 to see this graphically. Virtual functions are used so that you can code with function calls that are not bound until run-time. This is the key to COM and virtual functions. In essence, C++ has this built in, but you can do the same thing with C by using straight function pointers.

Figure 5.7 Virtual Function Table architecture.

A function pointer is a type of pointer used to make calls to a function. But instead of the function being hard-bound to some code, you can move it around as long as the prototype of the function pointer is the same as the function(s) you point it to. For example, say that you want to write a graphics driver function to plot a pixel on the screen. But also suppose that you have dozens of different video cards to support and they all work differently, as shown in Figure 5.8.

Figure 5.8 Software design needed to support different video cards.

You want to call the plot pixel function the same way for all these video cards, but the internal code is different depending on what card is plugged in. Here’s a typical C programmer’s solution:

    int SetPixel(int x, int y, int color, int card)

    {

    // what video card do we have?

    switch(card)

          {

          case ATI:    { /* hardware specific code */ } break;

          case VOODOO: { /* hardware specific code */ } break;

          case SIII:   { /* hardware specific code */ } break;

          .

          .

          .

          default:     { /* standard VGA code */ } break;

          } // end switch

    // return success

    return(1);

    } // end SetPixel

Do you see the problem with this? First, the switch statement sucks. It’s slow, long, prone to errors, and you might break the function while adding support for another card. A better solution for straight C is to use function pointers like this:

    // function pointer declaration, weird huh?

    int (* SetPixel)(int x, int y, int color);

    // now here’s all our set pixel functions

    int SetPixel_ATI(int x, int y, int color)

    {

    // code for ATI

    } // end SetPixel_ATI

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

    int SetPixel_VOODOO(int x, int y, int color)

    {

    // code for VOODOO

    } // end SetPixel_VOODOO

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

    int SetPixel_SIII(int x, int y, int color)

    {

    // code for SIII

    } // end SetPixel_SIII

Now you’re ready to rock. When the system starts up, it checks what kind of card is installed and then, once and only once, sets the generic function pointer to point to the correct card’s function. For example, if you wanted `SetPixel()` to point to the ATI version, you would code it like this:

    // assigning a function pointer

    SetPixel = SetPixel_ATI;

Isn’t that easy? Figure 5.9 shows what this looks like graphically.

Figure 5.9 Using function pointers to enable different code blocks.

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.