XLL+ Class Library (6.3)

Custom handle formats

Object handles, as implemented in the RtdHandles.xpe extension, consist of a set of parts, separated by colons (or other separators), which combine to form a handle. The parts include:

Using the default handle format, a handle looks like this:

{A6CBD52A-040D-4990-89EF-41A6721BADC6}:1280298484

Custom handle format

The developer can implement their own handle formatting class, and thereby take control of the appearance of the handle, by deriving a class from CHandleFormatter<T>.

For example, you can include the type, name and other details of the object being represented:

CustomThing:The name of the thing:{A6CBD52A-040D-4990-89EF-41A6721BADC6}:1280298484
YieldCurve:USD:Govt:{A6CBD52A-040D-4990-89EF-41A6721BADC6}:1280298484

This makes for much more user-friendly handles; it becomes much easier to understand the structure of a spreadsheet that uses handles, and to track down problems.

If you do not specify a separator, then a colon is used. If you have handles that need to include colons, you can specify a different separator by passing it to the constructor of the base class, e.g.:

YieldCurve~USD:Govt~{A6CBD52A-040D-4990-89EF-41A6721BADC6}~1280298484
YieldCurve/USD:Govt:Short/{A6CBD52A-040D-4990-89EF-41A6721BADC6}/1280298484

A custom handle formatter class

A class derived from CHandleFormatter<T> must implement the two conversion functions: MakeHandle and SplitHandle. If the class has a destructor, it must be declared as virtual.

The example code below can be found in the RtdHandleDemo sample. Here is the simple object which will be used with a custom handle formatter:

CopyC++
class CustomThing {
public:
    CustomThing(const TCHAR* name_, int value_, double created_) 
      : name(name_), value(value_), created(created_) 
    {
    }
    std::tstring name;
    int value;
    double created;
};

We will define a custom handle formatter class, which creates handles that include the target object's type and name, e.g.:

CustomThing/Gregory/{A6CBD52A-040D-4990-89EF-41A6721BADC6}/1280298484

The class implements the two required virtual methods, SplitHandle() and MakeHandle(). The code is based on the implementations in the base class, which can be found in include\XlRtdHandles\HandleFormatter.h.

In addition, the constructor passes a forward slash to the base class constructor, to be used as a separator character.

CopyC++
class CustomThingHandleFormatter : public psl::rtd::CHandleFormatter<CustomThing> 
{
public:
    // The constructor specifies a forward slash as the separator character
    CustomThingHandleFormatter() 
      : psl::rtd::CHandleFormatter<CustomThing>(_T('/')) 
    {
    }
    virtual bool SplitHandle(const TCHAR* pszHandle, std::tstring& strKey, long& lState)
    {
        // Extract the required parts: the guid key and the state 
        // which are at positions 2 & 3 respectively. 
        // The base class method ExtractKeyAndState() method does all the work. 
 
        return ExtractKeyAndState(pszHandle, strKey, lState, 2, 3);
    }
    virtual std::tstring MakeHandle(const CustomThing* t, const TCHAR* pszKey, long lState)
    {
        // Create a handle: [Class]/[Name]/[GUID]/[State] 
 
        // This code is based on that in the parent class, 
        // with an additional 2 key parts inserted at the start  
        // of the handle. 
 
        std::vector<std::tstring> parts;

        // 1st, the class
        parts.push_back(_T("CustomThing"));

        // 2nd, the name. Note that the base class 
        // method Escape() is used to remove any separator characters.
        parts.push_back(Escape(t->name));

        // 3rd and fourth, the GUID and the state. 
        // These two parts are required, but can be in any position. 
        // We put them at the end of the handle, because they are not 
        // interesting to the user, and we therefore do not care much 
        // if they are not visible.
        parts.push_back(pszKey);
        parts.push_back(WriteState(lState));

        // Join the parts together and return the concatenated string. 
        return Join(parts);
    }
};

Finally, in the OnXllOpen() event handler, we register the custom formatter with the single instance of HandleCache<CustomThing>:

CopyC++
BOOL CRtdHandleDemoApp::OnXllOpenEx()
{
    // Install the custom handle formatter, for CustomThings.
    psl::HandleCache<CustomThing>::GetOrCreateInstance()->SetFormatter(
        new CustomThingHandleFormatter());

    return TRUE;
}

Next: Persistent object handles >>