IMoniker: Table Group

Two monikers that compare as equal through IsEqual must also return the same value from Hash—both of these functions should be developed with the other in mind. The reason is that Hash makes a convenient value to use as an index for a moniker table (like the running object table) and IsEqual is commonly used to match a moniker to a moniker entry in such a table.

Hashing is not a difficult proposition. For example, the file moniker merely runs through the characters in the pathname and munges the characters with some XOR operations (this is code based on Unicode):


HRESULT CFileMoniker::Hash(LPDWORD pdwHash)
{
DWORD dwTemp = m_cAnti;
WCHAR *pch;
WCHAR ch;

[Set pch to path string, converting to Unicode if needed.]

while (*pch)
{
dwTemp *= 3;
ch = *pch++;
dwTemp ^= ch;
}

*pdwHash = dwTemp;
return NOERROR;
}

OLE's item monikers do exactly the same thing with their strings. For your own monikers, any suitable hash algorithm is fine.

Another important note about the hash value is that it must be consistent across processes—that is, the hash value can be marshaled. Be sure to take this into account when you're writing Hash because you cannot use the value of a pointer as part of the algorithm.

For IsRunning, its pmkNewlyRunning argument is the moniker of the last object registered as running in the running object table. If this argument is NULL, the moniker can use the running object table to implement this function directly (using IBindCtx::GetRunningObjectTable, of course). If pmkNewlyRunning is non-NULL, the return value from IsRunning is NOERROR only if pmkNewlyRunning is the same moniker as this. You can make this assumption because of the nature of composite binding and because servers will register newly running objects as they are bound in the process. This means that a moniker doesn't need to perform an exhaustive search to see that the object is running. At most, the moniker needs to look in the running object table. The documentation for IsRunning in the OLE Programmer's Reference has a number of sample implementations of this function that also account for "wildcard," as described earlier.

IMoniker::GetTimeOfLastChange can generally use IRunningObjectTable::GetTimeOfLastChange to implement itself. This function should not attempt to run an object to obtain information but should rely instead on any cached information the moniker might already have. The reason to keep change times in the first place is to help clients decide whether to bother binding to the object. For example, if the object has not changed for a long time, binding to it to get an update is not necessary. You can optimize the implementation of this member if the pmkToLeft passed to this function is non-NULL because then the moniker can assume that it cannot have changed any later than the moniker to its left, so it can pass this function call to pmkLeft->GetTimeOfLastChange. If pmkLeft is NULL, the moniker can try the running object table and then pursue other means such as checking file time stamps.


The IROTData Interface and Custom Monikers

The running object table in OLE under Windows NT 3.5 exhibited poor performance when calling IMoniker::IsEqual. To solve the performance question, monikers that are going to be registered in the running object table must implement a second interface, named IROTData. This interface has a single member—GetComparisonData—through which the table asks the moniker for data it can use to compare with another moniker in lieu of calling IsEqual. All of OLE's standard monikers will follow this rule, as should custom monikers. Monikers that do not implement this interface will not be allowed to register as running. This rule will affect custom monikers on Windows 95, Windows NT 3.51, and later platforms. Check your documentation for the details.