- Table of Contents
- Copyright
- About the Authors
- About the Contributors
- Acknowledgments
- Tell Us What You Think!
- Introduction
- How to Use This Book
- What You Need to Use This Book
- What's New in Visual C++ 6.0
- Contacting the Main Author
- Part I: Introduction
- Chapter 1. The Visual C++ 6.0 Environment
- Part II: MFC Programming
- Chapter 2. MFC Class Library Overview
- Chapter 3. MFC Message Handling Mechanism
- Chapter 4. The Document View Architecture
- Chapter 5. Creating and Using Dialog Boxes
- Chapter 6. Working with Device Contexts and GDI Objects
- Chapter 7. Creating and Using Property Sheets
- Chapter 8. Working with the File System
- Chapter 9. Using Serialization with File and Archive Objects
- Part III: Internet Programming with MFC
- Chapter 10. MFC and the Internet Server API (ISAPI)
- Chapter 11. The WinInet API
- Chapter 12. MFC HTML Support
- Part IV: Advanced Programming Topics
- Chapter 13. Using the Standard C++ Library
- Chapter 14. Error Detection and Exception Handling Techniques
- Chapter 15. Debugging and Profiling Strategies
- Chapter 16. Multithreading
- Chapter 17. Using Scripting and Other Tools to Automate the Visual C++ IDE
- Part V: Database Programming
- Chapter 18. Creating Custom AppWizards
- Chapter 19. Database Overview
- Chapter 20. ODBC Programming
- Chapter 21. MFC Database Classes
- Chapter 22. Using OLE DB
- Chapter 23. Programming with ADO
- Part VI: MFC Support for COM and ActiveX
- Chapter 24. Overview of COM and Active Technologies
- COM, OLE, and Active Technology History in a Nutshell
- COM and OLE from the Eyes of the End User
- COM, OLE, and Active Technologies from a Programmer's View
- Evolving OLE with Active Technologies
- New Active Technologies
- Summary
- Chapter 25. Active Documents
- Chapter 26. Active Containers
- Chapter 27. Active Servers
- Chapter 28. ActiveX Controls
- Part VII: Using the Active Template Library
- Chapter 29. ATL Architecture
- Chapter 30. Creating COM Objects Using ATL
- Chapter 31. Creating ActiveX Controls Using ATL
- Chapter 32. Using ATL to Create MTS and COM+ Components
- Part VIII: Finishing Touches
- Chapter 33. Adding Windows Help
- Part IX: Appendix
COM, OLE, and Active Technologies from a Programmer's View
This section presents COM, OLE, and Active Technologies from a programmatic view. For each service, you will be given a description of the technology and a programmer's view of the interfaces to these technologies. In addition, you will learn the MFC classes that support each technology. Pay particular attention to understanding what each topic does and where it fits into the architecture. Some of the technologies will be discussed in detail in later chapters.
Notice these are the same technologies the end user sees. However, the end user's view is a visual one, and the programmer's view is of a set of interfaces that must be mastered to provide the visual representation the end user sees. These sections are intended to give you an overview of Microsoft's entire component architecture. The specifics of implementation are left to later chapters. You are going to see, though, that Visual C++ and MFC do a lot of the work for you. The MFC implementation of COM, OLE, and the other Active Technologies is a kinder, gentler implementation.
Component Object Model (COM)
When Microsoft designed OLE, it was designed with object-oriented programming in mind. COM objects are much like instantiated C++ classes or an ADA package. In fact, COM was designed with C++ programmers in mind. It supports encapsulation, polymorphism, and reusability. However, COM was also designed to be compatible at the binary level and therefore has differences from a C++ object. As a programmer, you are aware that compiled programming languages such as C, C++, PASCAL, and ADA are machine-dependent. As a binary object, a COM object concerns itself with how it interfaces with other objects. When not used in the environment of its creator, an interface is exposed that can be seen in the non-native environment. It can be seen because it is a binary object and therefore not machine-dependent. This does not require the host environment or an interacting object to know anything about the COM object. When the object is created in the womb of its mother application, COM does not concern itself with how that object interacts within it. This interaction is between the mother application and the child object. When the object interacts with the rest of the world, however, COM is concerned about how to interface with that object. It is important to note that COM is not a programming language; it is a binary standard that enables software components to interact with each other as objects. COM is not specific to any particular programming language. COM can work with any language that can support the binary layout of a COM object. It is a programming model to facilitate the programmability of this standard.
COM objects consist of two types of items: properties and methods. Properties are the data members, and methods are member functions. COM objects each have a common interface. No matter what they do, COM objects all have to implement the IUnknown interface. This interface is the main interface for all others and is the base class from which all other COM interfaces are derived. The IUnknown interface has the following member functions:
- ULONG AddRef(void)
- ULONG Release(void)
- HRESULT QueryInterface(REFIID id, void **ipv)
Each object implements a vtable. A vtable is nothing more than an array of pointers to member functions implemented in the object (see Figure 24.6). This vtable is shared between all the instances of the object also maintaining the private data of each object. A client application evokes an instance of the interface and gets a pointer to a pointer that points to the vtable. Each time a new interface to the object is instantiated, the reference count of objects is incremented with AddRef(). Conversely, each time a reference is destroyed, the reference counter is decremented with Release(). Once the reference count is zero, the object can be destroyed. In order to see what interfaces an object supports, you can use QueryInterface().
Figure 24.6 This interface maps into a vtable.
COM objects are never directly accessed. COM objects are always accessed through a pointer to an interface exposed by the object. The QueryInterface(REFIID riid, void **ipv) function takes a reference to an interface identifier (riid) and a void pointer. The REFIID is a 128-bit unique ID that identifies the interface you are retrieving. Notice the double indirection on the pointer: **ipv. The ipv pointer is where the pointer to the interface you are trying to retrieve is stored. Consider this code fragment:
IAnyInterface* pAny = NULL;
HRESULT hr = pUnknown->QueryInterface(IID_IAnyInterface, (void**)&pAny);
if(SUCCEEDED(hr))
{
pAny->DoAnyObjectWork();
pAny->Release();
}
pUnknown is a pointer to the object's IUnknown interface. DoAnyObjectWork() is the member function you want to use to perform some work. You access that function through the pointer to that object's interface pAny.
Visual C++ and MFC encapsulates this IUnknown implementation through the use of interface maps, which are much the same as the message maps used to map the windows messages. It is a much easier implementation to understand. Visual C++ and MFC encapsulate much of the work involved in this through the wizards implemented in Visual C++ and the OLE classes implemented in the MFC Class Library. You will learn more about the Visual C++ and MFC implementations in Chapters 25–33.
Structured Storage
Most computing platforms today have different file systems, making sharing data a very difficult task. In addition, these file systems arose during the mainframe days when only a single application was able to update and in some cases access that data at any one time. COM is built with interoperability and integration between applications on dissimilar platforms in mind. In order to accomplish this, COM needs to have multiple applications write data to the same file on the underlying file system. Structured Storage addresses this need.
Structured Storage is a file system within a file. Think of it as a hierarchical tree of storages and streams. Within this tree, each node has one and only one parent, but each node may have from zero to many children. Think of it as like the Windows 95 Explorer. The folders are the storage nodes, and the files are the streams. Structured Storage provides an organization chart of data within a file, as seen in Figure 24.7. In addition, this organization of data is not limited to files, but includes memory and databases.
Figure 24.7 Structured Storage is a hierarchical tree of storages and streams.
Stream objects contain data, much like files in a traditional file system. This data can be either native data or data from other outside objects. Storage objects are compatible at the binary level; thus, in theory, they are compatible across platforms that support COM and OLE. However, you know that there are minute differences between the various platforms. Notice in Figure 24.7 the tree of the Structured Storage object. The definition of the tree is dependent on how the object's creator defined the storage of the object.
Structured Storage objects are manipulated using the following OLE interfaces:
- IPersistStorage
- IStorage
- IStream
IStorage,as the name implies, manipulates storage objects and IStream manipulates streams. Rarely would you want to manipulate stream or storage objects individually. More than likely, you would want to manipulate the Persistent Storage object with the IPersistStorage. Persistent Storage is data that will continue to exist even after an object is destroyed—for example, if you want to allow the user to define the color of an object such as a text label. You would persistently store that object's foreground and background colors. The next time the object was created you could read in from Persistent Storage the colors previously chosen by the end user. You could then apply those attributes to the object and thus maintain the user's preferences. IPersistStorage enables you to do this by performing the following operations:
- IsDirty
- InitNew
- Load
- Save
- SaveCompleted
- HandsOffStorage
A great way to see what Structured Storage looks like is with a utility that comes with Visual C++ 6 called DocFile Viewer. DocFile Viewer is added automatically to the Windows Start menu, in the Visual Studio 6.0 Tools folder. DocFile Viewer enables you to look at a compound file, also known as an OLE document. Compound files used for OLE documents are the most common implementation of Structured storage. Figure 24.8 shows an example of DocFile Viewer. (This is the Word document with an embedded Visio drawing object shown in Figure 24.2.)
Figure 24.8 DocFile Viewer shows the hierarchical tree of a Structured Storage object.
If you double-click a stream object, you can see its binary contents (see Figure 24.9).
Figure 24.9 The binary contents of a stream object.
MFC provides an encapsulation of IPersistStorage and IStorage through an easy-to-use class called COleDocument. In addition, to aid in the manipulation of storages and streams, MFC provides COleStreamFile. OLE documents and their extension Active documents will be covered in Chapter 25, "Active Documents." It is important to note that compound documents are not only used for OLE documents and Active documents, but any file type that uses Structured Storage to store its data.
Monikers (Persistent Naming)
Monikers are a way of referencing a piece of data or object in an object-based system such as COM. The original use for monikers was in OLE linking. When an object is linked, a moniker is stored that knows how to get to that native data. For example, if you link a sound file into a Word document, the WAV file is not stored natively in that document. A moniker is created that can intelligently find the WAV file object. Think of a moniker as a map to where X marks the spot. However, a moniker is more than just a name—a moniker is a COM object.
To use a moniker to locate and bind to an object, you must use the IMoniker interface and call IMoniker::BindToObject. By using the intelligent persistent name of that object, the IMoniker interface negotiates the location of that object and returns a pointer to the interface of that object's type. If the COM object is currently running, the moniker will return a pointer to the currently running object. If the COM object is inactive, the moniker handles activating the object, and returning a pointer to the specific instance named by the moniker. The moniker itself can then be released. Think of it as similar to de-referencing a pointer in C or C++ to locate a piece of data. Remember that monikers are persistent. IMoniker is derived from IPersistStream, and thus it can serialize itself into a stream. This gives it persistence. There are eight basic types of monikers:
- File monikers
- Item monikers
- Anti-monikers
- Pointer monikers
- Class monikers
- Asynchronous monikers
- URL monikers
- Composite monikers
File Monikers
File monikers store a filename persistently. In binding the text filename to an object that has been persisted to a file, a pointer to the activated object's interface is returned so that you can manipulate that object.
Item Monikers
Item monikers point to a specific place inside an instance of a COM object, such as a paragraph or a portion of an embedded video. An item moniker is always used in a composite moniker. For example, an item moniker is often composed with a file moniker to form a composite moniker that refers to a specific item within a file.
Anti-Monikers
An anti-moniker is the inverse of another moniker. When an anti-moniker is applied to a moniker, the two monikers cancel each other; this operation is often useful when maintaining composite monikers.
Pointer Monikers
Pointer monikers simply bind to COM objects that are already running. This type of moniker is useful when a moniker is required, but the COM object isn't persistent and can't be passed in an inactive state. In such a case, you can instantiate the object, and create a pointer moniker.
Class Monikers
Class monikers are used to bind to instances of class objects. A class object is a COM object used to create instances of COM objects. You typically use a class moniker as part of a composite moniker.
Asynchronous Monikers
Asynchronous monikers carry out the BindToObject operation asynchronously. Most other monikers bind synchronously, but in a network or Internet setting, these operations can take a while to complete. Asynchronous monikers enable binding operations to take place more efficiently in these settings.
URL Monikers
A URL moniker is an asynchronous moniker that enables binding to a file on the Internet or corporate intranet.
Composite Monikers
A composite moniker is an ordered collection of monikers. Taken as a whole, a composite moniker refers to a specific COM object. Each component of a composite moniker is resolved in turn in order to bind to the desired object. For example, a composite moniker might refer to a particular chart located on a particular worksheet in an Excel document. However, it is not necessary for the root of a composite moniker to be a file moniker—you are limited only by your imagination when composing monikers.
MFC Encapsulation of Monikers
In the previous section on Structured storage, you learned that MFC has a class called COleStreamFile. The purpose of this class is to encapsulate the functionality of IStream to provide access to the streams of data in a compound file. Derived from COleStreamFile is CMonikerFile. CMonikerFile is a class that encapsulates the functionality of monikers provided in the IMoniker interface. This class gives the ability to gain access to IStreams named by IMoniker. It is important to note that this class does not encapsulate the entire IMoniker interface. It provides the ability to work with the streams of a compound file. So, if you wish to bind to storage or an object, you have to implement the IMoniker interface directly. This means you will not be able to use MFC directly to implement all the moniker types stated previously.
Uniform Data Transfer (UDT)
OLE applications use a technology known as Uniform Data Transfer, or UDT, to exchange data. Uniform data transfer is a much more flexible way of exchanging data than the traditional Windows clipboard techniques. The primary interface used with UDT is IDataObject, and it's used in three major areas:
- OLE Clipboard
- OLE drag and drop
- Linking and embedding
COM objects that are data transfer sources expose the IDataObject interface and are known as data objects. The IDataObject interface makes use of two data describing structures:
- FORMATETC, which describes the data to be transferred
- STGMEDIUM, which describes the current location of the data
OLE Clipboard
The system clipboard is a system-level service used for interprocess communications. Because it is a system-level service, all applications have access to it. The Windows clipboard works great when transferring small pieces of data, but it is inefficient when transferring large objects. When transferring large bitmaps or multimedia clips, these objects must be loaded from disk, copied to global memory for the clipboard, and transferred to the other program. In low memory conditions, the same data may exist in several places simultaneously:
- In the original source file
- In the global memory (possibly swapped to disk)
- In the receiving program, which may copy the data to disk, copy it to local memory, or both
This is a poor use of system resources and leads to very poor performance. In fact, as the system begins to run low on resources, the traditional clipboard mechanism breaks down even more, as global memory is swapped to and from the hard drive.
The OLE clipboard uses UDT as its underlying transport mechanism. The OLE clipboard allows you to describe the data to be transferred, rather than passing the data directly in a global memory handle.
With an IDataObject pointer, you can use the function OleSetClipboard() to take a cut or copied object and expose this object to all processes through the OLE clipboard. Likewise, when you want to paste data from the OLE clipboard, you can use your IDataObject pointer to use the OleGetClipboard() function. This is a very powerful mechanism because it maintains the integrity of the object as a whole, enabling you to move complex object data types between applications.
Visual C++ and MFC provide access to the OLE clipboard through the use of member functions in the classes CWnd and COleClientItem.
Drag and Drop
Drag and drop is a method by which the user can select and move objects within an application and between applications. UDT is used to perform drag and drop actions. On the selection of the object, the source application packages the object and uses an IDataObject pointer to call DoDragDrop(). The source uses the IDropSource interface, which yields a pointer to its implementation. This pointer is passed to DoDragDrop(). The source controls the mouse cursors and handles the object in case of a cancellation.
Once the user brings the dragged object to its new client location or target, the client application evokes the IDropTarget interface. With the pointer to the IDropTarget, the client application tracks the object in relation to itself with the functions available in the IDropTarget interface. One function called IDropTarget::Drop() is called when the object is dropped on the target. Drop() passes the IDataObject pointer of the source to the target. Now that the client has the IDataObject pointer, it is free to manipulate the object.
OLE drag and drop will be discussed in detail as it is implemented in Visual C++ and MFC in Chapters 25,"Active Documents" 26,"Active Containers" and 27,"Active Servers." Drag and drop support is encapsulated in the following MFC classes:
- COleDropSource
- COleDropTarget
- COleClientItem
- COleServerItem
- COleDataSource
Embedding and Linking
A linked object is an object that is not stored within an OLE document, but rather elsewhere external to the document. In the document, a moniker is stored that references the linked object. This OLE function uses UDT to move the data from the data object source to the container application so that the data can be rendered as appropriate. Linked objects are manipulated through the IOleLink interface. By linking an object instead of embedding it, you cut down on the size of the compound file. In addition, you expose the linked object so that multiple people can use it.
An embedded object is an object that is stored, through the OLE Structured Storage mechanism, as native data within an OLE document. Although this increases the size of the compound file, it provides a single file object that can contain multiple data types.
OLE Documents
OLE documents are nothing more than compound files that use Structured Storage to hold the objects that make up the document. These objects can be native data, or they can, through the use of monikers, link to data outside the document. In addition, an OLE document can contain objects created by other processes, embedded as if they were natively a part of the document. OLE documents are handled through interfaces just like any other OLE object. As you can see, OLE documents are a conglomeration of several OLE services. Here are some of the interfaces used to implement OLE document interfaces:
- IOleItemContainer
- IPersistFile
- IClassFactory
- IOleInPlaceActiveFrame
- IOleInPlaceUIObject
- IOleInPlaceSite
COleDocument encapsulates the functionality of OLE documents and ActiveX documents in MFC. However, it is important to note that there are a series of classes in MFC that are used together to provide this functionality. You will explore these classes in detail in the later chapters.
In-Place Activation
OLE documents support in-place activation or what is commonly referred to as "visual editing." This enables you to edit embedded objects in a container application as if they were native. When you activate visual editing in the container, the user interface of the container morphs to support selected user-interface functions of the server application that created the object. There are a whole series of interfaces to enable you to implement and support in-place activation. These interfaces all begin with IOleInPlace. These are some of the interfaces you can use to implement and support in-place activation:
- IOleInPlaceObject
- IOleInPlaceActiveObject
- IOleInPlaceSite
- IOleInPlaceActiveFrame
- IOleInPlaceUIObject
- IOleInPlaceSite
Automation
Automation was originally called OLE Automation; during one of the original renaming purges orchestrated by Microsoft's marketing department, the OLE was dropped and the technology is now known simply as Automation. Automation basically enables you to manipulate the properties and methods of an application from within another application through the use of high-level macro languages and scripting languages such as VBScript and JavaScript. This enables you to customize objects and provide interoperability between applications.
In the world of Automation, there are Automation Servers and Automation Controllers. An Automation Server is a component or application that exposes properties and methods for use by other applications. Microsoft Excel is a good example of an Automation Server, because it exposes services that can create and manipulate worksheets, cells, and rows.
Descriptions of the properties and methods that are available through an Automation Server are available in an IDL file or can be made available as a type library. A type library is a binary representation of the information in an IDL file, although it has slightly lower fidelity.
In Visual C++ 6, there is a nice utility named OLE View that reads and graphically displays the contents of type libraries. You can use this utility to display the properties and methods exposed by Automation Servers. Figure 24.10 shows OLE View in action.
Figure 24.10 The OLE/COM Viewer that comes with Visual C++ 6.0.
Notice that the Type Library Viewer screen shows the disassembled type library in Interface Description Language (see Figure 24.11). It also displays the constants, properties, methods, and interfaces exposed by the Automation Server.
Figure 24.11 OLE View's Type Library Viewer.
Automation Controllers are client applications that use the properties and methods exposed by Automation Servers. Automation Controllers work through an interface called IDispatch. All interfaces that support Automation are derived from IDispatch.
ActiveX Controls
As discussed previously, ActiveX controls are self-contained reusable components that can be embedded in applications. ActiveX controls typically support Automation to enable properties to be set at both compile time and runtime. ActiveX controls also have methods that can perform certain operations.
They provide two-way communication between the control and the container. These components have a very profound impact in the area of application development. These reusable self-contained pockets of functionality are discussed in detail in Chapter 28, "ActiveX Controls."
Evolving OLE with Active Technologies | Next Section

Account Sign In
View your cart