What I am trying to do is to create User Defined Functions (UDFs) for Excel using VSTO’s C# “Excel 2007 Add-in”-project type (since I just want to generate some general UDFs
VSTO has no support for creating Excel UDFs. Automation Add-Ins can be created in .Net, and seem to be the Microsoft approved way of doing it.
You should have a look at ExcelDna - http://www.codeplex.com/exceldna. ExcelDna allows managed assemblies to expose user-defined functions (UDFs) and macros to Excel through the native .xll interface. The project is open-source and freely allows commercial use. And you'll find that the performance of your .Net-based UDF is similar to native .xll add-ins for Excel. Excel 2007 features like the large sheet, long Unicode strings and multi-threaded recalculation are supported.
With ExcelDna your function as posted above will be exposed to Excel with no VSTO - you can put to code into an xml-based .dna file or compile it to a .dll.
The .dna file exposing your UDF would look like this:
<DnaLibrary Language="C#">
using System;
using ExcelDna.Integration;
public class MyFunctions
{
[ExcelFunction(Description="Calculate Stuff", Category="Cool Functions")]
public static double HeronicCal(int a, int b, int c)
{
//first compute S = (a+b+c)/2
double S = (a + b + c) / 2;
double area = Math.Sqrt(S * (S - a) * (S - b) * (S - c));
return area;
}
}
</DnaLibrary>
Update: These days, the easiest way to get started with Excel-DNA is to make a new Class Library project in Visual Studio, then add the 'ExcelDna.AddIn' package from NuGet. That makes a starter add-in - just paste your code and press F5 to run.
As far as I know, you cannot directly create UDFs in VSTO.
See Paul Stubbs' article How to create Excel UDFs in VSTO managed code where he uses a VBA add-in to expose VBA UDFs, which in turn call his Managed UDFs written in VSTO.
You can use managed code to create UDFs, however, when not using VSTO. See Eric Carter's article Writing user defined functions for Excel in .NET on how to do this.
As for VSTO's execution speed, I think you'll find it fine for virtually all tasks. Looping through cells, however, which is already Excel's weak-point, might be painfully slow, depending on what you are doing. Try to execute things in batch, as much as possible. For example, instead of looping through the cells one by one, return a two dimensional array of values from an area, process the array, and then pass it back to the range.
To demonstrate, the following will return a two dimensional array of values from an area, process the values, and then pass the resulting array back to the original area in one shot:
Excel.Range rng = myWorksheet.get_Range("A1:D4", Type.Missing);
//Get a 2D Array of values from the range in one shot:
object[,] myArray = (object[,])rng.get_Value(Type.Missing);
// Process 'myArray' however you want here.
// Note that the Array returned from Excel is base 1, not base 0.
// To be safe, use GetLowerBound() and GetUpperBound:
for (int row = myArray.GetLowerBound(0); row <= myArray.GetUpperBound(0); row++)
{
for (int column = myArray.GetLowerBound(1); column <= myArray.GetUpperBound(1); column++)
{
if (myArray[row, column] is double)
{
myArray[row, column] = (double)myArray[row, column] * 2;
}
}
}
// Pass back the results in one shot:
rng.set_Value(Type.Missing, myArray);
Hope this helps!
Mike
What I've found that works well is to keep the UDF as a VB module to avoid the issues with COM objects.
I've got tons of C# code running and when I'm ready to build the release I do the following:
1. Add a module:
Developer [tab in Excel] | Visual Basic -> project window, right-click, insert module
- merely copy/paste the VB code here
2. Include the appropriate reference library (Tools in same VB window)
3. Save the Excel file as an .xlsm (ie, Macro-Enabled)
You can then delete the .xlsx file.
What I do is zip the entire directory (eg, "Release") and send it to our users.
Looks like Eric Carter has a winner here:
http://blogs.msdn.com/b/eric_carter/archive/2004/12/01/273127.aspx
It's pure .NET - no dependency on third party libraries.
Giving it a burl now...