Monikers are often composed from other monikers to allow object hierarchies to be navigated based on a textual description of a path. To support this type of navigation easily, COM provides a standard moniker implementation that, when composed to the right of another moniker, asks the object to bind a reference to another object in the hierarchy. This moniker is called the Item Moniker, and it uses the object’s IOleItemContainer interface to resolve an object name to an interface pointer.
The following display name demonstrates the Item Moniker used in tandem with the Class Moniker:
clsid:571F1680-CC83-11d0-8C48-0080C73925BA:!Ursus
Note the use of the “!” character to delimit the Class Moniker’s display name from the item name “Ursus”. When parsed, MkParseDisplayName will first use the prefix “clsid” as a ProgID to contact the implementation of the Class Moniker. MkParseDisplayName will then ask the Class Moniker implementation to parse as much of the string as it recognizes. This means that after the Class Moniker has parsed its GUID from the string, the following fragment will still need to be parsed:
!Ursus
Because this name is meaningful only in the scope of the object named by the moniker to its left, MkParseDisplayName will actually bind the leftmost moniker (the Class Moniker) and ask the object it names (the Gorilla class object) to parse the remainder of the string. To support parsing display names, COM defines the standard interface IParseDisplayName:
[ object,uuid(0000011a-0000-0000-C000-000000000046) ]
interface IParseDisplayName : IUnknown {
// convert display name to a moniker
HRESULT ParseDisplayName(
[in, unique] IBindCtx *pbc,
[in] LPOLESTR pszDisplayName,
[out] ULONG *pchEaten,
[out] IMoniker **ppmkOut
);
}
In the case of the display name used in this example, the Gorilla class object would need to implement IParseDisplayName and convert the string “!Ursus” to a moniker that MkParseDisplayName will compose to the right of the Class Moniker. Because the standard Item Moniker is desired, the following implementation would suffice:
STDMETHODIMP GorillaClass::ParseDisplayName(IBindCtx *pbc,
LPOLESTR pszDisplayName, ULONG *pchEaten,
IMoniker **ppmkOut) {
// create an item moniker using explicit API function
HRESULT hr = CreateItemMoniker(OLESTR("!"),
pszDisplayName + 1,
ppmkOut);
// indicate how many characters were parsed
if (SUCCEEDED(hr))
*pchEaten = wcslen(pszDisplayName);
else
*pchEaten = 0;
return hr;
}
Note that this example does not attempt to validate the name it is parsing. It simply shears off the leading “!” and creates a new Item Moniker from the remaining display name.
Once the two monikers have been parsed, MkParseDisplayName will couple the two monikers together using a generic composite moniker. Generic composite monikers simply hold two monikers together. The generic composite moniker’s implementation of BindToObject simply binds the moniker on the right first, passing the pointer to the moniker on the left as the pmkToLeft parameter. The following pseudocode illustrates this:
// pseudo-code from OLE32.DLL
STDMETHODIMP GenericComposite::BindToObject (IBindCtx *pbc,
IMoniker *pmkToLeft,
REFIID riid, void **ppv) {
return m_pmkRight->BindToObject(pbc, m_pmkLeft, riid, ppv);
}
This implementation illustrates that the moniker on the right is meaningful only in the scope of the moniker to its left. In the case of the Class!Item Moniker used in this example, the Item Moniker will receive the Class Moniker as its pmkToLeft parameter at bind time.
As stated earlier, the Item Moniker uses the IOleItemContainer interface to bind an interface pointer. The following is the pseudocode for the Item Moniker’s BindToObject implementation:
// pseudo-code from OLE32.DLL
STDMETHODIMP ItemMoniker::BindToObject(
IMoniker *pmkToLeft, IBindCtx *pbc,
REFIID riid, void **ppv) {
// assume failure
*ppv = 0;
if (pmkToLeft == 0) // requires a scope
return E_INVALIDARG;
// first bind moniker to left
IOleItemContainer *poic = 0;
HRESULT hr = pmkToLeft->BindToObject(0 ,pbc,
IID_IOleItemContainer, (void**)&poic);
if (SUCCEEDED(hr)) {
// cache the bound object in binding context
pbc->RegisterObjectBound(poic);
// get bind speed from Bind Context
DWORD dwBindSpeed = this->MyGetSpeedFromCtx(pbc);
// ask object for named sub-object
hr = poic->GetObject(m_pszItem, dwBindSpeed, pbc,
riid, ppv);
poic->Release();
}
}
This implementation implies that the following code:
HRESULT GetUrsus(IApe *&rpApe) {
const OLECHAR pwsz[] =
OLESTR("clsid:571F1680-CC83-11d0-8C48-0080C73925BA:!Ursus");
return CoGetObject(pwsz, 0, IID_IApe, (void**)&rpApe);
}
is equivalent to
HRESULT GetUrsus(IApe *&rpApe) {
IOleItemContainer *poic = 0;
HRESULT hr = CoGetClassObject(CLSID_Gorilla, CLSCTX_ALL,
0, IID_IOleItemContainer, (void**)&poic);
if (SUCCEEDED(hr)) {
hr = poic->GetObject(OLESTR("Ursus"), BINDSPEED_INFINITE,
0, IID_IApe, (void**)&rpApe);
poic->Release();
}
return hr;
}
Note that the level of indirection afforded by using CoGetObject allows the client to change the binding policy simply by reading a different display name from a configuration file or registry key.