2

I've recently joined a project where we are extending the functionality of an external application that our company has bought.

It is an Office Add-In with an admin console that allows you to create Windows Forms style forms with code-behind files, and gives you a single place to store common code. This can be quite challenging when implementing code that would be simple in a 'normal' Windows Forms application.

How do I implement global error handling so we can log all errors?

We aren't using Visual Studio and have no access to csproj files so we can't use IL weaving / AOP. We don't have access to the starting point of the application so we can't subscribe to thread or application unhandled exception events. I suspect this is already being subscribed to in the external code, as unhandled exceptions are exposed in a custom error message box by default.

I'm currently looking to see if we can extend the external dlls; however I don't see where I'm going to call our extended code.

The alternative is to go through every method and add a try/catch block in just to log errors, which makes my skin crawl! This project has been running for a year without dealing with error handling yet so it will also take a long time to implement this approach too.

Pseudo-Example class:

namespace Scriptlets
{
  using System;
  using System.Windows.Forms;

  public partial class NewClient : FormScriptType
  {
    protected override void PageChanged(object sender, PageChangedEventArgs e)
    {
        ChangeAddressLabels();
    }

    ...
  }
}
8
  • can you show an example piece of code? Can you not define a base form class that contains logging and then inherit it from every new form and thus use the logger? If possible you can create the Forms as UserControls and add them to one form only, that has logging. Apr 24, 2014 at 17:00
  • Unfortunately we can't inherit from a base form class, as the class declaration line is not editable in our editor either. I'm curious to how you would implement logging on the base class though. How would you add error handling to each method from a base class? I think our hands may be tied on this one.
    – Catherine
    Apr 25, 2014 at 10:24
  • About the logging - i thought of one method that takes a Func or Action and wraps the execution in a try/catch block: something like: public T ExecuteAndLogIfFail<T>(Func<T> action) { try { return action(); } catch (Exception exception) { Console.WriteLine("Implement your logger here: {0}", exception.Message); } return default(T); } You can now wrap your new code into this and you will always have exception handling. Can't you trie something like PostSharp at least for the new code? Apr 25, 2014 at 11:59
  • Usage of the code from the previous example: ExecuteAndLogIfFail<Boolean>(() => { Console.WriteLine("Do something without output"); return true; }); var zero = 0; List<Int32> items = new List<Int32> { 10, 20, 30, 40, 50 }; var result = ExecuteAndLogIfFail<Int32>(() => { return items.Sum(i => i / 10); }); Console.WriteLine(result); ExecuteAndLogIfFail<Boolean>(() => { result /= zero; return true; }); Apr 25, 2014 at 12:02
  • Thanks for the suggestions. We tried Postsharp, but since we don't have access to the csproj files we can't get it to do it's thing unfortunately as that would have been brilliant. Your example code would be a good way of only having to write the catch block once. We'll still have to go back and modify all our methods, but it's a bit nicer than having try catch blocks everywhere, thanks!
    – Catherine
    Apr 25, 2014 at 13:41

1 Answer 1

1

About the logging - if you want to spare some typing of try/catch blocks, you could create one method that takes a Func or Action and wraps the execution in a try/catch block:

public T ExecuteAndLogIfFail<T>(Func<T> action)
{
    try
    {           
        return action();
    }
    catch (Exception exception)
    {
        Console.WriteLine("Implement your logger here: {0}", exception.Message);
    }
    return default(T);
}

You can now wrap your new code into this and you will always have exception handling.

Usage of the wrapper:

ExecuteAndLogIfFail<Boolean>(() =>
{
    Console.WriteLine("Do something without output");
    return true;
});

var zero = 0;
List<Int32> items = new List<Int32> { 10, 20, 30, 40, 50 };
var result = ExecuteAndLogIfFail<Int32>(() =>
{
    return items.Sum(i => i / 10);
});
Console.WriteLine(result);

ExecuteAndLogIfFail<Boolean>(() =>
{
    result /= zero; // catch and log!
    return true;
});

If you haven't tried this already you could experiment with the AppDomain.CurentDomain object and hook the unhandled exception event:

AppDomain.CurrentDomain.UnhandledException += (s, e) =>
{
    Console.WriteLine("something happened ...");
};

If you figured out, that the new window launch is wrapped in a try/catch block, you could try to modify (if this is ok) the assembly using Cecil and add a throw statement into the catch block. I once used the Reflexil Reflector plugin to inject code into an assembly and save the modified executable - no app start points needed. The plugin site states, that it works with Telerik's JustDecompile too.

The conclusion is - maintaining closed legacy systems is PITA. :-)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.