0 evaluări0% au considerat acest document util (0 voturi)
93 vizualizări32 pagini
C++ has no built in facility for drawing graphs and for displaying data in an attractive way. We will construct simple DLL's (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.
C++ has no built in facility for drawing graphs and for displaying data in an attractive way. We will construct simple DLL's (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.
C++ has no built in facility for drawing graphs and for displaying data in an attractive way. We will construct simple DLL's (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.
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; } }