Implementing a Linking Server: LinkSource

A server's implementation of binding and name parsing is stipulated by the types of monikers it supports. In actual applications, this means that the server must be able to bind or parse any moniker it creates and hands to other clients through the clipboard or some other transfer mechanism; it may, of course, fail to parse any moniker that it didn't originally provide or one that has more elements than the server can support.

LinkSource, in particular, supports binding of File, File!Item, and File!Item!Item monikers, in which the File item is related to a file object, the first item is related to objects contained in first-level substorages of that file, and the second item is related to objects contained in substorages below those of the first level, as illustrated in Figure 9-8 on the next page. The file object has to support binding to a file moniker as well as the resolution of a first-level item moniker, so it must implement IPersistFile and IOleItemContainer. The "Container Item" object (named with such a File!Item moniker) needs to support only the resolution of the second-level item moniker, so it needs only IOleItemContainer. The second-level object itself, called here the "Simple Item," needs no special interfaces for binding because that's all handled by its container. All three of these objects also implement IDescription to allow LinkUser to retrieve some interesting information from the objects for its own demonstrative purposes.

Figure 9-8.

LinkSource's object implementations as they relate to pieces of its storage.

The implementation of LinkSource's three objects is found in the classes CFileObject (FILEOBJ.CPP), CContainerItem (CONTITEM.CPP), and CSimpleItem (SIMPITEM.CPP). Although these are separate objects as far as reference counting is concerned, LinkSource actually centralizes some of their code inside common interface implementations. The CImpIOleItemContainer class (IOLECONT.CPP) implements IOleItemContainer for both CFileObject and CContainerItem—a Boolean flag in the interface class differentiates the two. As we'll see, much of the implementation of any IOleItemContainer interface for any object is somewhat independent of the actual object. This is even more true with the IDescription interface. As shown in Figure 9-8, the descriptive text for every object is stored in a stream named "Description" under the object's dedicated storage. The CImpIDescription class (IDESCRIP.CPP) doesn't care what IStorage it should look at; it simply implements GetText by attempting to read the description stream. Each object that exposes this interface instantiates a CImpIDescription and hands it its particular IStorage pointer; thus, we need only one piece of code for each object, showing one case in which the interface implementation technique is highly useful and actually more efficient and robust than a deep multiple-inheritance technique.

You will also notice that CContainerItem and CSimpleItem are so much alike (the former implements one additional interface) that you may wonder why I haven't used CSimpleItem as a base class for CContainerItem. My reason is that the similarity is an artifact of the relationship of these objects to the hierarchy in LinkSource's compound file. Real-world scenarios will likely not be this simple, so I'd rather illustrate a framework with distinct object classes. In later chapters, we'll be adding link-source capabilities to the Patron sample in which its Document, Page, and Tenant objects act in the same ways that LinkSource's three objects act here. But the Page and Tenant objects are so dissimilar that they share no implementation at all.

The following sections look at this implementation in more detail, specifically those parts related to binding and name parsing. First, however, we need to create a file so that LinkUser can name it with a file moniker.