XLL+ Class Library (7.0)

Using object handles

Handle extensions

Virtually all of the code required to implement handles can be generated by for you, if you use one of the XLL+ Handle extensions.

Several different flavours are available, which differ in how the handle is displayed, and in how the lifetime of the handle objects is managed:

Extension Handle format Example
RtdHandles.xpe Guid:State {A6CBD52A-040D-4990-89EF-41A6721BADC6}:1280298484
NumericHandles.xpe Address 66145697
StringHandles.xpe Class:Index MyObject:3
StringPtrHandles.xpe Class:Address MyObject:66145697

For this example, we will use the RtdHandles.xpe extension.

Load the extension file

Use the "Manage extensions" command to show the XLL+ Options/Extensions page:

Use the Add button on the XLL+ Options/Extensions page to load the extension file RtdHandles.xpe from the extensions sub-directory.

Note: Ensure that none of the alternative handles extensions (NumericHandles.xpe, StringHandles.xpe or StringPtrHandles.xpe) are loaded. Remove them from the list if they are. Only one variety of handles at a time can be used in a project.

If you use the View Extensions command (located in the XLL Add-ins window on the Tools menu) to inspect the loaded extensions, you will observe that RtdHandles.xpe contains two extended types - Handle and NCHandle - and a function extension - HandleCreator. We will examine these in a moment.

Create the project

Let us try an example, using the RtdHandles extension.

Note: You can find all the source code discussed here in the RtdHandleDemo sample.

Create a new project, RtdHandleDemo with a menu and a results cache. (Check the options Add a menu and Include a cache for results in the XLL+ AppWizard. Do not check the option Save the cache to file.)

We're going to need the menu later, so that the user can clear the handles from time to time if they choose.

The object to be represented as a handle

In the new project, add the code below to the main source file. It is the definition of a very simple object that will be represented in Excel by a numeric handle. We will also create "getter" add-in functions that take a handle as an argument and return data from the object.

class Thing {
    Thing(const char* name_, int value_, double created_) 
      : name(name_), value(value_), created(created_) 
    std::string name;
    int value;
    double created;

An add-in function to create the Thing

Our first add-in function Thing.Create() creates an object with the supplied name and value, adds it to a cache, and returns a numeric handle for the object.

Use the XLL+ Function Wizard to create a new add-in function with the characteristics below:

Name: Thing.Create
Return type: CXlOper
Category: User Defined
Description: Creates a new Thing

Add two arguments to the function, the name and the value of the new Thing:

Type Name Description
String Name Name of thing
Int Value Value of thing

The wizard looks something like this:

We have one more thing to do in the Wizard. We need to register the function as a creator of handles. Select the Extensions tab, and select the Extension HandleCreator in the list on the left, and put a check against it.

On the right-hand side a list of the function extension's parameters will appear. Set the Handle type parameter to Thing, because that is what we will be returning.

Finally, save the function and add the code shown below:

CXlOper* Thing_Create_Impl(CXlOper& xloResult, const CXlStringArg& Name, long 
    // End of generated code 
    Thing* thing = new Thing(Name, Value, CXlDate::Now());  
    xloResult = psl::CreateHandleInCache(thing);            
    return xloResult.Ret();

The two new lines do three things:

  1. Create a new Thing object based on the inputs and the current time.
  2. Add the Thing to the handle store, and get a handle in return.
  3. Return the numeric handle to Excel.

Add-in functions to get the Thing's properties

Now we need some functions to get something useful out of the handle.

Using the XLL+ Function Wizard, create a new function, with the characteristics below:

Name: Thing.Name
Return type: CXlOper
Category: User Defined
Description: Gets the name of a Thing from its handle

Next, add a new argument, thing, of type Handle. As you set the type of the argument, the Function Wizard will display a new column, Handle type. You should set it to Thing, as shown below.

Type Name Description Handle type
Handle thing Handle to a thing Thing

The wizard should look something like this:

Save your changes and add the one line of code shown below:

CXlOper* Thing_Name_Impl(CXlOper& xloResult, double thing_op)
    // Input buffers 
    const Thing* thing;
    // Validate and translate inputs
    XlReadScalarEx(thing_op, thing, psl::HandleConverter<Thing>(), 
        CScalarConvertParams<const Thing*>(L"thing", 0, 0, -1).SetStringParam(0,
    // End of generated code 
    xloResult = thing->name; 
    return xloResult.Ret();

As you can see, the validation function, XlReadScalarEx has validated the handle and converted it to a Thing* pointer. There's no need for any checks in our code: we know that the thing is a valid pointer; if it were not, an exception would have been thrown by now, and this code would not be running.

So all we have to do is set the result to be thing->name.

For the other functions, Thing.Value() and Thing.Created(), you should save yourself time by copying the signature of Thing.Name(), thus:

Next: How handles work >>