You are on page 1of 10

Calling IMSL C# from Excel

Calling the IMSL C# Numerical Library from Microsoft Excel


q q q

Background The Back End (Visual Basic.NET) The Front End (Excel / VBA) r Passing Multiple Parameters r Passing An Array r Returning An Array r Working with 2D Arrays Summary

Background
Microsoft offers an API and several documented methods to get your data from Excel to a .NET application; however, a very common need for our customers is to include some IMSL functionality in an Excel spreadsheet. This is the reverse direction that is easily supported. While this path may become easier as the .NET platform evolves and becomes more integrated with Microsoft Office, it currently requires using COM (Component Object Model) interoperability. This COM requirement places some constraints on the code to be used in this example. First, the ComClass attribute must be used, which is not supported in the C# language. As a result, Visual Basic will be the programming language on the back end instantiating IMSL objects. Additionally, it is possible to build the example from the command line, but this example will focus on using Visual Studio.NET.

The Back End


The basic steps in this section are to write the wrapper code (in Visual Basic.NET) and to register the assembly with COM. To begin, create a new Visual Basic.NET project in Visual Studio.NET. Use the Class Library template as we will build a DLL to register with COM. Our project is named ExcelTest for this example, and the main source file has the default name Class1.vb. The basic template for this library will be: Namespace test

file:///C|/Program Files/VNI/IMSLCS4.0.2_2.0/IMSLCS4.0.2/manual/Calling IMSL C (1 of 10)1/26/2007 11:09:32 AM

Calling IMSL C# from Excel

<ComClass(TestIMSL.ClassId, TestIMSL.InterfaceId, TestIMSL. EventsId)> _ Public Class TestIMSL Public Const ClassId As String = "6D0BC857-E796-4b39-BCB2316ED4AF7F37" Public Const InterfaceId As String = "3B5C6011-E0F9-4cb5-A23D4628A7388FE9" Public Const EventsId As String = "CB080278-0452-49a9-927AD2F946BC2BC7" Public Sub New() MyBase.New() End Sub End Class End Namespace Make note of the empty constructor; this is required for the library to register correctly according to documentation on MSDN . The 16-byte (128-bit) hex identification strings are known as GUIDs (Globally Unique Identifiers) and must be unique for the library to avoid binary confusion. To create these for your library, use Create GUID from the Tools menu in the IDE and use the fourth format, Registry Format, and then use the Copy button to place a copy of the generated GUID on the clipboard to paste into the source code window. These will be wrapped in curly braces {} that will need to replace with double quotes "". Further note that the ComClass attribute must be on the same line as the class declaration. If you want to separate them for readability, use _ (space underscore) to indicate line continuation in the source code. Before getting further into details, lets add to the code so that it has something to do. To begin, we will create two public methods that each returns a single double value. One accepts no arguments and the other accepts a double value. Do not set the seed in the constructor for the Random object or you will get the same random number every time. Because the IMSL library is being called, it will have to be added as a reference by browsing to ImslCS.dll from the menu under Project, Add Reference on the Projects tab. The entire Visual Basic.NET code so far looks like: Imports Imsl.Math Imports Imsl.Stat Namespace test <ComClass(TestIMSL.ClassId, TestIMSL.InterfaceId, TestIMSL. EventsId)> _ Public Class TestIMSL Public Const ClassId As String = "6D0BC857-E796-4b39-BCB2316ED4AF7F37"
file:///C|/Program Files/VNI/IMSLCS4.0.2_2.0/IMSLCS4.0.2/manual/Calling IMSL C (2 of 10)1/26/2007 11:09:32 AM

Calling IMSL C# from Excel

Public Const InterfaceId As String = "3B5C6011-E0F9-4cb5-A23D4628A7388FE9" Public Const EventsId As String = "CB080278-0452-49a9-927AD2F946BC2BC7" Dim R As New Imsl.Stat.Random Public Function NextNormal() As Double Return R.NextNormal() End Function Public Function Erf(ByVal dIn As Double) As Double Return Sfun.Erf(dIn) End Function Public Sub New() MyBase.New() End Sub End Class End Namespace The assembly will need to be added to the Global Assembly Cache (GAC) to allow Excel to find it. This requires that the file be Strong Named. With Microsoft Visual Studio 2003 the Strong Name Key file (. snk) can be generated with the sn utility. Using the Visual Studio .NET 2003 Command Prompt, which can be found on the Start Menu, create a key file using the command: sn -k MySNFile.snk The project references a key file by adding the following line the end of AssemblyInfo.vb: <Assembly: AssemblyKeyFile("path\MySNFile.snk")> Registering the COM Object Now, lets configure the build properties of the library. Select your projects properties from the Project menu. Under Common Properties, General, be sure the Output Type is Class Library. Then under Configuration Properties, Build, check the box next to Register for COM Interop. If you were not using Visual Studio.NET, the assembly would be registered with COM by using the command line utility: regasm /tlb ExcelTest.dll This can be used any time to manually update the version registered with COM.

