Naming and Binding: Monikers

Think for a moment about a standard, mundane filename that refers to some collection of data stored somewhere on a disk. The filename essentially describes the "somewhere," and so the name identifies a file that we could call an object (in a primeval sort of way). But this is somewhat limited—filenames have no intelligence because all the knowledge about how to use and store the name exists elsewhere, in whatever application makes use of that filename. This normally hasn't been a problem because most applications can deal with files quite readily.

Now think about a name that describes the result of a query in a database, or one that describes a range of spreadsheet cells or a paragraph in a document. Then think about a name that identifies a piece of code that executes some operation on a network server. Each different name, if unintelligent, would require each application to understand the use of that name. In a component integration system, this is far too expensive. To solve the problem, OLE has Persistent, Intelligent Names, otherwise known as monikers, the topic of Chapter 9. Monikers are objects that encapsulate a type of name and the intelligence to work with that name behind an interface named IMoniker. Different moniker classes deal with different names, but they are all polymorphic through IMoniker. Furthermore, because IMoniker is itself derived from IPersistStream, monikers know how to store and retrieve the names they manage in a stream.

The primary intellectual operation of a moniker is called binding, which executes whatever operations are necessary to locate the named object and return an interface pointer to that named object. The named object, however, is not in any way related to the moniker itself—the moniker is simply doing the work of locating that object. After a client has been bound to the named object, the moniker falls out of the picture entirely. What actually happens to bind a moniker depends on the type of moniker you have, but regardless of that fact a client never needs to maintain that intelligence in itself.

OLE defines and implements five monikers itself. Four of these are "simple" monikers: File, Item, Pointer, and Anti, which are discussed fully in Chapter 9. As these simple monikers by themselves can only provide trivial names, OLE also implements a "generic composite" moniker, where the composite is a container for any number of other monikers that might themselves be simple or composite. Binding a composite basically means binding its constituent elements. The composite then enables the creation of complex names using any desired combination of other simpler names, greatly reducing the number of simple monikers necessary to create a very large number of complex names. For example, you can use a File and two Item monikers combined in a composite to name a selection of cells in a certain page of a large spreadsheet, as illustrated in Figure 1-12.

Figure 1-12.

A composite moniker that contains a file moniker and two item monikers.

Of course, if OLE's standard monikers are not suitable for your naming purposes, you can always implement your own moniker component with IMoniker. Because you encapsulate your functionality behind the interface, your moniker is immediately usable in any other application that knows how to work with IMoniker. Again, this is polymorphism on the object level, thanks to interfaces.

Working with monikers is generally called linking, the moniker's information being the link to some other data. OLE documents use monikers to implement linked compound document objects, which involves other user interface standards for managing links.