Sunteți pe pagina 1din 32

Chapter 9

Interacting with EXCEL


We will investigate methods of using C++ routines in Excel and also methods of run-
ning Excel from C++. Since C++ has no built in facility for drawing graphs and for
displaying data in an attractive way, except by using the gui which is limited, the in-
teraction will provide us with ways of using the speed, structural features and greater
accuracy of C++ in a user-friendly environment.
We will look at three scenarios:
We will construct simple DLLs (Dynamic Link Libraries) which export functions
of a certain type and which can be called from Excel. These functions can be used
(after declaring them in a module) in VBA programs and the results returned to
the spreadsheet. Some of them can also be used directly in the spreadsheet as
worksheet functions. There is a great deal more that is possible, but the technique
we present will do for most purposes.
A DLL is a library whose code can be used at run time and does not have to be
compiled together with the code of the application. In contrast, Static Libraries
contain functions which must be included at compile time.
We will construct simple XLLs which can be used as Add-Ins. These are DLLs
which contain special functions that tell Excel what type of functions are contained
in the DLL. This enables Excel to register them when the XLL is loaded as an
Add-In and to use them like ordinary worksheet functions. The functions must
still be declared in a VBA module in order to use them in VBA.
We will show how to call an instance of Excel from a C++ program and how to
use the Excel objects in the program.
9.1 A Simple DLL
In this section we will indicate how to construct a library of functions (DLL) which can
be accessed from Excel.
1
2 CHAPTER 9. INTERACTING WITH EXCEL
We will construct a DLL which exports the following function quad. It solves a
quadratic equation with real coecients.
#include <cmath>
#include <cstdlib>
using namespace std;
double __stdcall quad(double a,double b,double c,double &rrt1,
double &rrt2,double &crt1r,double &crt1i,
double &crt2r,double &crt2i)
{
rrt1=-999999.;rrt2=-999999.;crt1r=-999999.;crt1i=-999999.;
crt2r=-999999.;crt2i=-999999.;
if (a == 0.)
if (b != 0) {rrt1=-c/b;exit(1);}
else exit(1);
double sqrtd;
double oneover2a=0.5*a;
double d=b*b-4*a*c;
double realpart = oneover2a*b;
if (d>=0)
{
sqrtd=oneover2a*sqrt(d);
rrt1=realpart + sqrtd;
rrt2=rrt1 - sqrtd - sqrtd;
}
else
{
sqrtd=oneover2a*sqrt(-d);
crt1r=realpart;crt2r=realpart;
crt1i=sqrtd;crt2i=-sqrtd;
}
return 0.;
}
Here, the result is returned in the last 6 arguments. rrt is a mnemonic for real
root and crt is a mnemonic for complex root, r for real part and i for imaginary
part. The returning variables have defaults 0f -999999., and the various cases
can be checked according to whether the return values are defaults or not. We
need a wrapper function in VBA to do this. The function uses 6 multiplications
and returns the real and imaginary parts of the roots. 5 Multiplications would
be optimal. We could omit the last two arguments, as they provide no new
information.
Open a Win32 project in VS and on the second screen check the boxes marked
DLL and Empty Project.
9.1. A SIMPLE DLL 3
Add a .cpp module to the Sources and enter the code. Build to check for entry
errors.
Note that you do not need a prototype for the function quad. However, you would
need prototypes for auxiliary functions called by your functions.
The function(s) that you are to export have their name preceded by __stdcall
(note the two underline characters before the rst alphabetic character). This is
an instruction to the operating system on how to run the function. It is platform-
dependent. There are other possible ways to run a function which we do not
mention.
It is easy to pass Integer,Long,Double arguments to VBA and to the worksheet.
Vectors and multidimensional arrays of these types are more dicult to pass
and will be dealt with in the following example. These types should suce for
numerical applicaions. However, with a lot more eort, we can arrange to pass
almost any type.
Add a new Source module by choosing one with a .def qualier. Add a line to
it with the word EXPORTS Then add the name(s) of the function(s) (quad in this
case) one to a line.
Build the application under the Debug option in the edit box on the top toolbar.
Look in the Debug folder in you project folder. You will nd one le with a .dll
qualier. When you are eventually satised with everything (after the experimen-
tation to be outlined) you should do the build under the Release option to obtain
a leaner version. Now add a few breakpoints to the code.
Create a subfolder of your project folder called XLS.
(See below for an alternative). Open Excel and in the Tools | Options | General
menu and in the edit box marked At startup, open all files in: put the full
path of your XLS folder. Save your Excel instance in this folder. This should be
the only le in this folder.
In VS go to the menu item Project | (ProjName) Properties ... | Conguration
Properties | Debugging | Command edit box. Click the Down arrow and select
Browse. Look for your .exe le for Excel (it should be in the Program Files |
Microsoft Oce | OFFICE11 folder). Click on the le.
(Alternative to the item above). In the same menu, just below Command, is a
box called Command Arguments. In the box, enter the full path of an Excel le
that you have previously saved and wish to use for debug purposes. You do not
need to follow the above alternative instructions.
Click on the Run Debug triangle on the top toolbar. The debugger opens your
Excel le.
Insert a code module and in the declarations section at the start of the module
insert Option Explicit and the following declaration and Sub:
4 CHAPTER 9. INTERACTING WITH EXCEL
Declare Function quad _
Lib "J:\Visual Studio Projects\Finance\qtst\Debug\qtst.dll" _
(ByVal a As Double, ByVal b As Double, ByVal c As Double, _
rrt1 As Double, rrt2 As Double, crt1r As Double, crt1i As Double, _
crt2r As Double, crt2i As Double) As Double
You must replace the path to the DLL by your own DLL path.
Sub quadtst()
Dim x As Double, rrt1 As Double, rrt2 As Double, crt1r As Double, _
crt1i As Double, crt2r As Double, crt2i As Double
x = quad(1, 1, 1, rrt1, rrt2, crt1r, crt1i, crt2r, crt2i)
End Sub
Note the ByVal prefacing the rst 3 arguments. Without any modication, a
C++ argument is called by value and a VBA argument is called by reference.
Since our C++ function had its rst 3 arguments called by value, we must do
the same for our VBA version. The last 6 arguments were called by reference in
C++ and so do not need modication in VBA. If you get it wrong the operating
system will say something like Bad Calling Convention at run time.
Place a breakpoint at End Sub and run the sub, looking at the return values in
the Locals Window. Try various values of the rst three parameters a,b and c.
You can now write a wrapper function in VBA which calls the function quad and
interrogates the return values to nd what the situation for your equation is in
fact. This information can then be put out to the spreadsheet. Try to make it
pretty.
Sometimes, functions of this type can be used directly in as worksheet functions.
If you look at user dened functions in the Insert Function dialog box, you will
nd it listed under user functions. However, it is usually best to write a VBA
wrapper function which calls your function internally and puts out exactly the
right information to the spreadsheet. In the present case, the function quad will
not run when entered on the spreadsheet with help from the Insert Function
dialog. The function expects all arguments to be called by value. If you removed
the last 6 arguments, the function would run - try something similar. To get
call by reference arguments to work you need to inform Excel of the argument
reference types. We will see how to do this when constructing an XLL.
9.2 A DLL That Takes Array Arguments
We would like to call the C++ function from VBA with return type one of double,int,long
and possibly array arguments with data having one of these three types. The arrays
that we will consider are either vectors (1-dimensional) or matrices (2-dimensional).
Very occasionally in numerical work, one needs 3-dimensional arrays, and we will leave
to the reader extensions to this type of array.
9.2. A DLL THAT TAKES ARRAY ARGUMENTS 5
VBA uses a Microsoft dened type called SAFEARRAY to store arrays. This type is a
structure containing the bounds of the various dimensions and a pointer to the array
values. We need to do the following:
Dene our C++ function with the correct type of arguments. The arrays must
be of SAFEARRAY type and the input and output array variables must be pointers
to pointers to variables of type SAFEARRAY.
Find the bounds of the input arrays.
Construct arrays in C++ (using whatever array types you like) of appropriate
size and type.
Transfer the array values from the input arrays to the C++ arrays.
Process the input arrays and values.
Transfer the desired outputs to the SAFEARRAY type output variables.
We give an example of how to do this for one of the programs in NR, namely the
construction of the singular value decomposition (svd) of a rectangular matrix.
#include "nr.h"
#include <ole2.h>
#include <windows.h>
#include "MyFns.h"
using namespace std;
double __stdcall svdc(SAFEARRAY **a,SAFEARRAY **w,SAFEARRAY **v)
{
long nrows,ncols;
GET_SAFE_BOUNDS_MAT(a,nrows,ncols);
Mat_IO_DP ac(nrows,ncols);
SAFE_to_CArray(a,ac);
GET_SAFE_BOUNDS_MAT(v,nrows,ncols);
Mat_IO_DP vc(nrows,ncols);
SAFE_to_CArray(v,vc);
long nelts;
GET_SAFE_BOUNDS_VEC(w,nelts);
Vec_IO_DP wc(nelts);
SAFE_to_CVec(w,wc);
NR::svdcmp(ac,wc,vc); // Main function called from NR
CArray_to_SAFE(ac,a);
6 CHAPTER 9. INTERACTING WITH EXCEL
CArray_to_SAFE(vc,v);
CVec_to_SAFE(wc,w);
return 0;
}
Comments
The function that we wish to run in C++ is called svdcmp and takes a matrix a
as input and outputs a vector w of singular values and a matrix v, as well as a
matrix u which is put in place of the matrix a. Hence the argument a is used for
both input and output.
Note the **a and similar. Thus *a is a pointer to a SAFEARRAY.
The auxiliary functions used are dened at the end of this section, and have names
which are self-explanatory. ac,wc,vc are C++ arrays of appropriate size.
The main called function from NR is contained in a module called, for example, svdcmp.cpp
and the contents are of the form
#include <cmath>
#include "nr.h"
using namespace std;
void NR::svdcmp(Mat_IO_DP &a, Vec_O_DP &w, Mat_O_DP &v)
{
.......
}
Other les that must be included in the Source les and in the Build are: pythag.cpp,svd.def,MyFns.cpp
and in the Header les we need MyFns.h. The pythag.cpp le is in the NR collection of
les and does a particular calculation in a numerically ecient way. MyFns.cpp,MyFns.h
contain the utility functions used in the program and are now listed:
MyFns.h:
#ifndef MYFNS_H
#define MYFNS_H
double __stdcall NormSDens(double x,double* v);
double __stdcall NormSProbInv(double u);
void SAFE_to_CArray(SAFEARRAY **p,Mat_IO_DP &q);
void CArray_to_SAFE(Mat_IO_DP &q,SAFEARRAY **p);
void SAFE_to_CVec(SAFEARRAY **p,Vec_IO_DP &q);
void CVec_to_SAFE(Vec_IO_DP &q,SAFEARRAY **p);
void GET_SAFE_BOUNDS_MAT(SAFEARRAY **p, long & nrows, long &ncols);
void GET_SAFE_BOUNDS_VEC(SAFEARRAY **p, long & nelts);
9.2. A DLL THAT TAKES ARRAY ARGUMENTS 7
double __stdcall svdc(SAFEARRAY **a,SAFEARRAY **w,SAFEARRAY **v);
#endif
MyFns.cpp:
#include <windows.h>
#include <ole2.h>
#include "nr.h"
#include "MyFns.h"
#include <cmath>
using namespace std;
const double OneDivRootTwoPi = 0.398942280401433;
void CVec_to_SAFE(Vec_IO_DP &q,SAFEARRAY **p)
{
long elt_index[1];
int nrows = q.size();
for (int i=0;i<nrows;i++)
{
elt_index[0] = i;
SafeArrayPutElement(*p, elt_index, & q[i]);
}
}
void SAFE_to_CVec(SAFEARRAY **p,Vec_IO_DP &q)
{
long dimsp = (**p).cDims;
long * ubounds = new long[dimsp];
SafeArrayGetUBound(*p,1,&ubounds[0]);
LONG elt_index[1];
for (int i=0;i<=ubounds[0];i++)
{
elt_index[0]=i;
SafeArrayGetElement(*p,elt_index,&q[i]);
}
delete [] ubounds;
}
void CArray_to_SAFE(Mat_IO_DP &q,SAFEARRAY **p)
{
long elt_index[2];
int nrows = q.nrows();
int ncols = q.ncols();
for (int i=0;i<nrows;i++)
8 CHAPTER 9. INTERACTING WITH EXCEL
for (int j=0;j<ncols;j++)
{
elt_index[0] = i;
elt_index[1] = j;
SafeArrayPutElement(*p, elt_index, & q[i][j]);
}
}
void SAFE_to_CArray(SAFEARRAY **p,Mat_IO_DP &q)
{
long Dims = (**p).cDims;
long * ubounds = new long[Dims];
SafeArrayGetUBound(*p,1,&ubounds[0]);
SafeArrayGetUBound(*p,2,&ubounds[1]);
LONG elt_index[2];
for (int i=0;i<=ubounds[0];i++)
for (int j=0;j<=ubounds[1];j++)
{
elt_index[0]=i;
elt_index[1]=j;
SafeArrayGetElement(*p,elt_index,&q[i][j]);
}
delete [] ubounds;
}
void GET_SAFE_BOUNDS_MAT(SAFEARRAY **p, long & nrows, long &ncols)
{
SafeArrayGetUBound(*p,1,&nrows);
SafeArrayGetUBound(*p,2,&ncols);
nrows+=1;ncols+=1;
}
void GET_SAFE_BOUNDS_VEC(SAFEARRAY **p, long & nelts)
{
SafeArrayGetUBound(*p,1,&nelts);
++nelts;
}
double __stdcall NormSDens(double x)
{
return OneDivRootTwoPi*exp(-x*x/2);
}
double __stdcall NormSProbInv(double u)
9.2. A DLL THAT TAKES ARRAY ARGUMENTS 9
{
static double a[4]={2.50662823884,-18.61500062529,49.32119773535};
static double b[4]={-8.47351093090,23.08336743743,-21.06224101826,3.13082909833};
static double c[9]={0.3374754822726147,0.9761690190917186,
0.1607979714918209,0.0276438810333863,0.0038405729373609,
0.0003951896511919,0.0000321767881768,0.0000002888167364,0.0000003960315187};
double x=u-0.5;
double r;
if (fabs(x)<0.42)
{
double y=x*x;
r=x*(((a[3]*y+a[2])*y+a[1])*y+a[0])/((((b[3]*y+b[2])*y+b[1])*y+b[0])*y+1.0);
}
else
{
r=u;
if (x>0.0)
r=1.0-u;
r=log(-log(r));
r=c[0]+r*(c[1]+r*(c[2]+r*(c[3]+r*(c[4]+r*(c[5]+r*(c[6]+r*(c[7]+r*c[8])))))));
if (x<0.0)
r=-r;
}
return r;
}
The calling functions in VBA, together with some auxiliary functions used for output
to the spreadsheet, are:
Option Explicit
Declare Function svdc _
Lib "J:\Visual Studio Projects\Finance\Svd\Debug\Svd.dll" _
(a() As Double, w() As Double, v() As Double) As Double
Sub svd()
Dim a() As Double, u() As Double, w() As Double, v() As Double, _
nrows As Double, ncols As Double, rslt As Double
Dim i As Integer, j As Integer, k As Integer, L As Integer
Dim prod() As Double
nrows = Range("G1").Value
ncols = Range("G2").Value
ReDim a(nrows - 1, ncols - 1), u(nrows - 1, ncols - 1), w(nrows - 1), _
v(nrows - 1, ncols - 1), prod(nrows - 1, ncols - 1)
For i = 0 To nrows - 1
For j = 0 To ncols - 1
a(i, j) = Range("A3").Cells(i + 1, j + 1).Value
10 CHAPTER 9. INTERACTING WITH EXCEL
u(i, j) = a(i, j)
Next j
Next i
rslt = svdc(u, w, v)
For k = 0 To nrows - 1
For L = 0 To ncols - 1
prod(k, L) = 0
For j = 0 To ncols - 1
prod(k, L) = prod(k, L) + u(k, j) * w(j) * v(L, j)
Next j
Next L
Next k
FormatWkbkColor
InsertHead "A2", "Matrix A"
DisplayMat "A3", 5, 5, a
InsertHead "A9", "Matrix U"
DisplayMat "A10", 5, 5, u
InsertHead "A16", "Matrix V"
DisplayMat "A17", 5, 5, v
InsertHead "A23", "MatProd UDV"
DisplayMat "A24", 5, 5, prod
InsertHead "G9", "DiagMatVec D"
DisplayVec "G10", 5, 1, w
End Sub
Sub DisplayMat(TopCornerCell As String, nrows As Integer, ncols As Integer, u() As Double)
Dim i As Integer, j As Integer
For i = 0 To nrows - 1
For j = 0 To ncols - 1
Range(TopCornerCell).Cells(i + 1, j + 1).Interior.ColorIndex = xlColorIndexNone
Range(TopCornerCell).Cells(i + 1, j + 1).NumberFormat = "0.0000000000"
Range(TopCornerCell).Cells(i + 1, j + 1).Value = u(i, j)
Next j
Next i
End Sub
Sub DisplayVec(TopCornerCell As String, nrows As Integer, ncols As Integer, u() As Double)
9.3. AN AUTOMATION DLL 11
Dim i As Integer, j As Integer, val As Double
For i = 0 To nrows - 1
For j = 0 To ncols - 1
Range(TopCornerCell).Cells(i + 1, j + 1).Interior.ColorIndex = xlColorIndexNone
Range(TopCornerCell).Cells(i + 1, j + 1).NumberFormat = "0.0000000000"
If nrows = 1 Then val = u(j) Else val = u(i)
Range(TopCornerCell).Cells(i + 1, j + 1).Value = val
Next j
Next i
End Sub
Sub FormatCellColors(ran As Range)
ran.Cells.Interior.ColorIndex = 52
ran.Cells.Interior.ColorIndex = xlColorIndexNone
End Sub
Sub FormatWkbkColor()
Range("A:Z").Interior.ColorIndex = 16 23
End Sub
Sub InsertHead(CellName As String, txt As String)
Range(CellName).Interior.ColorIndex = 6 xlColorIndexNone
Range(CellName).Value = txt
End Sub
9.3 An Automation DLL
The DLLs that we have created up to the present require their functions to be registered
using VBA. The functions cannot be used in the spreadsheet. To register the functions,
we can add an interface to the DLL and make it into an XLL whose functions can be
used in the spreadsheet, once the XLL has been added as an AddIn using the Tools |
AddIns menu. The functions must still be added via Declarations in VBA in order for
them to be usable in VBA.
The XLL is a relatively old technology (1997) and Microsoft has not been supporting
them further. Some changes in the handling of strings has made them more dicult to
implement. Using the wizards in VS C++, it is easy to create a newer technology AddIn
called an Automation AddIn. These can only be added in more recent versions of Excel
(2002 onwards). The Tools | AddIns menu has a button for loading Automations. The
functions appear on the Insert Function menus and they can be used in the spreadsheet.
It is also possible to make them available for use in VBA. The following summarises
the various points:
To use your functions in VBA create a DLL as above and Declare the functions
in a VBA code module. They can also be used in the spreadsheet by writing a
12 CHAPTER 9. INTERACTING WITH EXCEL
wrapper function for them in VBA, provided the arguments of the VBA function
and its return return type are suitable.
To use the functions via an AddIn (in the spreadsheet) create an Automation
AddIn as outlined below. They can also be made available for use in VBA.
For VBA-usable functions, we use functions whose arguments and return types
are (possibly arrays of) either int, long or double.
For worksheet-usable functions, all the arguments and return types must be single
(non-array) variables of type int,long,double. Argument types can be extended
to take Range arguments and thence to extract the cell-data as doubles, but this
is tricky and requires more eort. It seems more desirable to use VBA. I do not
know how to create a usable function for the worksheet that returns an array.
Once you have included all the functions you need (worksheet and VBA) you
can save the workbook as a .xla AddIn and include it when you have the need
from the Tools | AddIns menu. To create an .xla AddIn open the workbook and
choose File | Save As and search for the .xla extension in the drop down Save as
type: BOX.
Creating the Automation AddIn
In the menu, choose File | New |Project | Visual C++ | ATL | ATL Project. Enter
a name (say, ATLTst), in the resulting wizard, click Application Settings, uncheck
the Attributed checkbox, click the DLL radio button. Click Finish.
A Solution ATLTst is created containing two projects and a number of les.
Right click on the ATLTst Project and in the drop-down menu, click the Add |
Add Class item. In the resulting wizard, select the ATL Simple Project icon.
On the next screen, enter in the textbox Short Name the name ATLMyFns and
Finish (the remaining textboxes are lled in automatically). Two new les with
this name are added, with extensions .h,.cpp.
We now add user functions. Choose menu View | Class View (or nd the tool-
bar button for Class View). Open ATLTst and right click on IATLTst (I is for
interface). Choose Add | Add Method. The Add Method wizard window appears.
In the Method name textbox write the name of the function, Norm D in this
case. The function will have a return type HRESULT, which is a system type
(not native C++) - it receives information when the function has run. The input
and output variables are all arguments of the function. Inputs to the function
may be any of the types in the drop down menu under Parameter type. Outputs
must be a pointer type in the list.
Choose type double and check the in checkbox. Enter name x for the variable.
Click Add. The variable is added to the large textbox preceded by [in]. Other
input variables can now be chosen if needed.
9.3. AN AUTOMATION DLL 13
Add a variable of type DOUBLE* with name y. Check the boxes out and retval
(return value of the function). Click Finish.
Other allowed combinations are in and out (must be of pointer type); out only
(pointer type).
Open ATLMyFns.cpp and locate the function declaration NormD. Add the code
for the function and add an include for cmath and using namespace std;. The
complete le to date is
// ATLMyFns.cpp : Implementation of CATLMyFns
#include "stdafx.h" #include "ATLMyFns.h" #include ".\atlmyfns.h"
#include <cmath> using namespace std;
// CATLMyFns
STDMETHODIMP CATLMyFns::NormD(DOUBLE x, DOUBLE* y)
{
*y=(1/sqrt(2*3.14159265358979))*exp(-x*x/2);
return S_OK;
}
Build the solution. Open Excel and go to menu Tools | Add-Ins. Click on button
Automation. Select item ATLMyFns Class. Press OK. The function NormD is
now available for use in the spreadsheet.
Enter =NormD(0.5) in a cell and press enter. The value appears. Go to menu
Insert | fx or click on the fx icon on the toolbar. In the dialog box search the
category combo box for ATLTst.ATLMyFns.1. A dialog box with the functions
appears - in this case only the function NormD. You can enter the function in the
spreadsheet by clicking it and completing the dialog.
Using the Automation DLL Functions in VBA
In the VBE (Visual Basic Editor in Excel) choose menu Tools and click on the
Browse button.
Locate the debug or release folder in your project ATLTst folder. In one of these
folders locate the ATLTst.tlb le and click Open (this is a Type Library). In the
listbox locate the entry ATLTst 1.0 Type Library. Check the box next to it.
The following sub illustrates the use of the functions in the class ATLMyFns - it
only contains one function, namely NormD.
Sub trial()
Dim oFns As ATLMyFns, d As Double
Set oFns = New ATLMyFns
d = oFns.NormD(0)
End Sub
14 CHAPTER 9. INTERACTING WITH EXCEL
9.4 Using the COM Interface and the Excel Source
Files
Excel programs can be written directly in C++ and Excel can be called from C++
using the COM (Component Object Model) interface. The programs look similar to
what one would write in VBA but extensive use is made of pointers. To obtain the MS
Excel code one must import the les mso.dll, vb6ext.olb, Excel.exe which are,
respectively, a DLL, a static library and an executable.
We will simply give a program to show how things are done.
9.4.1 Imports
The following code lines (or substitutes for them) can be placed in Imports.h and
should be included in each code module. Each code statement should appear on a
SINGLE LINE (do not use multiple lines).
#import "C:\Program Files\Common Files\Microsoft Shared\
office11\mso.dll" rename("DocumentProperties",
"DocumentPropertiesXL") rename("RGB", "RBGXL")
#import "C:\Program Files\Common Files\Microsoft Shared\VBA\VBA6\vbe6ext.olb"
#import "C:\Program Files\Microsoft Office\Office11\EXCEL.EXE"
rename("DialogBox", "DialogBoxXL") rename("RGB", "RBGXL")
rename("DocumentProperties", "DocumentPropertiesXL")
rename("ReplaceText", "ReplaceTextXL") rename("CopyFile",
"CopyFileXL") no_dual_interfaces
You must set your paths according to the le locations on your computer.
9.4.2 The C++ Code
//Main Code
#include <iostream>
#include "nr.h"
#include "Functions.h"
#include "Utilities.h"
#include <cmath>
#include <string>
#include <stdlib.h>
#include "Imports.h"
using namespace std;
9.4. USING THE COM INTERFACE AND THE EXCEL SOURCE FILES 15
//At present, the Chart Title and the Axes Titles are always the same -
//alter them in EXCEL or insert user interface to enter them from the console
//The following two arrays hold function title, function pointers, display info
//The first two must be changed if the function list is changed
//NUMFNSAVAILABLE must reflect the number of functions available for plotting
const int NUMFNSAVAILABLE=6;
//Array of function names
string FnNames[NUMFNSAVAILABLE]={"cosine","sine","NormSDens","NormSCum",
"NormSInv","Exp"};
//Following is an array of function pointers
double (*pFns[NUMFNSAVAILABLE])(double) = {cos,sin,NormSDens,NormSDist,NormSInv,exp};
void main() {
//Initialise COM
CoInitialize(NULL);
//Initiate Excel
Excel::_ApplicationPtr xl;
xl.CreateInstance(L"Excel.Application");
//Get Pointer to Sheet1, Rename
while(true) {
//Add a workbook. Get pointer to the workbook
xl->Workbooks->Add((long) Excel::xlWorksheet);
Excel::_WorkbookPtr pWorkbook = xl->ActiveWorkbook;
Excel::_WorksheetPtr pSheet = pWorkbook->Worksheets->GetItem("Sheet1");
pSheet->Name = "Chart Data";
//Set up data structures
double leftendpt,rightendpt;
int N; //Number of points to plot
int numfns; //Number of functions to plot
//Setup array which will record which functions to graph - all entries 0 initially
int process[NUMFNSAVAILABLE]={0};
//Get User input
SetUpData(numfns,N,leftendpt,rightendpt,process,FnNames,NUMFNSAVAILABLE);
//Get Points on X-Axis, Write it to the worksheet
16 CHAPTER 9. INTERACTING WITH EXCEL
double xval=leftendpt;
double dx=(rightendpt-leftendpt)/((double)N);
Vec_IO_DP xvals(N+1);
for(int i=0;i<N+1;i++)
{
xvals[i]=xval;
xval+=dx;
}
WriteColumn(pSheet,1,1,"xvals",xvals);
//Calculate the function values and write them to sheet
Vec_IO_DP yvals(N+1);
CalcFn(N+1,xvals,cos,yvals);
int writecol=2;
for (int i=0;i<NUMFNSAVAILABLE;i++)
{ if (process[i]==0)continue;
CalcFn(N+1,xvals,pFns[i],yvals);
WriteColumn(pSheet,1,writecol,FnNames[i].c_str(),yvals);
++writecol;
}
// Create range objects for source data.
long beginRow=1,endRow=N+2,beginColumn=1,endColumn=numfns+1;
Excel::RangePtr pBeginRange = pSheet->Cells->Item[beginRow][beginColumn];
Excel::RangePtr pEndRange = pSheet->Cells->Item[endRow][endColumn];
Excel::RangePtr pTotalRange = pSheet->Range[(Excel::Range*)pBeginRange]
[(Excel::Range*)pEndRange];
// Create the chart,set its type, set sourcedata from spreadsheet, set its location
Excel::_ChartPtr pChart=xl->ActiveWorkbook->Charts->Add();
pChart->PutChartType(Excel::xlXYScatterSmoothNoMarkers);
pChart->SetSourceData(pTotalRange,Excel::xlColumns);
pChart->Location(Excel::xlLocationAsNewSheet,"Chart");
//FORMAT THE CHART
const double FONTSIZE = 10;
Excel::AxisPtr pAxis = pChart->Axes((long)Excel::xlValue, Excel::xlPrimary);
pAxis->TickLabels->Font->Name="Arial";
pAxis->HasMajorGridlines = false;
//pAxis->MinimumScale = -1.2;
//pAxis->MaximumScale = 1.2;
9.4. USING THE COM INTERFACE AND THE EXCEL SOURCE FILES 17
Excel::AxisPtr pAxisCat = pChart->Axes((long)Excel::xlCategory, Excel::xlPrimary);
pAxisCat->MinimumScale = leftendpt;
pAxisCat->MaximumScale = rightendpt;
pAxis->TickLabels->Font->Name="Arial";
pAxis->TickLabels->Font->Size = FONTSIZE;
pAxis->TickLabels->Font->FontStyle = "Regular";
pAxis->HasTitle = true;
pAxisCat->HasTitle = true;
pAxis->AxisTitle->Text = "Prob";
pAxisCat->AxisTitle->Text = "Point";
//pAxis->MinorUnit = .01;
//pAxis->MajorUnit = .001;
Excel::ChartAreaPtr pChartArea = pChart->ChartArea;
pChartArea->Border->Weight = 3;
pChartArea->Border->LineStyle = -1;
Excel::SeriesPtr pSeries = pChart->SeriesCollection(1);
pSeries->Border->Weight = Excel::xlThick;
pSeries->Border->LineStyle = Excel::xlAutomatic;
pSeries->Border->ColorIndex = 3;
for(i=1;i<numfns;i++)
{
pSeries=pChart->SeriesCollection(i+1);
pSeries->Border->Weight = Excel::xlThick;
pSeries->Border->LineStyle = Excel::xlAutomatic;
pSeries->Border->ColorIndex = 3+2*i;
}
pChart->PlotArea->Fill->ForeColor->SchemeColor = 50;
pChart->PlotArea->Fill->BackColor->SchemeColor = 43;
pChart->PlotArea->Fill->TwoColorGradient(Office::msoGradientHorizontal,1);
pChart->ChartArea->Fill->ForeColor->SchemeColor = 20;
pChart->ChartArea->Fill->BackColor->SchemeColor = 36;
pChart->ChartArea->Fill->TwoColorGradient(Office::msoGradientHorizontal,1);
pChart->HasLegend = true;
pChart->Legend->Position = Excel::xlLegendPositionRight;
pChart->Legend->Fill->ForeColor->SchemeColor = 20;
pChart->Legend->Fill->BackColor->SchemeColor = 36;
18 CHAPTER 9. INTERACTING WITH EXCEL
pChart->Legend->Fill->OneColorGradient(Office::msoGradientHorizontal,1,0.5);
pChart->Legend->Font->Size = FONTSIZE - 4;
pChart->PlotArea->Border->ColorIndex = 16;
pChart->PlotArea->Border->Weight = Excel::xlThin;
pChart->PlotArea->Border->LineStyle = Excel::xlContinuous;
pChart->HasTitle = true;
pChart->ChartTitle->Text = "Normals";
pChart->ChartTitle->Font->Size = FONTSIZE + 5;
//Display the spreadsheet
xl->Visible = VARIANT_TRUE;
//Prevent program from terminating
cout << endl << endl << "Click the Close button at top right to quit" << endl;
cout << endl << "Enter c to draw another chart" << endl;
char ch;
cin >> ch;
}
//Release Excel and COM
xl.Release();
CoUninitialize();
}
//Imports.h Each Import to go on a single line
#import "C:\Program Files\Common Files\Microsoft Shared\office11\mso.dll"
rename("DocumentProperties", "DocumentPropertiesXL") rename("RGB", "RBGXL")
#import "C:\Program Files\Common Files\Microsoft Shared\VBA\VBA6\vbe6ext.olb"
#import "C:\Program Files\Microsoft Office\Office11\EXCEL.EXE"
rename("DialogBox", "DialogBoxXL") rename("RGB", "RBGXL")
rename("DocumentProperties", "DocumentPropertiesXL")
rename("ReplaceText", "ReplaceTextXL") rename("CopyFile", "CopyFileXL")
no_dual_interfaces
//Functions.h
#ifndef functions_h
#define functions_h
double NormSDens(double x);
double NormSInv(double u);
9.4. USING THE COM INTERFACE AND THE EXCEL SOURCE FILES 19
double NormSDist(double x);
#endif
//Functions.cpp
#include <cmath>
#include "functions.h"
using namespace std;
const double OneDivRootTPi =1/sqrt(2*3.14159);// 0.398942280401433;
double NormSInv(double u) //Inverse Cum Normal
{
static double a[4]={2.50662823884,-18.61500062529,41.39119773534,-25.44106049637};
static double b[4]={-8.47351093090,23.08336743743,-21.06224101826,3.13082909833};
static double c[9]={0.3374754822726147,0.9761690190917186,0.1607979714918209,0.0276438810333863,
0.0038405729373609,0.0003951896511919,0.0000321767881768,0.0000002888167364,0.0000003960315187};
double x=u-0.5;
double r;
if (fabs(x)<0.42)
{
double y=x*x;
r=x*(((a[3]*y+a[2])*y+a[1])*y+a[0])/((((b[3]*y+b[2])*y+b[1])*y+b[0])*y+1.0);
}
else
{
r=u;
if (x>0.0)
r=1.0-u;
r=log(-log(r));
r=c[0]+r*(c[1]+r*(c[2]+r*(c[3]+r*(c[4]+r*(c[5]+r*(c[6]+r*(c[7]+r*c[8])))))));
if (x<0.0)
r=-r;
}
return r;
}
double NormSDens(double x) //Normal Density
{
return OneDivRootTPi*exp(-x*x/2);
}
20 CHAPTER 9. INTERACTING WITH EXCEL
double NormSDist(double x) //Cumulative Normal Density
{
static double a[5]={0.319381530,-0.356563782,1.718477937,-1.821255978,1.330274429};
if (x<-7.0)
return NormSDens(x)/sqrt(1.0+x*x);
else
{
if (x>7.0)
return 1.0-NormSDist(-x);
else
{
double k=1.0/(1.0+0.2316419*fabs(x));
double tmp2=NormSDens(x)*(k*(a[0]+k*(a[1]+k*(a[2]+k*(a[3]+k*a[4])))));
if (x<0.0)
return tmp2;
else
return 1.0-tmp2;
}
}
}
//Utilities.h
#ifndef utilities_h
#define utilities_h
#include "Imports.h"
void WriteColumn(Excel::_WorksheetPtr sheet,long sheetRow,long sheetColumn,
const char* label,Vec_IO_DP & values);
void WriteRow(Excel::_WorksheetPtr sheet,long sheetRow,
long sheetColumn,string * values,int numfns);
void SetUpData(int &numfns,int &N,double &leftendpt,double &rightendpt,
int *process,string * FnNames,const int numfnsavailable);
void CalcFn(int N,Vec_IO_DP &xvals,double (func)(double),Vec_IO_DP &yvals);
#endif
//Utilities.cpp
#include <iomanip>
#include <string>
#include "nr.h"
#include "Imports.h"
#include "Utilities.h"
using namespace std;
void CalcFn(int N,Vec_IO_DP &xvals,double (func)(double),Vec_IO_DP &yvals)
9.4. USING THE COM INTERFACE AND THE EXCEL SOURCE FILES 21
{
for(int i=0;i<N;i++)
{
yvals[i]=func(xvals[i]);
}
}
void SetUpData(int &numfns,int &N,double &leftendpt,double &rightendpt,
int * process,string *FnNames,const int numfnsavailable)
{ int yn;numfns=0;
cout << "List of Functions Available for Graphing:" << endl <<endl;
for(int i=0;i<numfnsavailable;i++)
cout << FnNames[i].c_str() << "=" << i+1 << "\t";
cout << endl;
cout << endl << "Input Number of Points: ";
cin >> N;
cout << endl << "Input Lefthand Endpoint: ";
cin >> leftendpt;
cout << endl << "Input Righthand Endpoint: ";
cin >> rightendpt;
do
{cout << endl << "Input Function Number to be Plotted, to Finish Enter 9: ";
cin >> yn;
if ((yn<7) && (yn>0)){process[yn-1]=1;++numfns;}
}while ((yn<7) && (yn>0));
}
void WriteColumn(Excel::_WorksheetPtr sheet,long sheetRow,
long sheetColumn,const char* label,Vec_IO_DP & values)
{
// Get cells.
Excel::RangePtr pRange=sheet->Cells;
// First cell contains the label.
pRange->Item[sheetRow][sheetColumn] = label;
sheetRow++;
// Next cells contain the values.
for(int i=0; i < values.size(); i++)
{
pRange->Item[sheetRow][sheetColumn] = values[i];
22 CHAPTER 9. INTERACTING WITH EXCEL
sheetRow++;
}
}
void WriteRow(Excel::_WorksheetPtr sheet,long sheetRow,
long sheetColumn,string * values,int numfns)
{
// Get cells.
Excel::RangePtr pRange=sheet->Cells;
// Next cells contain the values.
for(int i=0; i < numfns; i++)
{
pRange->Item[sheetRow][sheetColumn] = values[i].c_str();
sheetColumn++;
}
}
9.5. CREATING XLLS WITH XLW 23
9.5 Creating XLLs with XLW
XLW is an open source program available at http://www.sourceforge.net - also on
the cd provided for the course. It simplies the construction of XLLs consisting of func-
tions usable in the spreadsheet. Documentation on the classes and member functions
can be found in the installation by loading the le XLW\docs\index.chm. A solution
that is already built is available in XLW\xlwExample. You will nd there an Excel
spreadsheet with the functions from the XLL produced when the solution is built. In
the function wizard, the functions added are in the category xlw Example.
In order to produce your own XLL, one way is to add another project to the xlw solution
in XLW\xlwExample by adding from the File|Add Project (it should be an Empty DLL
project). Then add a cpp le and use the patterns exhibited in the xlwExample.cpp
le. We will discuss the steps that must be taken below.
The same three Win32StreamBuf modules as in xlwExample should be added to
the source les if they are not already present in the source les of another project
in the solution.
The main cpp le should begin with the following (and in addition, add any
includes necessary for your functions):
#include "c:\Program Files\xlw\xlw\xlw.h"
#include <iostream>
#include <sstream>
#include <vector>
#include "Win32StreamBuf.h"
Win32StreamBuf debuggerStreamBuf;
std::streambuf * oldStreamBuf;
extern "C"
{
....... add body of file discussed below
}
The following is an example of a function added
LPXLOPER EXCEL_EXPORT xlCirc(XlfOper xlDiam)
{
EXCEL_BEGIN;
// Converts d to a double.
double ret=xlDiam.AsDouble();
// Multiplies it.
ret *= 3.14159;
// Returns the result as a XlfOper.
return XlfOper(ret);
EXCEL_END;
}
24 CHAPTER 9. INTERACTING WITH EXCEL
XLOPER,LPXLOPER are, respectively, a data type and a pointer to this type
in C++ which is a union of other types and is similar to the Excel variant
type. Excel functions accept these types in their functions. EXCEL_EXPORT is
a macro indicating the function type and has value __declspec(dllexport).
XlfOper is a C++ wrapper for the XLOPER type. xlCirc is the function name.
EXCEL_BEGIN,EXCEL_END must come at the beginning and end of the function
body. They facilitate error handling and if you wish to use them, look at
their denition in macros.h. The code needs to be modied.
xlDiam is of type XlfOper, which is a class and has a number of constructors
allowing conversion from C++ types to XlfOper and methods converting
from XlfOper to native and other C++ types. xlDiam.AsDouble() converts
from XlfOper to C++ type double.
XlfOper(ret) converts the double variable ret to a LPXLOPER which is
accepted by Excel.
LPXLOPER EXCEL_EXPORT xlConcat(XlfOper xlStr1, XlfOper xlStr2)
{
EXCEL_BEGIN;
// Converts the 2 strings.
std::string str1 = xlStr1.AsString();
std::string str2 = xlStr2.AsString();
// Returns the concatenation of the 2 string as a XlfOper.
std::string ret = str1+str2;
return XlfOper(ret.c_str());
EXCEL_END;
}
Here, the xlStr1 is converted to a standard string as is xlStr12 , they are
concatenated and the result converted to a LPXLOPER and returned.
LPXLOPER EXCEL_EXPORT xlStats(XlfOper xlTargetRange)
{
EXCEL_BEGIN;
// Temporary variables.
double averageTmp = 0.0;
double varianceTmp = 0.0;
std::vector<double> temp = xlTargetRange.AsDoubleVector(XlfOper::RowMajor);
// All cells are copied. We do the actual work.
size_t popSize = temp.size();
for (size_t i = 0; i < popSize; ++i)
{
// sums the values.
averageTmp += temp[i];
9.5. CREATING XLLS WITH XLW 25
// sums the squared values.
varianceTmp += temp[i] * temp[i];
}
// Initialization of the resultArray.
double resultArray[2];
// compute average.
double& average = resultArray[0];
average = averageTmp / popSize;
// compute variance
double& variance = resultArray[1];
variance = varianceTmp / popSize - average * average;
// Create the XlfOper returned with the resultArray containing the values.
return XlfOper(1,2,resultArray);
EXCEL_END;
}
temp is the conversion of an Excel range XLOPER (several rows and columns)
to a single standard row vector (in row-major order). Alternatives are
UniDimensional,RowMajor,ColumnMajor).
A C++ resultArray is initialised and the mean and variance of the numbers
in the range are computed and placed in the two elements of the result vector.
The C++ vector is converted to a LPXLOPER and returned.
We do not discuss the functions xlIsInWiz,NbCalls here. The rst function is
instructive as it shows how to deal with a cell address (or reference).
Two other functions must be added: AutoOpen, AutoClose. These are run by
Excel - the rst registers the functions and the second releases Excel and the
steambuer. There are other such functions but they are not essential and the
example here does not use them.
long EXCEL_EXPORT xlAutoOpen()
{
oldStreamBuf = std::cerr.rdbuf(&debuggerStreamBuf);
std::cerr << __HERE__ << "std::cerr redirected to MSVC debugger" << std::endl;
// Displays a message in the status bar.
XlfExcel::Instance().SendMessage("Registering library...");
// Registers the first function xlCirc.
// Variable description of type R (default).
XlfArgDesc diameter("diameter","Diameter of the circle");
// xlCirc function description function as Circ.
XlfFuncDesc circ("xlCirc","Circ",
"Computes the circumference of a circle","xlw Example");
// Set the diameter argument definition for circ.
circ.SetArguments(diameter);
26 CHAPTER 9. INTERACTING WITH EXCEL
// Registers the function circ.
circ.Register();
// Registers the second function xlConcat.
// Argument descriptions.
XlfArgDesc str1("string1","First string");
XlfArgDesc str2("string2","Second string");
// xlConcat function description as concat.
XlfFuncDesc concat("xlConcat","Concat","Concatenate two strings in one",
"xlw Example");
// Set the arguments for the function. Note how you
//create a XlfArgDescList from two or more XlfArgDesc (operator+).
//You can not push the XlfArgDesc one by one.
concat.SetArguments(str1+str2);
// Registers the concat function.
concat.Register();
// Registers the third function xlStats.
// Argument descriptions.
XlfArgDesc pop("Population","Target range containing the population");
// xlStats Function description
XlfFuncDesc stats("xlStats","Stats",
"returns a (1x2) range containing the average and
the variance of a numeric population","xlw Example");
// Sets the arguments for the function.
stats.SetArguments(pop);
// Registers the stats function.
stats.Register();
// Registers the fourth function xlIsInWiz.
XlfFuncDesc isInWiz("xlIsInWiz","IsInWiz",
"returns true if the function is called from the
function wizard, and the address of the caller otherwise",
"xlw Example");
isInWiz.Register();
// Registers the fifth function xlNumberOfCall as volatile
(unconditionally recalculated)
XlfFuncDesc nbCalls("xlNbCalls","NbCalls",
"returns the number of times the function has been
calculated since the xll was loaded (volatile)",
"xlw Example",XlfFuncDesc::Volatile);
nbCalls.Register();
// Clears the status bar.
XlfExcel::Instance().SendMessage();
9.5. CREATING XLLS WITH XLW 27
return 1;
}
For each function, we need to create two objects: XlfArgDesc,XlfFuncDesc
(see the details in the header les of the same name).
The arguments of XlfArgDesc are given by
XlfArgDesc(const std::string& name, const std::string& comment, char type=R);
The rst argument gives the function name in Excel and the second gives a
comment listed in the Excel function wizard. The third argument has a de-
fault which is R and means that the input argument is an XLOPER. For this
implementation, it is recommended that all input arguments be of this type.
If there is more than one argument, an XlfArgDesc object must be created for
each argument and then a list of them (str1+str2 for the concat function)
created and then passed to SetArguments (concat.SetArguments(str1+str2);).
The arguments of XlfFunDesc are given by
XlfFuncDesc(const std::string& name, const std::string& alias,
const std::string& comment, const std::string& category,
RecalcPolicy recalcPolicy = NotVolatile);
name is the function name in this (example) program. alias is the name
it will be known as in Excel. category is the category name under which
the function will appear in the Excel function wizard. If you change the
default argument by entering Volatile, the function will recalculate in the
spreadsheet when it undergoes a minimal change (see the xlNbCalls function
for an example).
The variable (for example pop) of the function (for example stats) must
have its argument set via stats.SetArguments(pop);.
The function must be registered via, for example, stats.Register();.
28 CHAPTER 9. INTERACTING WITH EXCEL
The entire xlwExample.cpp module
/*
Copyright (C) 1998, 1999, 2001, 2002 Jrme Lecomte
This file is part of xlw, a free-software/open-source C++ wrapper of the
Excel C API - http://xlw.sourceforge.net/
xlw is free software: you can redistribute it and/or modify it under the
terms of the xlw license. You should have received a copy of the
license along with this program; if not, please email xlw-users@lists.sf.net
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the license for more details.
*/
// $Id: xlwExample.cpp,v 1.12 2003/03/08 22:21:02 jlecomte Exp $
#include "c:\Program Files\xlw\xlw\xlw.h"
#include <iostream>
#include <sstream>
#include <vector>
#include "Win32StreamBuf.h"
Win32StreamBuf debuggerStreamBuf;
std::streambuf * oldStreamBuf;
extern "C"
{
LPXLOPER EXCEL_EXPORT xlCirc(XlfOper xlDiam)
{
EXCEL_BEGIN;
// Converts d to a double.
double ret=xlDiam.AsDouble();
// Multiplies it.
ret *= 3.14159;
// Returns the result as a XlfOper.
return XlfOper(ret);
EXCEL_END;
}
LPXLOPER EXCEL_EXPORT xlConcat(XlfOper xlStr1, XlfOper xlStr2)
{
EXCEL_BEGIN;
9.5. CREATING XLLS WITH XLW 29
// Converts the 2 strings.
std::string str1 = xlStr1.AsString();
std::string str2 = xlStr2.AsString();
// Returns the concatenation of the 2 string as a XlfOper.
std::string ret = str1+str2;
return XlfOper(ret.c_str());
EXCEL_END;
}
LPXLOPER EXCEL_EXPORT xlStats(XlfOper xlTargetRange)
{
EXCEL_BEGIN;
// Temporary variables.
double averageTmp = 0.0;
double varianceTmp = 0.0;
// XlfExcel::Coerce method (internally called) will return to Excel
// if one of the cell was invalidated and need to be recalculated.
// Excel will calculate this cell and call again our function.
// Thus we copy first all the data to avoid to partially compute the
// average for nothing since one of the cell might be uncalculated.
std::vector<double> temp = xlTargetRange.AsDoubleVector(XlfOper::RowMajor);
// All cells are copied. We do the actual work.
size_t popSize = temp.size();
for (size_t i = 0; i < popSize; ++i)
{
// sums the values.
averageTmp += temp[i];
// sums the squared values.
varianceTmp += temp[i] * temp[i];
}
// Initialization of the resultArray.
double resultArray[2];
// compute average.
double& average = resultArray[0];
average = averageTmp / popSize;
// compute variance
double& variance = resultArray[1];
variance = varianceTmp / popSize - average * average;
// Create the XlfOper returned with the resultArray containing the values.
return XlfOper(1,2,resultArray);
EXCEL_END;
}
30 CHAPTER 9. INTERACTING WITH EXCEL
/*!
* Demonstrates how to detect that the function is called by
* the function wizard, and how to retrieve the coordinates
* of the caller cell
*/
LPXLOPER EXCEL_EXPORT xlIsInWiz()
{
EXCEL_BEGIN;
// Checks if called from the function wizard
if (XlfExcel::Instance().IsCalledByFuncWiz())
return XlfOper(true);
// Gets reference of the called cell
XlfOper res;
XlfExcel::Instance().Call(xlfCaller,res,0);
XlfRef ref=res.AsRef();
// Returns the reference in A1 format
std::ostringstream ostr;
char colChar=A+ref.GetColBegin();
ostr << colChar << ref.GetRowBegin() + 1 << std::ends;
std::string address=ostr.str();
return XlfOper(address.c_str());
EXCEL_END;
}
/*!
* Registered as volatile to demonstrate how functions can be
* recalculated automatically even if none of the arguments
* have cahnged.
*
* \return the number of time the function has been called.
*/
LPXLOPER EXCEL_EXPORT xlNbCalls()
{
EXCEL_BEGIN;
static short nbCalls = 0;
++nbCalls;
return XlfOper(nbCalls);
EXCEL_END;
}
9.5. CREATING XLLS WITH XLW 31
long EXCEL_EXPORT xlAutoOpen()
{
oldStreamBuf = std::cerr.rdbuf(&debuggerStreamBuf);
std::cerr << __HERE__ << "std::cerr redirected to MSVC debugger" << std::endl;
// Displays a message in the status bar.
XlfExcel::Instance().SendMessage("Registering library...");
// Registers the first function xlCirc.
// Variable description of type R (default).
XlfArgDesc diameter("diameter","Diameter of the circle");
// xlCirc function description function as Circ.
XlfFuncDesc circ("xlCirc","Circ",
"Computes the circumference of a circle","xlw Example");
// Set the diameter argument definition for circ.
circ.SetArguments(diameter);
// Registers the function circ.
circ.Register();
// Registers the second function xlConcat.
// Argument descriptions.
XlfArgDesc str1("string1","First string");
XlfArgDesc str2("string2","Second string");
// xlConcat function description as concat.
XlfFuncDesc concat("xlConcat","Concat",
"Concatenate two strings in one","xlw Example");
// Set the arguments for the function.
//Note how you create a XlfArgDescList from
// two or more XlfArgDesc (operator+).
//You can not push the XlfArgDesc one by one.
concat.SetArguments(str1+str2);
// Registers the concat function.
concat.Register();
// Registers the third function xlStats.
// Argument descriptions.
XlfArgDesc pop("Population","Target range containing the population");
// xlStats Function description
XlfFuncDesc stats("xlStats","Stats","returns a (1x2) range containing
the average and the variance of a numeric population","xlw Example");
// Sets the arguments for the function.
stats.SetArguments(pop);
// Registers the stats function.
stats.Register();
32 CHAPTER 9. INTERACTING WITH EXCEL
// Registers the fourth function xlIsInWiz.
XlfFuncDesc isInWiz("xlIsInWiz","IsInWiz","returns true if the function is called
from the function wizard, and the address of the caller otherwise","xlw Example");
isInWiz.Register();
// Registers the fifth function xlNumberOfCall as volatile (unconditionally recalculated)
XlfFuncDesc nbCalls("xlNbCalls","NbCalls","returns the number of times
the function has been calculated since the xll was loaded (volatile)",
"xlw Example",XlfFuncDesc::Volatile);
nbCalls.Register();
// Clears the status bar.
XlfExcel::Instance().SendMessage();
return 1;
}
long EXCEL_EXPORT xlAutoClose()
{
std::cerr << __HERE__ << "Releasing ressources" << std::endl;
delete &XlfExcel::Instance();
std::cerr.rdbuf(oldStreamBuf);
return 1;
}
}

S-ar putea să vă placă și