file:///C|/Program Files/VNI/IMSLCS4.0.2_2.0/IMSLCS4.0.2/manual/Calling IMSL C (3 of 10)1/26/2007 11:09:32 AM

Calling IMSL C# from Excel

With all of the code in place and the IDE options configured we are ready to build the assembly. After compiling with Build, Build Solution, youll have a new ExcelTest.dll in the \bin directory of your project. If you notice under the Output window in Visual Studio.NET, there are lines Unregistering previous project output for COM Interop at the top and then Registering project output for COM Interop at the bottom, so the DLL is registered with COM automatically each time you build the solution. Finally the COM Object should be loaded into the Global Assembly Cache (GAC) in order to share it generally. From the Visual Studio .NET 2003 Command Prompt, type: gacutil -i ExcelTest.dll If necessary, it can be removed from the GAC later using the -u switch. Now that the library as been compiled and registered with the system, we can move on to the Excel side.

The Front End


Start a new Excel workbook and open the Visual Basic Editor (Tools, Macro, Visual Basic Editor or AltF11). First, add your newly registered assembly, ExcelTest, as a reference from Tools, References and scroll down the list to find the library. Check its box and click OK. Now create a new module (Insert, Module) and create a couple functions that call the methods we created on the back end: Public Function GetRand() As Double Dim o As New ExcelTest.TestIMSL GetRand = o.NextNormal() End Function Public Function GetErf(dbl) As Double Dim o As New ExcelTest.TestIMSL GetErf = o.Erf(dbl) End Function As you type the Dim line in these functions, the code completion popup should show your ExcelTest reference and TestIMSL class automatically. And then once you type the o. the available methods should appear. These dialogs confirm that the dll has been properly built, registered, and added as a reference. Save the workbook and go back to the standard Excel view. To use the GetRand() method, select cell A1 and enter: =GetRand()

file:///C|/Program Files/VNI/IMSLCS4.0.2_2.0/IMSLCS4.0.2/manual/Calling IMSL C (4 of 10)1/26/2007 11:09:32 AM

Calling IMSL C# from Excel

as the formula. After pressing Enter, you should see the contents of this cell as some random value like: 0.800388 To test the GetErf() method, enter the value 0.5 in cell B1 for example. And then in cell B2, enter: =GetErf(B1) as the formula. After pressing Enter, you should see: 0.5205 Note that if you need to modify your Visual Basic.NET code while Excel is open with a macro that calls the assembly, you will not be able to rebuild the solution. Visual Studio will report a build error like COM Interop registration failed. Access is denied. or The process cannot access the file because it is being used. To modify the back end, Excel must be closed first.

Passing Multiple Parameters

We can extend these examples to multiple arguments. For example, consider the Chi method of Imsl. Stat.Cdf. This method accepts two input parameters and returns a single value. In this case, on the VB. NET side, our method might look like: Public Function ChiCdf(ByVal xIn As Double, ByVal nIn As Double) As Double Return Cdf.Chi(xIn, nIn) End Function On the Excel side, we shall accept two parameters in the macro: Public Function GetChi(x, n) As Double Dim o As New ExcelTest.TestIMSL GetChi = o.ChiCdf(x, n) End Function And then the spreadsheet function would need to have two values passed in as well, so set cell C1 as 0.15 and cell C2 as 2.0, and then in C3 type: =GetChi(C1,C2)
file:///C|/Program Files/VNI/IMSLCS4.0.2_2.0/IMSLCS4.0.2/manual/Calling IMSL C (5 of 10)1/26/2007 11:09:32 AM

Calling IMSL C# from Excel

And you should see: 0.072257

Passing an Array

Next we can extend to passing an array of values to VB.NET for analysis with IMSL C#. For this example, our VB.NET code looks like: Public Function Sign(ByRef xIn() As Double) As Double Dim st As SignTest st = New SignTest(xIn) Return st.Compute End Function Here the input parameter is a double array; notice it is passed by reference (ByRef) instead of by value (ByVal) which is how the scalar doubles had been passed. We are returning a scalar double again. The VBA Excel function is a little different now. Instead of typing in a function, we need to pass an array or a range of cell values. This is most easily coded by adding a button control and manually coding the range of cells to pass. Turn on the Control Toolbox toolbar by right-clicking on the Excel toolbar and selecting Control Toolbox off the list. Select a Command Button and draw an outline in the spreadsheet. Right-click on the new button and choose CommandButton Object, Edit and rename the button as RunSign. Next right-click on the button again and choose View Code. This will bring you back to the Visual Basic Editor with a new page (this time on Sheet1 instead of Module1). Fill in the callback function as follows: Private Dim Dim Dim Dim Sub CommandButton1_Click() a(18) As Double i As Integer v As Double o As New ExcelTest.TestIMSL

