Implementing Parsing Support

For the most part, asking a server to parse moniker strings works very similarly to the binding process except that we do, of course, parse the display name from left to right. MkParseDisplayName will check for a filename or a string starting with @ in order to determine the CLSID of the first object in the name. So let's say LinkUser wants to parse "C:\INOLE\CHAP09\LINKSRC\GOOP.LKS!Object 2!Sub-Object 3". MkParseDisplayName will parse out the filename and create a file moniker out of it; this file moniker is the base to which MkParseDisplayName will attempt to add more individual monikers. Somehow it has to take the remaining string, "!Object 2!Sub-Object 3", and find someone to parse it. Well, because it knows that the file object named with the file moniker also understands the items within it, MkParseDisplayName asks that file object to do the parsing. It finds the CLSID for the filename already parsed and calls CoCreateInstance and asks for IParseDisplayName.

In the case of LinkSource, this will once again go all the way through the class factory to instantiate a CFileObject. When LinkSource is queried or when IParseDisplayName is called, we return our IOleItemContainer because the latter is derived from the former. When MkParseDisplayName gets this interface back from CoCreateInstance, it will pass the remaining string to parse to IParseDisplayName::ParseDisplayName:


STDMETHODIMP CImpIOleItemContainer::ParseDisplayName(LPBC pbc
, LPOLESTR pszName, ULONG *pchEaten, LPMONIKER *ppmk)
{
OLECHAR ch;
ULONG chEaten=0;
TCHAR szName[256];
TCHAR szComp[15];
LPTSTR psz;
UINT cch;

*ppmk=NULL;
*pchEaten=0;
psz=szName;

ch=*pszName++;
chEaten++;

if ((OLECHAR)'!'!=ch)
return ResultFromScode(MK_E_SYNTAX);

ch=*pszName++;

while ((OLECHAR)0!=ch && (OLECHAR)'!' !=ch)
{
*psz++=(TCHAR)ch;
chEaten++;
ch=*pszName++;
}

*psz=(TCHAR)0;

lstrcpy(szComp, m_fFileObj ? TEXT("Object ")
: TEXT("Sub-Object "));

//Does szName start with szComp?
cch=lstrlen(szComp);

if (0!=_tcsncicmp(szName, szComp, cch))
{
*pchEaten=1; //Parsed ! at least.
return ResultFromScode(MK_E_SYNTAX);
}

//Check for number in szName.
if ((TCHAR)'0' != szName[cch])
{
if (0==_ttoi(szName+cch))
{
*pchEaten=cch; //Got past name.
return ResultFromScode(MK_E_SYNTAX);
}
}

*pchEaten=chEaten;
return CreateItemMoniker(TEXT("!"), szName, ppmk);
}

LinkSource knows that it separates item monikers with a ! delimiter, so this code needs only to scan for strings between those delimiters. The pszName argument should be initially pointing to a !, so if that is absent we return MK_E_SYNTAX. Otherwise, we copy the remaining characters into a buffer up to the next ! or to the end of the string. We then confirm that this string has the format "Object m" or "Sub-Object n," depending on the m_fFileObj flag in this interface, return MK_E_SYNTAX in case of error, and ensure that an appropriate value is stored in *pchEaten to indicate where the error occurred. We do not, however, verify that an object exists—parsing is meant to check syntax, leaving binding to check existence.

After we finish parsing and validation, we need to return an appropriate moniker. In this example, that is always an item moniker, but there are no restrictions as to the moniker type. (IParseDisplayName does not depend on IOleItemContainer; it's the other way around.)

The final part of this implementation is the IOleContainer::LockContainer function, which MkParseDisplayName will call to stabilize the object doing the parsing—that is, to ensure that the object doesn't disappear as long as the bind context remains alive. You can implement LockContainer with a simple AddRef and Release, depending on the fLock argument:


STDMETHODIMP CImpIOleItemContainer::LockContainer(BOOL fLock)
{
if (fLock)
AddRef();
else
Release();

return NOERROR;
}

You might also consider using CoLockObjectExternal in this function if your implementation calls for managing the object on the basis of external connections rather than through a reference count. Because no other strong locks are involved with LinkSource, we can use just a reference count.