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.
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
orStringPtrHandles.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.
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.
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 { public: Thing(const char* name_, int value_, double created_) : name(name_), value(value_), created(created_) { } std::string name; int value; double created; };
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 Value) { // End of generated code //}}XLP_SRC Thing* thing = new Thing(Name, Value, CXlDate::Now()); xloResult = psl::CreateHandleInCache(thing); return xloResult.Ret(); }
The two new lines do three things:
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, L"Thing")); // End of generated code //}}XLP_SRC 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: