Skip to content

Instantly share code, notes, and snippets.

@govert
Created July 2, 2015 21:39
Show Gist options
  • Save govert/fd4cf051deada526039f to your computer and use it in GitHub Desktop.
Save govert/fd4cf051deada526039f to your computer and use it in GitHub Desktop.
A sample about recovering caller information for async calls, and the effect of different async keys
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using ExcelDna.Integration;
using ExcelDna.Logging;
namespace AsyncCaller
{
public class AddIn : IExcelAddIn
{
public void AutoOpen()
{
ExcelIntegration.RegisterUnhandledExceptionHandler(ErrorHandler);
}
public void AutoClose()
{
}
object ErrorHandler(object exception)
{
object caller;
Exception innerException;
ExcelAsyncException asyncEx = exception as ExcelAsyncException;
if (asyncEx != null)
{
// Caller is already wrapped in async exception
caller = asyncEx.CallerReference;
innerException = asyncEx.InnerException;
}
else
{
// Get caller for regular function
caller = XlCall.Excel(XlCall.xlfCaller);
innerException = (Exception)exception;
}
LogDisplay.WriteLine(caller.ToString() + " : " + innerException.Message);
return ExcelError.ExcelErrorValue;
}
}
public static class Functions
{
public static object SlowAdd(double val1, double val2)
{
Thread.Sleep(3000);
if (val1 == 0)
throw new ArgumentOutOfRangeException("val1");
return val1 + val2;
}
public static object SlowAddAsync(double val1, double val2)
{
string funcName = "SlowAddAsync";
object asyncKey = new object[] { val1, val2 };
return ExcelAsyncUtil.Run(funcName, asyncKey, () => SlowAdd(val1, val2));
}
public static object SlowAddAsyncEx(double val1, double val2)
{
// The asyncKey (together with the funcName string) will determine the RTD topic key,
// and thus which async calls are considered 'the same'
// If the caller is part of the key, then every async call (even those with exactly the same parameters)
// will be calculated separately, and can thus report separate errors.
// If the caller is not part of the asyncKey, the inner function will only be called once for
// a given set of parameter values, and hence only one of the calling references will be available
// in the error handler.
object caller = XlCall.Excel(XlCall.xlfCaller);
string funcName = "SlowAddAsyncEx";
object asyncKey = new object[] { val1, val2, caller };
// object asyncKey = new object[] { val1, val2 };
return ExcelAsyncUtil.Run(funcName, asyncKey, () =>
{
try
{
return SlowAdd(val1, val2);
}
catch (Exception ex)
{
throw new ExcelAsyncException("Wrapped error", ex, caller);
}
});
}
}
public class ExcelAsyncException : Exception
{
public object CallerReference { get; private set; }
public ExcelAsyncException(string message, Exception innerException, object callerReference)
: base(message, innerException)
{
CallerReference = callerReference;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment