Category Archives: Excel-DNA

A Real Time Data (RTD) Server using Excel-DNA in Excel

Creating an RTD server/function in Excel-DNA is very simple.

An RTD function in Excel is basically a function that continually updates Excel, for example a cell might show the current price of a stock without the Excel user having to refresh/recalc.

I’m going to take the code a sample written by Govert van Drimmelen’s on the Excel-DNA google group and work through it to show what does what. His code demonstrates code for a time service/server. i.e. it will supply the current time to Excel and update it every 500ms.

  1. Create a class library named RtdFunctions (or whatever you like)
  2. Using NuGet add the Excel-DNA package to the project
  3. Add the packages\Excel-DNA.0.30.3\tools\ExcelDna.xll file to your project and rename to match the project but suffixed -AddIn.xll, i.e. mine is RtdFunctions-AddIn.xll set it’s “Copy to Output Directory” property to “Copy if newer”. You could ofcourse handle the copy within the post build step if you prefer.
  4. Rename the Class1.cs file to TimerServer.cs

We’ve now created a bare bones of this project, it’s time to now implement the TimeServer. Whilst we could implement the IRtdServer interface ourselves. The Excel-Dna assembly includes a thread-safe implementation already named ExcelRtdServer, so let’s use that so we can concentrate on the actual RTD functionality.

So…

  1. Derive the new TimeServer class from ExcelRtdServer, you’ll need to add the using clause for ExcelDna.Integration.Rtd
  2. If we want the potential to support multiple topics we’ll need a list of topics, ExcelRtdServer wraps topic id’s in a Topic object so add
    private readonly List<Topic> topics = new List<Topic>();
    

    to our TimerServer

  3. We are going to need to override three methods from the ExcelRtdServer, ServerTerminate, ConnectData and DisconnectData, so go ahead and override those and then we’ll implement the functionality in each
  4. As the purpose of this TimerServer is to supply the time using a push style notification, we’re going to add a Timer to the class, I’m using the System.Threading.Timer so add the following
    private Timer timer;
    
    public TimeServer()
    {
       timer = new Timer(Callback);
    }
    
    private void Start()
    {
       timer.Change(500, 500);
    }
    
    private void Stop()
    {
       timer.Change(-1, -1);
    }
    
    private void Callback(object o)
    {			
       Stop();
       foreach (Topic topic in topics)
       {
          topic.UpdateValue(GetTime());
       }
       Start();
    }
    
    private static string GetTime()
    {
       return DateTime.Now.ToString("HH:mm:ss.fff");
    }
    

    As you can see the Callback method, which is triggered on a timer event, will loop through the topics calling UpdateValue with results from GetTime, in other words it will tell each topic to update to the latest time.

  5. Amend the ServerTerminate method to dispose of the timer, this will be called when Excel terminates any use of the TimrServer, so we can safely clean up here, so ServerTerminate should look like this
    protected override void ServerTerminate()
    {
       timer.Dispose();
       timer = null;
    }
    
  6. Next we want to handle the ConnectData implementation. Each time the server is connected to we will cache the topic in our topics like and ensure the timer is started before returning the current time
    protected override object ConnectData(Topic topic, IList<string> topicInfo, ref bool newValues)
    {
       topics.Add(topic);
       Start();
       return GetTime();
    }
    

    The ConnectData method is called when a spreadsheet is opened with the RTD functions or, ofcourse, when you add a function call to the server method

  7. Finally we want to implement the DisconnectData method to remove the cached topic and possible stop the service, so add the following
    protected override void DisconnectData(Topic topic)
    {
       topics.Remove(topic);
       if (topics.Count == 0)
          Stop();
    }
    

So the full code is listed here

public class TimeServer : ExcelRtdServer
{
   private readonly List<Topic> topics = new List<Topic>();
   private Timer timer;

   public TimeServer()
   {
      timer = new Timer(Callback);
   }

   private void Start()
   {
      timer.Change(500, 500);
   }

   private void Stop()
   {
      timer.Change(-1, -1);
   }

   private void Callback(object o)
   {			
      Stop();
      foreach (Topic topic in topics)
         topic.UpdateValue(GetTime());
      Start();
   }

   private static string GetTime()
   {
      return DateTime.Now.ToString("HH:mm:ss.fff");
   }

   protected override void ServerTerminate()
   {
      timer.Dispose();
      timer = null;
   }

   protected override object ConnectData(Topic topic, IList<string> topicInfo, ref bool newValues)
   {
      topics.Add(topic);
      Start();
      return GetTime();
   }

   protected override void DisconnectData(Topic topic)
   {
      topics.Remove(topic);
      if (topics.Count == 0)
         Stop();
   }
}

This is all well and good but to use this we need to use the =RTD syntax in Excel and to do this we’ll need to add a Guid to the class and mark the project, via the project properties (build tab), as Register for COM interop and then register the component.

It’s far simpler and less messy to now create a new class, let’s call it TimeFunctions as follows

public static class Timefunctions
{
   public static object GetCurrentTime()
   {
      return XlCall.RTD("RtdFunctions.TimeServer", null, "NOW");
   }
}

This is now exposed to Excel and will use XlCall.RTD to call into our RTD server so bypassing any issues with COM etc.

Creating an XLL add-in using Excel-DNA

On a previous post I walked through creating an XLL add-in using C# and ManagedXLL for Excel. This is a commercial library which offers many options for interacting and integrating with Excel. But there’s a nice, open source, alternative called Excel-DNA, if you prefer to go this way.

So let’s take a look at creating a Excel add-in using Excel-Dna.

  1. Let’s start off by creating a class library in C# (I’m going to call mine ExcelDNATest, I know it’s not very original)
  2. Using NuGet, add the Excel-DNA package to your solution
  3. Copy and rename packages\Excel-DNA.0.30.3\tools\ExcelDna.xll to projectname-AddIn.xll, so in my case that’s ExcelDNATest-AddIn.xll
  4. We need to copy this file to the output folder so either create a post build step to copy this or I simply added it to the project and marked it’s “Copy to Output Directory” to “Copy if newer”
  5. You’ll notice that the NuGet package added a file named projectname-AddIn.dna to the project. This is the config file for the XLL add-in we’re creating. If you open it you should see an ExternalLibrary element which has the name of the DLL we’re going to build.

At this point we’ve created the barebones for our DLL which the information which allows the XLL file to call into our DLL which we shall use to contain the Excel functions.

You should have found the NuGet package added a file ExcelDna.Integration.dll to our project. This contains the attributes and other code which allows us to expose Excel functions from our code and for us to talk to Excel.

So at this point, to recap, we’ve created a DLL, we’ve added Excel-DNA to the project. We’ve copied/renamed the XLL file to match out project and ensure the .dna configuration file points to our DLL name. Now it’s time to have a little more fun…

Let’s add a function to our class and usable from Excel

  1. Rename your Class1.cs file to MyFunctions.cs (or whatever you prefer)
  2. Open the class file make the class static
  3. Add a static method as follows
    [ExcelFunction(Name="MyFunctions.Hello", Description="Hello X", 
    Category="My Excel Functions"]
    public static string Hello([ExcelArgument("Enter your name")] string name)
    {
       return "Hello " + name;
    } 
    

    We’ve now got a function which will appear in the Excel function wizard in the category “My Excel Functions”. The function will be named MyFunction.Hello and has the description “Hello X”.

  4. Simply type

    =MyFunctions.Hello(“Mark”)

    in your spreadsheet cell to see the function in action.

Alternatively create the code in the DNA file

I’m not sure how useful this is, but you can also create UDF (user-defined functions) inside the .DNA file.

For example

<DnaLibrary Name="ExcelDNATest Add-In" RuntimeVersion="v4.0" Language="CS">
  <![CDATA[
  using ExcelDna.Integration;
  
  public class SomeMoreFunctions
  {
     [ExcelFunction(Description="Test Function")]
     public static string Test()
     {
        return "This is a test function";
     }
  }
  ]]>
</DnaLibrary>

And the rest is as per the compiled source code, i.e. it’s available in the function wizard etc.