IMoniker: Display Name Group

The final two member functions of IMoniker, GetDisplayName and ParseDisplayName, deal with a textual representation of the moniker that can be used in a user interface. GetDisplayName asks a moniker for this display name, but this might actually be as costly an operation as binding the object altogether, which it might require anyway. For this reason, GetDisplayName takes as arguments a bind context and the moniker to its left in a composite. The resulting display name is returned in *ppszDisplayName, which the moniker allocates with CoTaskMemAlloc; thus, the caller is responsible for freeing that memory with CoTaskMemFree.

For example, a file moniker that contains the path \\BUNNYKINS\CDRIVE\DATA\OLE\CH09.DOC will generally use that string for the display name unless it can resolve the \\machine\volume part into a local drive letter, which results in C:\DATA\OLE\CH09.DOC, for example. An isolated item moniker (pmkLeft is NULL) will do nothing more than return its item string, and pointer monikers and anti-monikers have no display name, so they fail this function altogether.

A composite moniker creates a display name out of the strings of each constituent moniker. A File!Item!Item moniker will create a display name like C:\DATA\OLE\CH09.DOC!Section5!Graphic6. Keep in mind that when the composite asks an item moniker for its display name, it will pass a non-NULL pmkLeft to that moniker. In such a case, the item moniker will prefix its item string with the delimiter character originally passed to CreateItemMoniker. This character thus separates the item from whatever comes before it in the display name.

This separation not only is useful for providing a user with a visual separation but also provides for parsing a display string into a moniker—that is, for performing the opposite of GetDisplayName. The function IMoniker::ParseDisplayName does this—the moniker called names the object that knows how to parse the display name. In other words, the moniker called is not parsing its own display name but some display name that is relevant to the object named by the moniker. The pszDisplayName argument is thus the string to parse, *ppmkOut is the resulting moniker, and pchEaten is filled with the number of characters parsed from pszDisplayName in the process of creating *ppmkOut.

Some monikers might understand enough about their named objects to actually perform some of this parsing themselves. Most monikers, however, including file, item, and pointer monikers, depend on the objects they themselves name to perform the parsing. More specifically, these monikers bind to their named objects (calling their own BindToObject) and request the IParseDisplayName interface. The monikers then call IParseDisplayName::ParseDisplayName to do the honors, which returns the new moniker and the value to store in pchEaten, which the moniker called originally and then returns to the client.

Because this parsing will generally bind to the named object anyway, the operation can be just as expensive as binding is already. Usually parsing is needed only when the user has provided some name to an application and has told the application to create a link from it. We'll see examples when we talk about OLE Document containers in Part V of this book.

Parsing a display name almost always happens on a composite, which asks each of its constituent monikers to parse in turn. This is the whole reason why IMoniker::ParseDisplayName takes arguments such as pchEaten: the composite must track each moniker as it gets parsed from the name. In addition, simple monikers will call IBindCtx::RegisterObjectBound in the process to optimize possible later uses of the same object for parsing.

A client that wants to parse a user-provided string into a moniker doesn't actually have a composite moniker to call in the first place! This is the reason for the OLE API function MkParseDisplayName (the Mk stands for "moniker"), which has the following signature:


HRESULT MkParseDisplayName(IBindCtx *pbc, LPCWSTR pszName
, ULONG *pchEaten, IMoniker **ppmk)

Here the caller must create a new bind context before calling, passing that pointer in pbc along with the display string in pszName. On return, pchEaten will specify how far the parsing was successful, and ppmk will have a moniker for whatever was successfully parsed. You can use pchEaten to show the user how much of a string was parsed (with a highlight) and where the first parsing error occurred, allowing the user to correct the name if necessary.

MkParseDisplayName has the challenge to figure out exactly what type of moniker to start with, after which it can call IMoniker::ParseDisplayName as often as needed. Because of this, the display name passed to MkParseDisplayName must contain one of two initial patterns: a UNC pathname or the character @. If a UNC path is found, this function creates a file moniker and calls its ParseDisplayName to get the ball rolling down the rest of the string. (The file parses the next moniker in the string, which is asked to parse the rest, and so on.) If the string begins with @, MkParseDisplayName assumes the next string of characters is a ProgID, up to the next character that is not legal for a ProgID (such as \ or ! or anything other than 0–9, a–z, A–Z, and a period). With this ProgID, MkParseDisplayName looks up the CLSID registered for it and calls CoCreateInstance(clsid, NULL, CLSCTX_ALL, IID_IParseDisplayName, &pIPDN). The resulting object is given the entire display name to parse, so it must understand what sort of moniker to create initially. MkParseDisplayName then asks this initial moniker to parse its display name, and this continues until the whole string is parsed or an error occurs.