' load array For i = 0 To 18 a(i) = Cells(i + 1, 4) Next i ' call IMSL routine
file:///C|/Program Files/VNI/IMSLCS4.0.2_2.0/IMSLCS4.0.2/manual/Calling IMSL C (6 of 10)1/26/2007 11:09:32 AM

Calling IMSL C# from Excel

v = o.Sign(a) Range("D21").Select ActiveCell.FormulaR1C1 = v End Sub The input array is the range of cells D1:D19 that are being placed in the double array a using the Cells() function. The double returned from Sign() is placed in cell D21. Return to the spreadsheet and toggle the first button on the Control Toolbox to Exit Design Mode; you can close this toolbar now. To test this example, place these values in cells D1 through D19: 92 139 -6 10 81 -11 45 -25 -4 22 2 41 13 8 33 45 -33 -45 -12 Click the RunSign button and cell D21 will show: 0.179642

Returning an Array

Besides passing an array of values, wed like to be able to return an array of values. This example returns Gamma(x,a) for 21 values of x [0,20] for a given value of a. The wrapper code is: Public Function Gamma(ByVal aIn As Double) As Double()
file:///C|/Program Files/VNI/IMSLCS4.0.2_2.0/IMSLCS4.0.2/manual/Calling IMSL C (7 of 10)1/26/2007 11:09:32 AM

Calling IMSL C# from Excel

Dim i As Double Dim g(20) As Double For i = 0 To 20 g(i) = Cdf.Gamma(i, aIn) Next i Return g End Function On the Excel side, we need to pass the input parameter and return an array. As above, we will use another Command Button called RunGamma. The input value will be read from cell E1 and the output array will be written to the range E3:E23: Private Sub CommandButton2_Click() Dim o As New ExcelTest.TestIMSL ReDim g(20) As Double Dim x As Double Dim i As Integer ' get input and call IMSL Range("E1").Select x = ActiveCell.Value g = o.Gamma(x) ' copy result to output cells For i = 0 To 20 Cells(i + 3, 5) = g(i) Next i End Sub Notice that the definition of the g array uses ReDim instead of Dim to allocate contiguous memory. Entering a value of 5 in cell E1, the output range will show: 0 0.00366 0.052653 0.184737 0.371163 0.559507 0.714943 0.827008 0.900368 0.945036 0.970747 0.984895
file:///C|/Program Files/VNI/IMSLCS4.0.2_2.0/IMSLCS4.0.2/manual/Calling IMSL C (8 of 10)1/26/2007 11:09:32 AM

Calling IMSL C# from Excel

0.9924 0.99626 0.998195 0.999143 0.9996 0.999815 0.999916 0.999962 0.999983

Working with 2D Arrays

Extending the above examples to 2D arrays is straightforward. This example will pass an array, compute the inverse of the matrix, and return that as a 2D array. The Visual Basic.NET code is: Public Function Inverse(ByRef aIn As Double(,)) As Double(,) Dim lu As LU lu = New LU(aIn) Return lu.Inverse End Function Note again that the input array is passed ByRef. The Excel VBA macro is run through another Command Button and is: Private Sub FindInverse_Click() Dim o As New ExcelTest.TestIMSL Dim a(2, 2) As Double ReDim inv(2, 2) As Double Dim i As Integer Dim j As Integer ' fill the 2D array For i = 0 To 2 For j = 0 To 2 a(i, j) = Cells(j, i + 6) Next j Next i ' call IMSL routine inv = o.Inverse(a)

file:///C|/Program Files/VNI/IMSLCS4.0.2_2.0/IMSLCS4.0.2/manual/Calling IMSL C (9 of 10)1/26/2007 11:09:32 AM

Calling IMSL C# from Excel

For i = 0 To 2 For j = 0 To 2 Cells(j + 4, i + 6) = inv(i, j) Next j Next i End Sub Here were reading a 3x3 matrix from F1:H3 and writing its inverse to F5:H7. These input and output regions will show: 1 1 1 7 -1 -1 3 3 4 -3 0 1 3 4 3 -3 1 0

Summary

These examples are fairly trivial but expose all of the steps required to build an assembly and register it with COM so that Excel macros can access its public methods. The summary steps are: 1. 2. 3. 4. 5. Create a Visual Basic Class Library project Include unique GUIDs in the ComClass Attributes Register the assembly with COM through the IDE or with regasm Add the assembly in the Global Assembly Cache with gacutil Include the assembly as a reference in an Excel Module

file:///C|/Program Files/VNI/IMSLCS4.0.2_2.0/IMSLCS4.0.2/manual/Calling IMSL C (10 of 10)1/26/2007 11:09:32 AM

You might also like