COM Basics

COM Basics

During the 1990s, as operating systems and application programs grew progressively more complex, software architects struggled with the need to produce reusable code objects that could, through clearly defined interfaces, be reused throughout an operating system or application program. Such objects could simplify the design of programs dramatically. Rather than rely on native functionality, which could dramatically increase the complexity of a program, the programmer could call on these reusable code objects as needed.

For example, a word processor and an e-mail client both need access to a spelling checker so why write two spelling checkers when a reusable code object could be invoked by both programs when needed? In the ideal case, nothing would need to be known about the reusable code object other than its name. Once the programmer included this ideal object, the program would be able to query it to determine its properties. Like a normal object, this object would have data and methods, both of which would be accessible (if public) to the invoking program. In short, the ideal reusable code object would act just like code written by the programmer.

By the mid-1990s, Microsoft had introduced COM, its version of the reusable code object. Although the first generations of COM were somewhat rough in their design, nearly a decade of refinement has produced a level of functionality that begins to approach the ideal of the reusable code object. A COM object, like a C++ object, has properties that can be inspected, methods to be invoked, and interfaces that illustrate characteristics inherited from base classes. The creator of a COM object can choose to hide or reveal any of these qualities, producing an object that is both easy to manage and easy to use.

Naming and GUIDs

As in the case of the ideal reusable code object, the only thing you need to know about a COM object is its name. However, this name isn t a string of Roman characters; it s a globally unique identifier (GUID), a string of hexadecimal numbers, in the format of 32 bits 16 bits 16 bits 16 bits 44 bits. The GUID is guaranteed to be unique, so each COM object has a unique name. (This is true only for COM objects you ve created yourself if you follow Microsoft s rules for the creation of new GUIDs. Those details are available through the developer support section of Microsoft s Web site.) Fortunately, you don t have to remember these meaningless strings of numbers; each COM object used by DirectShow has been given a C++ defined name, also known as a class ID. The class ID is English-readable and easy to understand. For example, the COM object that represents a filter graph has the class ID CLSID_FilterGraph, which represents the GUID e436ebb8-542f-11ce-9f53-0020af0ba770 (and thank goodness for that). The class ID provides the symbolic name that you ll use to instantiate a COM object.

Initializing and Releasing COM

Before COM can be used within a DirectShow application, the COM facilities must be initialized. (If you have multiple execution threads in your application, each thread must be initialized separately.) To initialize COM, add the following line of source code to the initialization routine of the application:

CoInitializeEx(NULL, COINIT_APARTMENTTHREADED) //Initializes COM

After COM has been initialized, calls to the COM libraries can be made in any desired fashion. Before the application terminates its execution, COM must be shut down again. (Failure to shut down COM could result in execution errors when another program attempts to use COM services .) To release COM services, add this line of code to the application s cleanup code:

CoUninitialize(); // Releases COM

No calls to the COM services can be made after COM has been uninitialized.

Creating an Instance of a COM Object

Once COM services have been initialized in DirectShow, you will likely make a number of COM invocations to create instances of COM objects. These calls will create various objects needed by the application, such as filters. One of the first COM invocations in any DirectShow application will generally be a call to create the Filter Graph Manager, an object that handles the internal details of the filter graph. (The filter graph isn t an object per se, but a logical construction consisting of several COM objects working closely together.)

The COM routine CoCreateInstance is used to create COM objects. In the case of the Filter Graph Manager, the code to create it might look like this:

IGraphBuilder *graphBuilder = NULL; // Pointer to created object HRESULT hr = CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, IID_IGraphBuilder, (void **)&pGraphBuilder);

The CoCreateInstance call takes five arguments, beginning with a class ID in this case CLSID_FilterGraph which requests that a COM object representative of a filter graph (really, a Filter Graph Manager) be created. The NULL parameter indicates that this is not an aggregate object, which will be the case in any DirectShow application. The value CLSCTX_INPROC_SERVER indicates the that the COM object is being loaded from an in-process (local to your application) DLL. This value is always present in this parameter.

The next argument is an interface ID, which informs COM of the unique interface being requested by the caller. In this case, the value is IID_IGraphBuilder, which means that you will be retrieving the object s IGraphBuilder interface, which has methods for building filter graphs. Later, you ll need to use another interface on the same object, IMediaControl, which provides methods to start, stop, and pause the graph. The pointer address is returned in the last parameter. This pointer is cast as void**, a generic pointer to a pointer, because the function could return a pointer to any number of objects.

Nearly every COM invocation returns a status code of some sort or another; this code should always be examined for error values, using the macros SUCCEEDED and FAILED to test for success or failure. A COM call that generates an error indicates either a logical error in the program or some failure of the operating system to fulfill a request, perhaps because resources already in use or as yet uninitialized have been requested by the program.

When you re through with a COM interface, you need to invoke its Release method so that the object will know how to delete itself at the appropriate time. For the preceding code fragment, this method might look like this:

pGraphBuilder->Release(); // Release the object pGraphBuilder = NULL; // And set it to NULL

If you fail to release COM interfaces, objects will not get deleted and you ll clutter up your memory, suffer a performance hit, and possibly confuse the operating system into thinking that resources are being used after you ve finished with them. So make sure you clean up after your COM invocations.

Querying Interfaces in COM Objects

After an object has been instantiated through a COM call, a DirectShow application will often need access to additional interfaces on the object. For example, if an application programmer wants to have control over the execution of the filter graph, a pointer to the Filter Graph Manager s IMediaControl interface will have to be acquired. It s the same object being manipulated in either case, but each interface presents unique methods and properties suited for a particular task.

To acquire this interface, you need to send a query (request) to the object using any of its interfaces that you have already obtained. In this case, we already have its IGraphBuilder interface, so we ll use that interface. If we assume that the code fragment in the previous section has already executed successfully, that call might look like this:

IMediaControl *pMediaControl = NULL; // Store pointer to interface hr = pGraphBuilder->QueryInterface(IID_MediaControl, (void**)&pMediaControl);

The QueryInterface method takes two parameters. The first parameter is the interface ID (a GUID) for the requested interface. In this case, the interface ID references the IMediaControl interface. The second parameter is a pointer to a storage location for the returned interface. Once again, an error code will be returned if the query fails.

Using COM Objects

For the most part, objects instantiated through calls to CoCreateInstance behave just as a standard, well-designed C++ object would. Once it s been instantiated, a COM object can be treated much like any other C++ object that has been created on the heap. It must be released when it s no longer needed, and it provides a portable container for properties and methods that will work across any application in the operating system s environment. Every interface on a COM object inherits from the IUnknown interface, which, in addition to QueryInterface, has two other methods that control the object s lifetime. Each time a COM object returns any of its interfaces to a client (such as your application) through the initial call to CoCreateInstance or later calls to QueryInterface, it calls its own AddRef method to increment its reference count. When a client is finished with an interface, it must call Release, and the COM object decrements its reference count by one. When the count reaches zero, meaning there are no outstanding interface pointers, the object deletes itself. That is why failure to call Release after you finish with an interface results in memory leaks.

All DirectShow filters are COM objects including those you create for yourself so when we get into the subject of writing your own filters, we ll cover the internal construction of COM objects in much greater detail.



Programming Microsoft DirectShow for Digital Video and Television
Programming Microsoft DirectShow for Digital Video and Television (Pro-Developer)
ISBN: 0735618216
EAN: 2147483647
Year: 2002
Pages: 108
Authors: Mark D. Pesce

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