XLL+ Class Library (7.0)

Other handle implementations

This discussion of handles has focused on the RTD handle implementation, since this is the most efficient and also the most flexible implementation.

Unsynchronized caches

As discussed earlier, another set of implementations are available, which use an unsynchronized cache, instead of the synchronized cache used by RTD. They are all very similar to each other, and differ only in how the handle is displayed.

Extension Handle format Example
NumericHandles.xpe Address 66145697
StringHandles.xpe Class:Index MyObject:3
StringPtrHandles.xpe Class:Address MyObject:66145697

The cache used by the RTD implementation receives a message from Excel whenever a handle goes out of use. It immediately destroys the object and removes it from the cache. As a result, the use of memory and the speed of the implementation remains constant. (We refer to this as a synchronized cache.)

These alternative implementations do not receive a message when a handle is no longer used, and therefore do not know when to destroy it. The cache grows in size until Excel is closed, or until the user decides to clear it. (We refer to this as an unsynchronized cache.)

Advantages

These unsynchronized implementations have three advantages over the RTD implementations:

  1. They work with very old versions of Excel, including Excel 2000 and below. RTD requires Excel XP (2002), Excel 2003 or Excel 2007 or above.
  2. They are completely self-contained, whereas the RTD handle implementations require an RTD server module, RtdHandleServer.dll, to be deployed and registered on users' machines.
  3. Functions that create handles can be marked as thread-safe, whereas they cannot be marked as thread-safe when using RTD handles.

Disadvantages

There are also significant disadvantages in using the unsynchronized handle implementations:

  1. The object cache grows as long as Excel is in use, so memory use and performance are not constant.
  2. In order to invalidate handles contained in workbooks that are reopened, the add-in forces a recalculation of all cells that contain handle creation functions. This is achieved through a COM event handler (which catches the WorkbookOpen event) and a macro which searches each worksheet. This technique is slower and much more intrusive than the technique used by RTD handles.
  3. The format of the handles is limited. You cannot apply custom formatters, as you can with RTD handles.
  4. You must provide a house-keeping mechanism, such as a menu command "Clear object handles", so that the user can reduce the size of the cache without exiting Excel. You also need to educate users as to the purpose and use of the command.

House-keeping

There are two additional "house-keeping" tasks that need to be managed in a handle add-in that uses these unsynchronized handles.

Stale handles

The handle values saved in a workbook are irrelevant when the workbook is reopened. Since they are, in effect, pointers, there is no guarantee that the memory pointed to is still valid, and still contains an object of the right type. Worse still, the pointer may be for an object of the right type, but out of date, so that the results are erroneous.

So, we must make sure that all the handles in a workbook are recalculated when the workbook is reopened. The HandleCache object contains an event observer object (m_workbookOpenObserver) which handles this by trapping the WorkbookOpen event, and using a CXlFormulaRefresh object to update all cells containing a handle creator function.

All this is dealt with by the code generated by the XLL+ Function Wizard. The developer does not need to add any code.

Clearing the cache

If Excel stays open for long periods, and many object handles are created over time, it is sensible to clear out all the handles in the cache from time to time.

You should provide a menu command that clears the handle cache thus:

CopyC++
psl::ClearHandleCache<Thing>();

You can find an example of this in the NumericHandleDemo sample.

Note that if you have used a results cache, you need to make sure that you clear the results cache first, and then clear all the handles:

CopyC++
CXlOper* Thing_ClearCache_Impl(CXlOper& xloResult)
{
    // End of generated code 
//}}XLP_SRC

    ::XllGetTypedApp()->m_cache.Clear();
    psl::ClearHandleCache<Thing>();
    return xloResult.Ret();
}

Next: Specifying a smart pointer type >>