Wednesday, September 24, 2014

This blog has moved to: http://kent-boogaart.com/

Saturday, February 16, 2013

Replacing Synology’s Data Replicator with Powershell and Robocopy

I recently upgraded (for some definition of “upgraded”) from Windows 7 to Windows 8. I had been using Data Replicator 3 to keep my machine backed up to my Synology server. However, I was keen to be rid of it because it is awfully slow and cumbersome. So I set out to do exactly that by leveraging Powershell and Robocopy, both of which are installed by default on Windows 8.

It’s important to note that the solution I provide here isn’t a like-for-like replacement of Data Replicator. It doesn’t provide any help restoring data – you would have to either do that manually or write another script to reverse the robocopy from server to client. But on the plus side, there’s nothing Synology-specific here either, so you can use this regardless of your server technology.

So here’s the powershell script I concocted (obviously you’ll need to tweak the $Server and $PathsToBackup properties for your setup):

# Incrementally backs up files on a client to a server

# the name of your server
$Server = "SERVER_NAME"

# the local paths to back up to your server. All paths must be fully-qualified
$PathsToBackup = @("C:\Users", "C:\SOME_OTHER_PATH")

$ScriptPath = $MyInvocation.MyCommand.Path
$ScriptDir = Split-Path $ScriptPath
$Client = [System.Net.Dns]::GetHostName()
$LogPath = "$ScriptDir\BackupLog.txt"

if (Test-Path -path $LogPath)
{
    Remove-Item $LogPath
}

New-Item $LogPath -type file

foreach ($PathToBackup in $PathsToBackup)
{
    $PathQualifier = Split-Path -Qualifier $PathToBackup
    $PathQualifier = $PathQualifier.Replace(":", "")
    $PathToBackupWithoutQualifier = Split-Path -NoQualifier $PathToBackup
    $Command = "robocopy $PathToBackup \\$Server\Backups\$Client\$PathQualifier\$PathToBackupWithoutQualifier /FFT /MIR /NP /W:0.5 /R:0 >> $LogPath"
    Invoke-Expression $Command
}

I placed this script in my home directory with a name of Backup.ps1.

Next, I set up a scheduled task to run this script every day. Open the Task Scheduler and select the Task Scheduler Library. Right-click in the list of tasks and select Create New Task… Use the following screenshots to guide you configuring the task. I’ve also listed the changes textually in case the images stop working (or are blocked for you):

 

General Tab

Name: Backup (or whatever you want to call it)
Security options: Run whether user is logged on or not and run with highest privileges
Hidden: true
Configure for: I set mine to Windows 8, but not sure whether that’s necessary

image

Triggers Tab

Set whatever trigger you like, but I set mine to run daily.

image

Actions Tab

Action: Start a program
Program/script: powershell
Arguments: enter the full path to the Backup.ps1 script

image

Click OK to create the task. You will be prompted for a password, so enter that. Now you’ve got a scheduled task that will incrementally back up whatever local files you like to your server. Finally, note that the script produces a log (truncated on each run) called BackupLog.txt in the same directory as Backup.ps1, so this may come in handy if something isn’t backing up as expected.

Monday, December 24, 2012

WPF Splash Screens

It may seem odd to blog about something as mundane as WPF splash screens. After all, it's a solved problem, right? Wrong. I think there are various problems that come about by using standard splash screen helpers, and I want to address those here.

First though, we need to get back to basics by discussing what the purpose of a splash screen is. Contrary to many a splash you may have been subjected to, the purpose of a splash screen is not to annoy users or to wow business folks. To quote the repository of all knowledge:

Splash screens are typically used by particularly large applications to notify the user that the program is in the process of loading. They provide feedback that a lengthy process is underway.

So a splash screen's primary goal is to reassure the user that their choice of software is, indeed, loading. That implies that the splash screen must appear quickly, otherwise the user won't feel reassured at all and may end up launching the software a second time. A splash screen might also provide some additional pieces of information about the software (such as the version number).

With those points in mind, it's time to move onto specifics.

SplashScreen

WPF provides a SplashScreen class. It is simple by design and addresses the main goal of splash screens: immediate feedback. By virtue of forgoing the WPF stack and instead relying on Windows Imaging Component (WIC) to display images, it provides the quickest path to getting a splash on the screen short of writing your own native bootstrapper.

Of course, it has limitations, otherwise this blog post wouldn't have much meat on it. We'll get to those shortly...

Disabling the Splash

Within mere minutes of adding a splash to your application, you'll be sick of seeing it, especially if your artistic skills are on par with mine. And when the debugger breaks with the splash screen superimposed over it, you'll be screaming abuse at Thaygorn (the god of splash screens, as ordained by me, just now).

So the first thing you should do is provide a command-line switch to disable the splash screen. I like -thaybegorn, but heathens may prefer -nosplash. For WPF applications, this means forgoing the generated entry point and providing your own. For any non-trivial application, you'll likely want your own entry point anyway, so that you can bootstrap logging and hook into unhandled exception events as early as possible.

In your entry code, you can have something like this:

internal static void Main(string[] args)
{
    var showSplash = !args.Any(x => string.Equals("-nosplash", x, StringComparison.OrdinalIgnoreCase));
    
    if (showSplashScreen)
    {
        ShowSplashScreen();
    }
}

private static void ShowSplashScreen()
{
    splashScreen = new SplashScreen("Splash.png");
    splashScreen.Show(true, true);
}

Now developers can simply configure their IDE to pass in this command line argument when starting the application. Most of the time, they can live in blissful ignorance of there even being a splash screen.

Regaining some Dynamism

One of the biggest things you give up by using SplashScreen is dynamic content. No animations (not even animated GIFs), no progress information, no nothing. If you're like me, you'd at least like the version number of the product in the splash screen. But that means you have to modify your splash image every time your version changes. Rather than doing this manually (ugh!), you should look to your build process to do it for you. I assume you already have a shared assembly info file with the version of your product in it? If not, go do that right now.

Once your build has access to a well-defined version number, you can use a custom task to modify a base splash image on the fly. Here's one I wrote (MSBuild):

<UsingTask TaskName="AddTextToImage" TaskFactory="CodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll">
    <ParameterGroup>
        <InputPath ParameterType="System.String" Required="true" />
        <TopRightPoint ParameterType="System.String" Required="true" />
        <OutputPath ParameterType="System.String" Required="true" />
        <IsBitmap ParameterType="System.Boolean" Required="false" />
        <Text ParameterType="System.String" Required="true" />
    </ParameterGroup>
    <Task>
        <Reference Include="WindowsBase"/>
        <Reference Include="PresentationCore"/>
        <Reference Include="PresentationFramework"/>
        <Reference Include="System.Xaml"/>
        
        <Using Namespace="System" />
        <Using Namespace="System.Globalization" />
        <Using Namespace="System.IO" />
        <Using Namespace="System.Windows" />
        <Using Namespace="System.Windows.Media" />
        <Using Namespace="System.Windows.Media.Imaging" />
        
        <Code Type="Fragment" Language="cs"><![CDATA[
            var originalImageSource = BitmapFrame.Create(new Uri(InputPath));
            var visual = new DrawingVisual();

            using (var drawingContext = visual.RenderOpen())
            {
                drawingContext.DrawImage(originalImageSource, new Rect(0, 0, originalImageSource.PixelWidth, originalImageSource.PixelHeight));

                var typeFace = new Typeface(new FontFamily("Century Gothic"), FontStyles.Normal, FontWeights.Bold, FontStretches.Normal);
                var formattedText = new FormattedText(Text, CultureInfo.InvariantCulture, FlowDirection.LeftToRight, typeFace, 8, Brushes.White);
                var topRightPoint = Point.Parse(TopRightPoint);
                var point = new Point(topRightPoint.X - formattedText.Width, topRightPoint.Y);

                drawingContext.DrawText(formattedText, point);
            }

            var renderTargetBitmap = new RenderTargetBitmap(originalImageSource.PixelWidth, originalImageSource.PixelHeight, originalImageSource.DpiX, originalImageSource.DpiY, PixelFormats.Pbgra32);

            renderTargetBitmap.Render(visual);

            var bitmapFrame = BitmapFrame.Create(renderTargetBitmap);
            BitmapEncoder encoder = null;
            
            if (IsBitmap)
            {
                encoder = new BmpBitmapEncoder();
            }
            else
            {
                encoder = new PngBitmapEncoder();
            }
            
            encoder.Frames.Add(bitmapFrame);

            using (var stream = File.OpenWrite(OutputPath))
            {
                encoder.Save(stream);
            }
            ]]>
        </Code>
    </Task>
</UsingTask>

In my case, I need to ensure the text within the splash image is right-aligned, so I have a TopRightPoint property. I use it like this:

<AddTextToImage InputPath="$(ResourcesPath)/SplashTemplate.png" OutputPath="$(ResourcesPath)/Splash.png" TopRightPoint="350,115" Text="$(Version)"/>

This takes the SplashTemplate.png image, adds the contents of the Version property at the location specified, then saves it to Splash.png. Easy. This step, of course, happens before I build the application, so that the modified Splash.png is included in the executable as a resource. I also check in a copy of the splash template as Splash.png so that developers don't run into missing file issues if they compile without the splash generation step having been executed. I then set up my source control to ignore any changes to that file.

I use the same technique to add version numbers to my setup images. You could take it a lot further (add other text, flexible fonts, superimpose other images), of course, but I haven't had any need.

Postponing Closure

The SplashScreen class supports an auto-close function, which will start closing the splash screen as soon as the application is loaded (ie. the dispatcher is processing messages with a priority of DispatcherPriority.Loaded). It also allows you to close the splash screen yourself, via its Close method.

In my experience, a splash screen closing as soon as the application is loaded can be a jarring experience. The application being loaded does not imply that all windows have been initialized and displayed. You may have some background process initializing services or connections or whatever, with UI initializing asynchronously in respect to that.

I much prefer to have explicit control over when the splash screen closes. I like to have it wait a couple of seconds after the application has loaded before fading out the splash screen. And sometimes I need even more control than that, such as waiting for some specific UI initialization event.

Simply starting a timer in our entry code does not suffice, because it will start before the application has loaded. Indeed, if the application takes more than a couple of seconds to load, the splash may close before the UI even appears! And since the entry code is the main UI thread, we can't just block until the application initializes, because then it never will!

One approach might be to spin off another thread or task to periodically check whether the application is loaded. However, there's a cleaner way via WPF's DispatcherFrame mechanism. What we can do is pump the dispatcher until the application is loaded:

private static void PumpDispatcherUntilPriority(DispatcherPriority dispatcherPriority)
{
    var dispatcherFrame = new DispatcherFrame();
    Dispatcher.CurrentDispatcher.BeginInvoke((ThreadStart)(() => dispatcherFrame.Continue = false), dispatcherPriority);
    Dispatcher.PushFrame(dispatcherFrame);
}

We can use that method like this:

if (showSplash)
{
    // pump until loaded
    PumpDispatcherUntilPriority(DispatcherPriority.Loaded);

    // start a timer, after which the splash can be closed
    var splashTimer = new DispatcherTimer
    {
        Interval = TimeSpan.FromSeconds(2)
    };
    splashTimer.Tick += (s, e) =>
    {
        splashTimer.Stop();
        CloseSplashScreen();
    };
    splashTimer.Start();
}

Great! Now we have a splash that closes two seconds after the application loads. You could also pump the dispatcher until some other criteria is met. Notice, however, that you need to be careful. If the user has sufficient time to open a dialog window within the application, this can preclude the closure of the splash screen (until the dialog is dismissed). That's because opening the dialog will result in a new dispatcher frame that pumps until the dialog is closed. That's precisely why I used a timer above instead of just pumping for a fixed period of time.

Fixing the Activation Issue

Now that our splash closes some time after the application appears, another problem has surfaced. When our splash screen fades, it temporarily becomes activated. That's weird. And rather ugly.

After perusing the source for SplashScreen, I found the reason for this jarring activation. This code is in the fade logic:

// by default close gets called as soon as the first application window is created
// since it will have become the active window we need to steal back the active window
// status so that the fade out animation is visible. 
IntPtr prevHwnd = UnsafeNativeMethods.SetActiveWindow(new HandleRef(null, _hwnd));

The comment seems to suggest that the window is being activated only to ensure it is visible. Nasty.

So where does this discovery leave us? We must either put up with the activation of the fading splash screen, or forgo the fade logic in SplashScreen and write our own. I did the latter, and it looks like this:

private static void CloseSplashScreen(TimeSpan fadeDuration)
{
    var fadeDurationTicks = fadeDuration.Ticks;
    var remainingTicks = fadeDurationTicks;
    var lastCheckTicks = DateTime.UtcNow.Ticks;
    var dispatcherTimer = new DispatcherTimer(DispatcherPriority.Normal)
    {
        // 60fps
        Interval = TimeSpan.FromMilliseconds(1000 / 60d)
    };
    var opacity = 1d;

    dispatcherTimer.Tick += (s, e) =>
        {
            var tickChange = DateTime.UtcNow.Ticks - lastCheckTicks;
            lastCheckTicks = DateTime.UtcNow.Ticks;
            remainingTicks -= tickChange;
            remainingTicks = Math.Max(0, remainingTicks);
            opacity = (double)remainingTicks / fadeDurationTicks;

            if (remainingTicks == 0)
            {
                // finished fading
                splashScreen.Close(TimeSpan.Zero);
                dispatcherTimer.Stop();
                splashForegroundTimer.Stop();
                splashScreen = null;
                splashForegroundTimer = null;
            }
            else
            {
                // still fading
                blendFunction.SourceConstantAlpha = (byte)(255 * opacity);
                SafeNativeMethods.UpdateLayeredWindow(splashScreen.GetHandle(), IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, 0, ref blendFunction, SafeNativeMethods.ULW_ALPHA);
            }
        };
    dispatcherTimer.Start();
}

As you can see, I set up a timer that continuously fades the splash screen until it is no longer visible, at which point I close it. At no point do I activate the splash screen.

Notice the call to splashScreen.GetHandle()? That's where things have gotten a bit ugly. SplashScreen does not expose its window handle to us, so I wrote an extension method to obtain it via reflection:

internal static class SplashScreenExtensions
{
    public static IntPtr GetHandle(this SplashScreen @this)
    {
        return (IntPtr)typeof(SplashScreen).GetField("_hwnd", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(@this);
    }
}

Not ideal, but it works. We can now fade our splash screen ourselves without it being activated.

Taming the Z-order

There's nothing more obnoxious that a splash screen that insists on covering other applications. OK, apart from Ken Ham that is. So when we show our splash screen, we should pass false for the topmost parameter (or don't specify the parameter at all, since false is the default value):

splashScreen.Show(autoClose: false, topMost: false);

However, doing so presents another issue. Other windows within our application can obstruct the splash screen. As we discovered in the last section, this is why WPF’s SplashScreen calls SetActiveWindow. But SetActiveWindow‘s primary purpose is to activate a window – bringing the window to the top of the stack is just a side-effect of that. We would like to bring our splash to the top of the window stack without activating it. Moreover, we need to ensure it remains at the top of the stack regardless of how many application windows happens to come (and maybe even go) during the time the splash is on-screen or fading away.

The first issue can be addressed by using SetWindowPos instead of SetActiveWindow. It allows us to pull a window in our application to the top of the stack without activating it.

The second issue – ensuring our splash stays on top even as application windows come and go – is trickier. I considered many approaches, including setting the splash window to be a child of the application window (but what if there are multiple application windows?), detecting the activation of new windows so I can call SetActiveWindow again on the splash, and hooking into message queues.

Ultimately I settled on the simplest possible thing I could get to work:

splashForegroundTimer = new DispatcherTimer(DispatcherPriority.Normal);
splashForegroundTimer.Interval = TimeSpan.FromMilliseconds(10);
splashForegroundTimer.Tick += delegate
{
    SafeNativeMethods.SetWindowPos(splashScreen.GetHandle(), SafeNativeMethods.HWND_TOP, 0, 0, 0, 0, SafeNativeMethods.SetWindowPosFlags.SWP_NOMOVE | SafeNativeMethods.SetWindowPosFlags.SWP_NOSIZE | SafeNativeMethods.SetWindowPosFlags.SWP_NOACTIVATE);
};
splashForegroundTimer.Start();

I have a timer that brings the splash screen to the top of the z-order every 10ms (I didn’t experiment too much with this interval, so it may be possible to lengthen it). Notice that I use SWP_NOACTIVATE to ensure the splash isn't activated. Notice also that this isn't a perfect solution. There is still a 10ms (give or take) window wherein the splash may be underneath application windows. In practice, however, I have found this to work satisfactorily. If anyone has any other ideas on how to achieve this more cleanly and simply then I’d love to hear them.

Conclusion

What we end up with when applying all the above changes is something rather more complicated than one might expect. Indeed, it makes me wonder whether I should forgo WPF’s SplashScreen class altogether and create my own. However, I have avoided doing so for a couple of reasons:

  1. SplashScreen has quite a bit of complexity in it.
  2. SplashScreen is in WindowsBase.dll, which is used by any WPF application, and is NGEN'd. Consequently, it's more likely to load faster than my own assembly.

I did, however, create my own wrapper around WPF's SplashScreen class. To confuse matters, I called mine SplashScreen too. Its API is very similar to that provided by WPF's SplashScreen.

Attached is a sample project showing all these techniques combining to result in a splash screen I (and my users) can live with.

Splash Screen

Saturday, September 08, 2012

And then there was Then

In his excellent post Processing Sequences of Asynchronous Operations with Tasks, Stephen Toub discusses how a series of asynchronous operations can be run one after the other in a pre-.NET 4.5 world (a world in which I currently reside, both at work and at home). I won't go into the details here - you should just read his post - but suffice to say that an implementation of a set of Then extension methods is desirable as a functional equivalent to the await keyword. This allows us to chain together asynchronous operations with ease and with better performance than that attainable with ContinueWith on its own:

DownloadImageAsync()
    .Then(x => SearchForAliensAsync())
    .Then(x => DistributeResultsAsync());

Stephen provides an implementation of Then and hints at the usefulness of further overloads. In this post, I provide my own implementation of Then that includes all overloads that I think are useful.

Firstly, here are the signatures for my overloads of Then:

public static Task Then(this Task antecedent, Func<Task, Task> getSuccessor)
public static Task Then(this Task antecedent, Action<Task> successor, TaskCreationOptions taskCreationOptions = TaskCreationOptions.None, TaskScheduler scheduler = null)

public static Task<TSuccessor> Then<TSuccessor>(this Task antecedent, Func<Task, Task<TSuccessor>> getSuccessor)
public static Task<TSuccessor> Then<TSuccessor>(this Task antecedent, Func<Task, TSuccessor> successor, TaskCreationOptions taskCreationOptions = TaskCreationOptions.None, TaskScheduler scheduler = null)

public static Task Then<TAntecedent>(this Task<TAntecedent> antecedent, Func<Task<TAntecedent>, Task> getSuccessor)
public static Task Then<TAntecedent>(this Task<TAntecedent> antecedent, Action<Task<TAntecedent>> successor, TaskCreationOptions taskCreationOptions = TaskCreationOptions.None, TaskScheduler scheduler = null)

public static Task<TSuccessor> Then<TAntecedent, TSuccessor>(this Task<TAntecedent> antecedent, Func<Task<TAntecedent>, Task<TSuccessor>> getSuccessor)
public static Task<TSuccessor> Then<TAntecedent, TSuccessor>(this Task<TAntecedent> antecedent, Func<Task<TAntecedent>, TSuccessor> successor, TaskCreationOptions taskCreationOptions = TaskCreationOptions.None, TaskScheduler scheduler = null)
Note that due to the use of optional arguments, there are more overload permutations here than apparent at first glance. Broadly, there are four supported scenarios:
  1. A non-generic antecedent task THEN a non-generic successor task.
  2. A non-generic antecedent task THEN a generic successor task.
  3. A generic antecedent task THEN a non-generic successor task.
  4. A generic antecedent task THEN a generic successor task.

Each scenario comes in two "flavours". The first flavour requires that the caller provide a Func that returns the successor Task. The second flavour allows you to specify the task logic in an Action or Func, which will be automatically wrapped in a Task for you.

Four scenarios and two flavours means eight overloads, many of which include optional arguments. The result is a great deal of flexibility in how you use Then:

InitializeAsync()
    .Then(x => Console.WriteLine("Initialize step done."))                  // an action that is wrapped in a Task for us
    .Then(x => DownloadDataAsync())                                         // a method that returns a Task
    .Then(                                                                  // a func that is wrapped in a Task for us
        x =>
        {
            Console.WriteLine("Download step done: " + x.Result);
            return x.Result;
        })
    .Then(x =>                                                              // a func that returns the Task with which to continue
        {
            if (x.Result.Contains("MAGIC"))
            {
                return ProcessMagicAsync();
            }
            else
            {
                return ProcessNonMagicAsync();
            }
        })
    .Then(x => Console.WriteLine("Processing step done: " + x.Result));     // another action

Right, on to the implementation then. To improve maintainability, I really wanted to ensure I had only a single implementation of the core Then logic, no matter the number of overloads I made available. This presented a problem in that the core implementation would need to be generic, but then the non-generic overloads would not be able to call it (because their Task instances are not generic). To that end, I created a simple method that takes a non-generic Task and wraps it as a Task<bool>:

public static Task<bool> ToBooleanTask(this Task task)
{
    var taskCompletionSource = new TaskCompletionSource<bool>();

    task.ContinueWith(t => taskCompletionSource.TrySetException(t.Exception.GetBaseException()), TaskContinuationOptions.OnlyOnFaulted);
    task.ContinueWith(t => taskCompletionSource.TrySetCanceled(), TaskContinuationOptions.OnlyOnCanceled);
    task.ContinueWith(t => taskCompletionSource.TrySetResult(true), TaskContinuationOptions.OnlyOnRanToCompletion);

    return taskCompletionSource.Task;
}

If and when the non-generic Task succeeds, the wrapper Task<bool> assumes a result of true. If it is cancelled or fails, that cancellation or failure propagates to the wrapper Task<bool> too. So now any Task can be treated as a Task<bool>, thus allowing our non-generic overloads to call into our generic core implementation.

With that in place, I could create the core implementation:

private static Task<TSuccessor> ThenImpl<TAntecedent, TSuccessor>(Task<TAntecedent> antecedent, Func<Task<TAntecedent>, Task<TSuccessor>> getSuccessor)
{
    antecedent.AssertNotNull("antecedent");
    getSuccessor.AssertNotNull("getSuccessor");

    var taskCompletionSource = new TaskCompletionSource<TSuccessor>();

    antecedent.ContinueWith(
        delegate
        {
            if (antecedent.IsFaulted)
            {
                taskCompletionSource.TrySetException(antecedent.Exception.InnerExceptions);
            }
            else if (antecedent.IsCanceled)
            {
                taskCompletionSource.TrySetCanceled();
            }
            else
            {
                try
                {
                    var successorTask = getSuccessor(antecedent);

                    if (successorTask == null)
                    {
                        taskCompletionSource.TrySetCanceled();
                    }
                    else
                    {
                        successorTask.ContinueWith(
                            delegate
                            {
                                if (successorTask.IsFaulted)
                                {
                                    taskCompletionSource.TrySetException(successorTask.Exception.InnerExceptions);
                                }
                                else if (successorTask.IsCanceled)
                                {
                                    taskCompletionSource.TrySetCanceled();
                                }
                                else
                                {
                                    taskCompletionSource.TrySetResult(successorTask.Result);
                                }
                            },
                            TaskContinuationOptions.ExecuteSynchronously);
                    }
                }
                catch (Exception ex)
                {
                    taskCompletionSource.TrySetException(ex);
                }
            }
        },
        TaskContinuationOptions.ExecuteSynchronously);

    return taskCompletionSource.Task;
}

This is very similar to the implementation provided on Stephen's blog, since I used his solution as a starting point.

With these two pieces in place, I could add all the overloads I required:

public static Task Then(this Task antecedent, Func<Task, Task> getSuccessor)
{
    Func<Task<bool>, Task<bool>> getSuccessorAsBoolean = x =>
    {
        var successiveTask = getSuccessor(x);
        return successiveTask == null ? null : successiveTask.ToBooleanTask();
    };
    return ThenImpl<bool, bool>(antecedent.ToBooleanTask(), getSuccessorAsBoolean);
}

public static Task Then(this Task antecedent, Action<Task> successor, TaskCreationOptions taskCreationOptions = TaskCreationOptions.None, TaskScheduler scheduler = null)
{
    successor.AssertNotNull("successor");

    Func<Task<bool>, Task<bool>> getSuccessor = x =>
    {
        var successiveTask = new Task(() => successor(antecedent), taskCreationOptions);
        successiveTask.Start(scheduler ?? TaskScheduler.Default);
        return successiveTask.ToBooleanTask();
    };
    return ThenImpl<bool, bool>(antecedent.ToBooleanTask(), getSuccessor);
}

public static Task<TSuccessor> Then<TSuccessor>(this Task antecedent, Func<Task, Task<TSuccessor>> getSuccessor)
{
    return ThenImpl<bool, TSuccessor>(antecedent.ToBooleanTask(), getSuccessor);
}

public static Task<TSuccessor> Then<TSuccessor>(this Task antecedent, Func<Task, TSuccessor> successor, TaskCreationOptions taskCreationOptions = TaskCreationOptions.None, TaskScheduler scheduler = null)
{
    successor.AssertNotNull("successor");

    Func<Task<bool>, Task<TSuccessor>> getSuccessor = x =>
    {
        var successiveTask = new Task<TSuccessor>(() => successor(antecedent), taskCreationOptions);
        successiveTask.Start(scheduler ?? TaskScheduler.Default);
        return successiveTask;
    };
    return ThenImpl<bool, TSuccessor>(antecedent.ToBooleanTask(), getSuccessor);
}

public static Task Then<TAntecedent>(this Task<TAntecedent> antecedent, Func<Task<TAntecedent>, Task> getSuccessor)
{
    Func<Task<TAntecedent>, Task<bool>> getSuccessorAsBoolean = x =>
    {
        var successiveTask = getSuccessor(x);
        return successiveTask == null ? null : successiveTask.ToBooleanTask();
    };
    return ThenImpl<TAntecedent, bool>(antecedent, getSuccessorAsBoolean);
}

public static Task Then<TAntecedent>(this Task<TAntecedent> antecedent, Action<Task<TAntecedent>> successor, TaskCreationOptions taskCreationOptions = TaskCreationOptions.None, TaskScheduler scheduler = null)
{
    successor.AssertNotNull("successor");

    Func<Task<TAntecedent>, Task<bool>> getSuccessor = x =>
    {
        var successiveTask = new Task(() => successor(antecedent), taskCreationOptions);
        successiveTask.Start(scheduler ?? TaskScheduler.Default);
        return successiveTask.ToBooleanTask();
    };
    return ThenImpl<TAntecedent, bool>(antecedent, getSuccessor);
}

public static Task<TSuccessor> Then<TAntecedent, TSuccessor>(this Task<TAntecedent> antecedent, Func<Task<TAntecedent>, Task<TSuccessor>> getSuccessor)
{
    return ThenImpl<TAntecedent, TSuccessor>(antecedent, getSuccessor);
}

public static Task<TSuccessor> Then<TAntecedent, TSuccessor>(this Task<TAntecedent> antecedent, Func<Task<TAntecedent>, TSuccessor> successor, TaskCreationOptions taskCreationOptions = TaskCreationOptions.None, TaskScheduler scheduler = null)
{
    successor.AssertNotNull("successor");

    Func<Task<TAntecedent>, Task<TSuccessor>> getSuccessor = x =>
    {
        var successiveTask = new Task<TSuccessor>(() => successor(antecedent), taskCreationOptions);
        successiveTask.Start(scheduler ?? TaskScheduler.Default);
        return successiveTask;
    };
    return ThenImpl<TAntecedent, TSuccessor>(antecedent, getSuccessor);
}

Each of these overloads directly calls the core ThenImpl implementation, massaging any parameters as necessary. It's all reasonably straightforward, so I won't elaborate too much here.

In the interests of completeness, I also wrote these unit tests to validate the implementation:

[Fact]
public void to_boolean_task_propagates_failures()
{
    var task = Task.Factory.StartNew(() => { throw new InvalidOperationException("Testing."); });
    var booleanTask = task.ToBooleanTask();

    var ex = Assert.Throws<AggregateException>(() => booleanTask.Wait(TimeSpan.FromSeconds(1)));
    Assert.Equal(1, ex.InnerExceptions.Count);
    Assert.IsType<InvalidOperationException>(ex.InnerExceptions[0]);
    Assert.Equal("Testing.", ex.InnerExceptions[0].Message);
}

[Fact]
public void to_boolean_task_propagates_cancellation()
{
    var cancellationTokenSource = new CancellationTokenSource();
    cancellationTokenSource.Cancel();

    var task = Task.Factory.StartNew(() => cancellationTokenSource.Token.ThrowIfCancellationRequested(), cancellationTokenSource.Token);
    var booleanTask = task.ToBooleanTask();

    var ex = Assert.Throws<AggregateException>(() => booleanTask.Wait(TimeSpan.FromSeconds(1)));
    Assert.Equal(1, ex.InnerExceptions.Count);
    Assert.IsType<TaskCanceledException>(ex.InnerExceptions[0]);
}

[Fact]
public void to_boolean_task_propagates_success()
{
    var task = Task.Factory.StartNew(() => { });
    var booleanTask = task.ToBooleanTask();

    Assert.True(booleanTask.Wait(TimeSpan.FromSeconds(1)));
}

[Fact]
public void then_non_generic_second_task_does_not_start_until_first_is_finished()
{
    var executed = false;

    var task = Task.Factory
        .StartNew(
            () =>
            {
                Thread.Sleep(100);
                executed = true;
            })
        .Then(
            x =>
            {
                Assert.True(executed);
            });

    Assert.True(task.Wait(TimeSpan.FromSeconds(3)));
}

[Fact]
public void then_non_generic_fault_in_first_task_prevents_second_task_from_running()
{
    var executed = false;

    var task = Task.Factory
        .StartNew(
            () =>
            {
                throw new InvalidOperationException("Failure");
            })
        .Then(
            x =>
            {
                executed = true;
            });

    try
    {
        task.Wait(TimeSpan.FromSeconds(3));
        Assert.True(false);
    }
    catch (AggregateException ex)
    {
        Assert.False(executed);
        Assert.Equal(TaskStatus.Faulted, task.Status);
        Assert.Equal(1, ex.InnerExceptions.Count);
    }
}

[Fact]
public void then_non_generic_fault_in_second_task_results_in_faulted_overall_task()
{
    var executed = false;

    var task = Task.Factory
        .StartNew(() => { })
        .Then(
            x =>
            {
                throw new InvalidOperationException("Failure");
            })
        .Then(
            x =>
            {
                executed = true;
            });

    try
    {
        task.Wait(TimeSpan.FromSeconds(3));
        Assert.True(false);
    }
    catch (AggregateException ex)
    {
        Assert.False(executed);
        Assert.Equal(TaskStatus.Faulted, task.Status);
        Assert.Equal(1, ex.InnerExceptions.Count);
    }
}

[Fact]
public void then_non_generic_cancellation_in_first_task_prevents_second_task_from_running()
{
    var executed = false;

    using (var cancellationTokenSource = new CancellationTokenSource())
    {
        // cancel up-front
        cancellationTokenSource.Cancel();

        var cancellationToken = cancellationTokenSource.Token;

        var task = Task.Factory
            .StartNew(
                () =>
                {
                    cancellationToken.ThrowIfCancellationRequested();
                },
                cancellationToken)
            .Then(
                x =>
                {
                    executed = true;
                });

        try
        {
            task.Wait(TimeSpan.FromSeconds(3));
            Assert.True(false);
        }
        catch (AggregateException ex)
        {
            Assert.False(executed);
            Assert.Equal(TaskStatus.Canceled, task.Status);
            Assert.Equal(1, ex.InnerExceptions.Count);
        }
    }
}

[Fact]
public void then_non_generic_cancellation_in_second_task_results_in_overall_canceled_task()
{
    var executed = false;

    using (var cancellationTokenSource = new CancellationTokenSource())
    {
        // cancel up-front
        cancellationTokenSource.Cancel();

        var cancellationToken = cancellationTokenSource.Token;

        var task = Task.Factory
            .StartNew(() => { }, cancellationToken)
            .Then(
                x =>
                {
                    cancellationToken.ThrowIfCancellationRequested();
                })
            .Then(
                x =>
                {
                    executed = true;
                });

        try
        {
            task.Wait(TimeSpan.FromSeconds(3));
            Assert.True(false);
        }
        catch (AggregateException ex)
        {
            Assert.False(executed);
            Assert.Equal(TaskStatus.Canceled, task.Status);
            Assert.Equal(1, ex.InnerExceptions.Count);
        }
    }
}

[Fact]
public void then_non_generic_cancellation_is_automatic_if_next_task_is_null()
{
    var task = Task.Factory
        .StartNew(() => { })
        .Then(x => null);

    try
    {
        task.Wait(TimeSpan.FromSeconds(3));
        Assert.True(false);
    }
    catch (AggregateException ex)
    {
        Assert.Equal(TaskStatus.Canceled, task.Status);
        Assert.Equal(1, ex.InnerExceptions.Count);
    }
}

[Fact]
public void then_non_generic_antecedent_task_is_passed_through()
{
    var task1 = Task.Factory
        .StartNew(() => { });

    var task2 = task1
        .Then(x => Assert.Same(task1, x));

    var task3 = task2
        .Then(x => Assert.Same(task2, x));

    Assert.True(task3.Wait(TimeSpan.FromSeconds(2)));
}

[Fact]
public void then_generic_second_task_does_not_start_until_first_is_finished()
{
    var executed = false;

    var task = Task.Factory
        .StartNew(
            () =>
            {
                Thread.Sleep(100);
                executed = true;

                return "result 1";
            })
        .Then(
            x =>
            {
                Assert.True(executed);

                return "result 2";
            });

    Assert.True(task.Wait(TimeSpan.FromSeconds(3)));
    Assert.Equal("result 2", task.Result);
}

[Fact]
public void then_generic_fault_in_first_task_prevents_second_task_from_running()
{
    var executed = false;

    var task = Task.Factory
        .StartNew<string>(
            () =>
            {
                throw new InvalidOperationException("Failure");
            })
        .Then(
            x =>
            {
                executed = true;

                return "result 2";
            });

    try
    {
        task.Wait(TimeSpan.FromSeconds(3));
        Assert.True(false);
    }
    catch (AggregateException ex)
    {
        Assert.False(executed);
        Assert.Equal(TaskStatus.Faulted, task.Status);
        Assert.Equal(1, ex.InnerExceptions.Count);
    }
}

[Fact]
public void then_generic_fault_in_second_task_results_in_faulted_overall_task()
{
    var executed = false;

    var task = Task.Factory
        .StartNew(() => "result 1")
        .Then(
            x =>
            {
                // dummy test to appease compiler
                if (!executed != executed)
                {
                    throw new InvalidOperationException("Failure");
                }

                return "result 2";
            })
        .Then(
            x =>
            {
                executed = true;
                return "result 3";
            });

    try
    {
        task.Wait(TimeSpan.FromSeconds(3));
        Assert.True(false);
    }
    catch (AggregateException ex)
    {
        Assert.False(executed);
        Assert.Equal(TaskStatus.Faulted, task.Status);
        Assert.Equal(1, ex.InnerExceptions.Count);
    }
}

[Fact]
public void then_generic_cancellation_in_first_task_prevents_second_task_from_running()
{
    var executed = false;

    using (var cancellationTokenSource = new CancellationTokenSource())
    {
        // cancel up-front
        cancellationTokenSource.Cancel();

        var cancellationToken = cancellationTokenSource.Token;

        var task = Task.Factory
            .StartNew(
                () =>
                {
                    cancellationToken.ThrowIfCancellationRequested();

                    return "result 1";
                },
                cancellationToken)
            .Then(
                x =>
                {
                    executed = true;
                    return "result 2";
                });

        try
        {
            task.Wait(TimeSpan.FromSeconds(3));
            Assert.True(false);
        }
        catch (AggregateException ex)
        {
            Assert.False(executed);
            Assert.Equal(TaskStatus.Canceled, task.Status);
            Assert.Equal(1, ex.InnerExceptions.Count);
        }
    }
}

[Fact]
public void then_generic_cancellation_in_second_task_results_in_overall_canceled_task()
{
    var executed = false;

    using (var cancellationTokenSource = new CancellationTokenSource())
    {
        // cancel up-front
        cancellationTokenSource.Cancel();

        var cancellationToken = cancellationTokenSource.Token;

        var task = Task.Factory
            .StartNew(() => "result 1", cancellationToken)
            .Then(
                x =>
                {
                    cancellationToken.ThrowIfCancellationRequested();
                    return "result ";
                })
            .Then(
                x =>
                {
                    executed = true;
                    return "result 3";
                });

        try
        {
            task.Wait(TimeSpan.FromSeconds(3));
            Assert.True(false);
        }
        catch (AggregateException ex)
        {
            Assert.False(executed);
            Assert.Equal(TaskStatus.Canceled, task.Status);
            Assert.Equal(1, ex.InnerExceptions.Count);
        }
    }
}

[Fact]
public void then_generic_cancellation_is_automatic_if_next_task_is_null()
{
    var task = Task.Factory
        .StartNew(() => "result 1")
        .Then<string, string>((Func<Task<string>, Task<string>>)(x => null));

    try
    {
        task.Wait(TimeSpan.FromSeconds(3));
        Assert.True(false);
    }
    catch (AggregateException ex)
    {
        Assert.Equal(TaskStatus.Canceled, task.Status);
        Assert.Equal(1, ex.InnerExceptions.Count);
    }
}

[Fact]
public void then_generic_antecedent_task_is_passed_through()
{
    var task1 = Task.Factory
        .StartNew(() => "One");

    var task2 = task1
        .Then(
            x =>
            {
                Assert.Same(task1, x);
                return "Two";
            });

    var task3 = task2
        .Then(
            x =>
            {
                Assert.Same(task2, x);
                return "Three";
            });

    Assert.True(task3.Wait(TimeSpan.FromSeconds(2)));
}

[Fact]
public void then_generic_tasks_can_change_type()
{
    var task = Task.Factory
        .StartNew(() => "One")
        .Then(
            x =>
            {
                Assert.Equal("One", x.Result);
                return 2;
            })
        .Then(
            x =>
            {
                Assert.Equal(2, x.Result);
                return 3d;
            },
            TaskCreationOptions.None)
        .Then(
            x =>
            {
                Assert.Equal(3d, x.Result);
            });

    Assert.True(task.Wait(TimeSpan.FromSeconds(2)));
}

Alright, that about wraps it up. I hope it is of use to some of you.

Tuesday, March 13, 2012

Debugging Failed, Unobserved TPL Tasks

I've been doing some work with the task parallel library (TPL) of late. For the most part, I've found it a joy to work with, despite the fact that I'm using .NET 4.0 with VS2010 and am thus unable to leverage the async / await keywords in C# 5. However, I found that complex composition of tasks could lead to difficulty in debugging the inevitable failures you will encounter throughout development. Specifically, one might see an exception similar to this:

System.AggregateException: A Task's exception(s) were not observed either by Waiting on the Task or accessing its Exception property. As a result, the unobserved exception was rethrown by the finalizer thread.

As suggested by the exception message, this occurs when a task has failed but nothing has observed its failure. This is a good thing - it would be awful if the TPL silently swallowed these problems and left it to the developer to suss out what was going on. It's important to note that the TPL uses a finalizer to notify you of such problems. Therefore, you aren't likely to see the exception immediately when your task fails. Indeed, it may not appear for some time after the offending task completes, which can make debugging especially painful.

I found that even though I had access to the failing task, there wasn't enough contextual information for me to relate the task back to my source code. This is partly as a result of my complicated task composition and partly due to the fact that many of those tasks could result in the same generic exceptions.

To combat this problem, I decided it would be nice to attach a conceptual name to each task that I start. I could then retrieve that name when the failure occurs, and therefore be able to relate it back to a specific point in my code.

I defined a TaskExtensions class that allows me to associate names with tasks as follows:

var firstTask = Task.Factory
    .StartNew(...)
    .WithName("whatever I want to call this task");

var continuation = firstTask
    .ContinueWith(...)
    .WithName("my continuation");

I then added a handler for TaskScheduler.UnobservedTaskException that retrieved the conceptual name associated with the failing task.

Result! I was able to quickly determine which of my tasks was improperly observed and fix accordingly.

I have attached a sample project demonstrating this. If you run it, you'll see you can create successful or failing tasks and force garbage collection. If you create a failing task, you'll see a message output to the console, but no failure. If you then force garbage collection, you'll see an error message that includes the conceptual name of the task.

Note that in my real project, I limit the use of TaskExtensions to DEBUG builds only. This is just to avoid memory leaks (ie. holding onto a massive list of task names that never gets cleaned up). You could conceivably use a weak reference to the task as the key in the dictionary, but then you'll need to decide how and when expired entries are moved. It's a complication I just did not need to bother with, considering I did not want WithName() calls scattered about the source code anyway. In other words, the infrastructure I'm describing is, for me, a debug-only aid. Once debugged, I expect all calls to WithName() to be removed from the code.

In addition, my UnobservedTaskException handler is also DEBUG-only, and automatically breaks into the debugger:

#if DEBUG
    // this allows us to see tasks that were left unobserved
    TaskScheduler.UnobservedTaskException += delegate(object sender, UnobservedTaskExceptionEventArgs e)
    {
        var task = (Task)sender;
        var taskName = HSBC.TraderDesktop.Utility.Tasks.TaskExtensions.GetName(task);

        // if you break here, it's because you've left a TPL task unobserved
        // the task and any assigned name are given above
        // you can assign names to your tasks using the TaskExtensions class: task.WithName("...")
        Debugger.Break();
    };
#endif

This will instantly get the developer's attention as soon as reasonably possible, and gives them the information they need to begin resolving the issue.

Saturday, November 19, 2011

WindowItemsControl

The application I'm currently working on - top secret, mum's the word, your death for my indiscretion, you get the idea - includes a widget-style interface. In order to render these widgets, I use an ItemsControl and bind it to a collection of view models, each of which represents a widget. I use a Canvas to lay them out according to their XOffset and YOffset properties. Something like this:

<ItemsControl ItemsSource="{Binding Widgets}">
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <Canvas/>
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
    <ItemsControl.ItemContainerStyle>
        <Style>
            <Setter Property="Canvas.Left" Value="{Binding XOffset}"/>
            <Setter Property="Canvas.Top" Value="{Binding YOffset}"/>
        </Style>
    </ItemsControl.ItemContainerStyle>
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <views:WidgetView/>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

This all works fine and I'd even go so far as to say it's a beautiful thing. However, it is only a widget-style interface by virtue of some trickery on my part. Without said trickery, it would be more of an MDI interface.

With an MDI interface, multiple child windows are contained within a parent window – they cannot appear outside the bounds of the parent. Whilst my widgets look like windows, they aren't. They're just regular WPF user controls with some additional smarts to allow positioning and what-have-you. And whilst it looks like they're free to roam wherever they like on the desktop, they're not. They're all contained within the ItemsControl, which is within the only Window in my application. Thus, they cannot be positioned outside the area in which the Window resides. It just so happens that I've stretched that Window across the entire desktop and made it transparent. This gives the illusion that I have a widget interface and am thus as cool as The Dude himself, but I actually have an MDI interface which makes me more Napolean Dynamite than Lebowski. Before the dance, that is.

All this wouldn't concern me terribly (one learns to live with it) but my users are actually privy to more screen real estate than I. And when I say "more", I mean they have six screens whilst I have two, one of which I can't use for anything of import because it frequently distorts and shows other signs of discontent (no audible screams as yet). Because of this abundance of screen real estate, my Window has to to stretch across a huge expanse of pixels in order to keep up this illusion of cool. This has dire consequences for performance. You see, if one's Window size exceeds the maximum texture size of one's video card (I don't have one of course, but my users do) then the video card won't be able to accelerate rendering of said Window. The Window will be software-rendered instead, which is probably going to be a lot slower and less capable of Dude-worthy effects and animations.

No problem, you say. Just host your widgets inside windows instead and be done with it. And this is indeed what I am going to do.

But I want it to be seamless with respect to the current code base. I don't want to have to go hook up a bunch of event handlers to create/show/close windows when my widget collection changes. I don't want to have to change the way my view models keep track of widget positions and sizes (all persisted across application restarts, of course). All I want to do is change this:

<ItemsControl ItemsSource="{Binding Widgets}">

to this:

<WindowItemsControl ItemsSource="{Binding Widgets}">

But unfortunately WPF doesn't have a WindowItemsControl. Boo. And it doesn't seem as though anyone in the community has written one.

Obviously, then, I set out to write my own.

My initial approach failed, but it's worth discussing anyway. I tried to have my WindowItemsControl create Window instances as containers. This failed because internal WPF code was attempting to add these Windows as visual children of the ItemsControl, and Windows must be top-level visual items (makes sense). So I tried to hack around this because I really wanted the logical connection between the Window and the WindowItemsControl, much the same way there's a logical connection between a ListBoxItem and its containing ListBox. If I could trick WPF into forgoing the visual connection, I could then attempt the logical connection.

Well, I tried all sorts of nastiness, and ended up reflectively invoking an internal member to trick WPF into not including the Window as a visual child. Success! Right!? Alas, no, because when I then added the Window as a logical child of the WindowItemsControl, I got another similar error. I can't remember the details, nor can I explain why a Window cannot be a logical child of another control (it's only a logical connection, after all). But it didn't work and I gave up on this approach entirely.

My second approach is much more sane but gives up on creating a logical connection between the Windows and their host. But I don't really need that anyway – it was a nice-to-have.

What I did instead was had the WindowItemsControl create WindowItemsControlItem instances as containers. These containers are really just surrogates for the Window they represent. When they're initialized, they display the Window. When they're destroyed, they close the Window. In addition, if a Window is closed ahead of time, the corresponding data item is removed from the underlying collection and thus too the surrogate from the visual tree.

The code is actually quite neat and compact. Here is the code for WindowItemsControl:

public class WindowItemsControl : ItemsControl
{
	public static readonly DependencyProperty ShowDialogProperty = DependencyProperty.Register(
		"ShowDialog",
		typeof(bool),
		typeof(WindowItemsControl));

	public static readonly DependencyProperty OwnerProperty = DependencyProperty.Register(
		"Owner",
		typeof(Window),
		typeof(WindowItemsControl),
		new FrameworkPropertyMetadata(OnOwnerChanged));

	public static readonly DependencyProperty WindowStartupLocationProperty = DependencyProperty.Register(
		"WindowStartupLocation",
		typeof(WindowStartupLocation),
		typeof(WindowItemsControl));

	public static readonly DependencyProperty RemoveDataItemWhenWindowClosedProperty = DependencyProperty.Register(
		"RemoveDataItemWhenWindowClosed",
		typeof(bool),
		typeof(WindowItemsControl),
		new FrameworkPropertyMetadata(true));

	static WindowItemsControl()
	{
		DefaultStyleKeyProperty.OverrideMetadata(typeof(WindowItemsControl), new FrameworkPropertyMetadata(typeof(WindowItemsControl)));
	}

	public bool ShowDialog
	{
		get { return (bool)this.GetValue(ShowDialogProperty); }
		set { this.SetValue(ShowDialogProperty, value); }
	}

	public Window Owner
	{
		get { return this.GetValue(OwnerProperty) as Window; }
		set { this.SetValue(OwnerProperty, value); }
	}

	public WindowStartupLocation WindowStartupLocation
	{
		get { return (WindowStartupLocation)this.GetValue(WindowStartupLocationProperty); }
		set { this.SetValue(WindowStartupLocationProperty, value); }
	}

	public bool RemoveDataItemWhenWindowClosed
	{
		get { return (bool)this.GetValue(RemoveDataItemWhenWindowClosedProperty); }
		set { this.SetValue(RemoveDataItemWhenWindowClosedProperty, value); }
	}

	protected override DependencyObject GetContainerForItemOverride()
	{
		return new WindowItemsControlItem(this);
	}

	protected override bool IsItemItsOwnContainerOverride(object item)
	{
		return item is WindowItemsControlItem;
	}

	protected override void PrepareContainerForItemOverride(DependencyObject element, object item)
	{
		(element as WindowItemsControlItem).Window.Content = item;
	}

	protected override bool ShouldApplyItemContainerStyle(DependencyObject container, object item)
	{
		// the item container style will be applied to the windows, not to the containers (which are surrogates for the window)
		return false;
	}

	private static void OnOwnerChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
	{
		var windowItemsControl = (WindowItemsControl)dependencyObject;
		var owner = (Window)e.NewValue;

		for (var i = 0; i < windowItemsControl.Items.Count; ++i)
		{
			var container = windowItemsControl.ItemContainerGenerator.ContainerFromIndex(i) as WindowItemsControlItem;

			if (container == null)
			{
				continue;
			}

			container.Window.Owner = owner;
		}
	}
}

Pretty straightforward stuff. Note the following:

  • it declares some properties (ShowDialog, Owner, WindowStartupLocation) that assist it in the display of child Windows
  • it declares a RemoveDataItemWhenWindowClosed property that can be used to prevent the control from removing data items when a window is closed. This can be useful in shutdown or other situations where windows are being closed programmatically rather than by the user
  • I don't apply the ItemContainerStyle to the containers themselves, but instead hold out so that I can apply them to the Windows they represent
  • I also make sure that any change of Owner is applied to any existing Windows
  • the default style is overridden to remove unnecessary stuff like the Border, because the WindowItemsControl will never actually be visible on screen

The WindowItemsControl works in conjunction with the WindowItemsControlItem, which looks like this:

public class WindowItemsControlItem : FrameworkElement
{
	private readonly WindowItemsControl windowItemsControl;
	private readonly Window window;

	static WindowItemsControlItem()
	{
		// there is no need for these items to be visible as they are simply surrogates for the windows that they display
		VisibilityProperty.OverrideMetadata(typeof(WindowItemsControlItem), new FrameworkPropertyMetadata(Visibility.Collapsed));
	}

	public WindowItemsControlItem(WindowItemsControl windowItemsControl)
	{
		windowItemsControl.AssertNotNull("windowItemsControl");

		this.windowItemsControl = windowItemsControl;
		this.window = this.CreateWindow(windowItemsControl);

		this.Loaded += delegate
		{
			if (this.windowItemsControl.ShowDialog)
			{
				this.window.ShowDialog();
			}
			else
			{
				this.window.Show();
			}
		};

		this.Unloaded += delegate
		{
			this.window.Close();
		};
	}

	public Window Window
	{
		get { return this.window; }
	}

	private Window CreateWindow(WindowItemsControl windowItemsControl)
	{
		var window = new Window
		{
			Owner = windowItemsControl.Owner,
			WindowStartupLocation = windowItemsControl.WindowStartupLocation
		};

		BindingOperations.SetBinding(window, Window.DataContextProperty, new Binding("Content") { Source = window });
		BindingOperations.SetBinding(window, Window.StyleProperty, new Binding("ItemContainerStyle") { Source = windowItemsControl });
		BindingOperations.SetBinding(window, Window.ContentTemplateProperty, new Binding("ItemTemplate") { Source = windowItemsControl });
		BindingOperations.SetBinding(window, Window.ContentTemplateSelectorProperty, new Binding("ItemTemplateSelector") { Source = windowItemsControl });

		window.Closed += delegate
		{
			// orphan the content because it might be hosted somewhere else later (including in another window)
			window.Content = null;

			// if the window closes, attempt to remove the original item from the underlying collection, which will result in this surrogate being removed too
			if (windowItemsControl.RemoveDataItemWhenWindowClosed)
			{
				var editableItems = windowItemsControl.Items as IEditableCollectionView;

				if (editableItems != null && editableItems.CanRemove)
				{
					editableItems.Remove(this.DataContext);
				}
			}
		};

		return window;
	}
}

This is all pretty self-explanatory, too. The important points to note are:

  • relevant properties on the WindowItemsControl are bound to the correct properties on the Windows themselves
  • Windows are displayed when the surrogate is initialized, and closed when the surrogate is unloaded
  • as mentioned earlier, Windows that are closed before the surrogate is destroyed (perhaps by the user clicking the close button) result in the related data item in the underlying collection being removed (unless the RemoveDataItemWhenWindowClosed property has been set to false). This, in turn, will cause the surrogate to be removed from the visual tree. In other words, if I close a widget Window, the corresponding WidgetViewModel will be removed from my collection of widget view models. Then, the ItemsControl will remove the related surrogate container from the visual tree.

Now, as I yearned for at the beginning of this post, I can simply change my ItemsControl to a WindowItemsControl, make minor adjustments to my ItemContainerStyle and it just magically works.

The Dude abides.

I created a little demo to show how it all works, which you can download below. Enjoy!

Saturday, November 12, 2011

LogExportProvider

We all know how important logging is to any non-trivial application, so it stands to reason that we should make it as pain free as possible to add logging to our application components. But at the same time, we don't want to sacrifice too much, such as having to deal with a sub-standard logging API, or with a logging infrastructure that doesn't log the obvious stuff on our behalf.

Wouldn't it be nice if logging were as simple as this:

namespace SomeNamespace
{
    public class SomeClass
    {
        [ImportingConstructor]
        public SomeClass(ILoggerService loggerService)
        {
            loggerService.Debug("Created an instance of {0}.", GetType().Name);
        }
    }
}

And, importantly, the resultant log entry looked something like this:

[2011-11-12 12:09:04,749] [1] [DEBUG] [SomeNamespace.SomeClass] Created an instance of SomeClass.

Prism ships with an ILoggerFacade interface that looks like this:

public interface ILoggerFacade 
{ 
    void Log(string message, Category category, Priority priority); 
}

This is obviously a very bare-bones interface. Every time you write a log statement you'll be forced to:

  1. Format any parameters yourself.
  2. Specify the category as a parameter (instead of having separate methods for each category)
  3. Choose and specify a priority, even if it makes no sense for your code.
  4. Incur the cost of any preparation for the log statement even if the pertinent log level is disabled because you have no way to check whether it is or not.

This is not very consumer-friendly at all, and you'll more than likely begin to find logging a more onerous task than it should be.

Another problem with using ILoggerFacade in Prism is the lack of any originating source in the output. There is nothing intrinsic to differentiate log statements from different components. If two components log the same message ("Initialized", for example), you will have no way to tell which component was initialized!

These problems forced me to come up with a custom solution. I wanted my logging to be based on log4net, and I wanted MEF to provide my components with a logging service instance. Moreover, that service must produce log entries that are specific to my component.

The first problem (poor API) was easiest to solve. I defined my own interface as follows:

public interface ILoggerService 
{ 
    bool IsVerboseEnabled 
    { 
        get; 
    }

    bool IsDebugEnabled 
    { 
        get; 
    }

    bool IsInfoEnabled 
    { 
        get; 
    }

    bool IsWarnEnabled 
    { 
        get; 
    }

    bool IsErrorEnabled 
    { 
        get; 
    }

    bool IsPerfEnabled 
    { 
        get; 
    }

    void Verbose(string message);

    void Verbose(string message, Exception exception);

    void Verbose(string message, params object[] args);

    void Debug(string message);

    void Debug(string message, Exception exception);

    void Debug(string message, params object[] args);

    void Info(string message);

    void Info(string message, Exception exception);

    void Info(string message, params object[] args);

    void Warn(string message);

    void Warn(string message, Exception exception);

    void Warn(string message, params object[] args);

    void Error(string message);

    void Error(string message, Exception exception);

    void Error(string message, params object[] args);

    IDisposable Perf(string message);

    IDisposable Perf(string message, params object[] args); 
}

As you can see, this interface provides many overloads for all the relevant combinations of parameters you might need. This saves you, the caller, from having to deal with the annoyance of formatting messages or exceptions. There are also properties that can be used to check whether a given log level is enabled, which can be crucial in performance-critical paths. Finally, notice the handy Perf overloads which can be used to measure the performance of a block of code like this:

using (loggerService.Perf("Authenticating the user")) 
{ 
    // do authentication here
}

The only thing I haven't included (because I haven't needed it) are generic Write methods that take the log level as a parameter instead of inferring the log level from the method name. Such methods can be useful in dynamic logging scenarios, so you may want to add your own.

With the API defined, it was time to write an implementation:

public sealed class Log4NetLoggerService : ILoggerService 
{ 
    private static readonly Level perfLevel = new Level(35000, "PERF"); 
    private readonly ILog log;

    public Log4NetLoggerService(ILog log) 
    { 
        log.AssertNotNull("log"); 
        this.log = log; 
    }

    public bool IsVerboseEnabled 
    { 
        get { return this.log.Logger.IsEnabledFor(Level.Verbose); } 
    }

    public bool IsDebugEnabled 
    { 
        get { return this.log.IsDebugEnabled; } 
    }

    public bool IsInfoEnabled 
    { 
        get { return this.log.IsInfoEnabled; } 
    }

    public bool IsWarnEnabled 
    { 
        get { return this.log.IsWarnEnabled; } 
    }

    public bool IsErrorEnabled 
    { 
        get { return this.log.IsErrorEnabled; } 
    }

    public bool IsPerfEnabled 
    { 
        get { return this.log.Logger.IsEnabledFor(perfLevel); } 
    }

    public void Verbose(string message) 
    { 
        this.log.Logger.Log(typeof(Log4NetLoggerService), Level.Verbose, message, null); 
    }

    public void Verbose(string message, Exception exception) 
    { 
        this.log.Logger.Log(typeof(Log4NetLoggerService), Level.Verbose, message, exception); 
    }

    public void Verbose(string message, params object[] args) 
    { 
        this.log.Logger.Log(typeof(Log4NetLoggerService), Level.Verbose, new SystemStringFormat(CultureInfo.InvariantCulture, message, args), null); 
    }

    public void Debug(string message) 
    { 
        this.log.Debug(message); 
    }

    public void Debug(string message, Exception exception) 
    { 
        this.log.Debug(message, exception); 
    }

    public void Debug(string message, params object[] args) 
    { 
        this.log.DebugFormat(CultureInfo.InvariantCulture, message, args); 
    }

    public void Info(string message) 
    { 
        this.log.Info(message); 
    }

    public void Info(string message, Exception exception) 
    { 
        this.log.Info(message, exception); 
    }

    public void Info(string message, params object[] args) 
    { 
        this.log.InfoFormat(CultureInfo.InvariantCulture, message, args); 
    }

    public void Warn(string message) 
    { 
        this.log.Warn(message); 
    }

    public void Warn(string message, Exception exception) 
    { 
        this.log.Warn(message, exception); 
    }

    public void Warn(string message, params object[] args) 
    { 
        this.log.WarnFormat(CultureInfo.InvariantCulture, message, args); 
    }

    public void Error(string message) 
    { 
        this.log.Error(message); 
    }

    public void Error(string message, Exception exception) 
    { 
        this.log.Error(message, exception); 
    }

    public void Error(string message, params object[] args) 
    { 
        this.log.ErrorFormat(CultureInfo.InvariantCulture, message, args); 
    }

    public IDisposable Perf(string message) 
    { 
        message.AssertNotNull("message"); 
        return new PerfBlock(this, message); 
    }

    public IDisposable Perf(string message, params object[] args) 
    { 
        message.AssertNotNull("message"); 
        args.AssertNotNull("args"); 
        return new PerfBlock(this, string.Format(CultureInfo.InvariantCulture, message, args)); 
    }

    private sealed class PerfBlock : IDisposable 
    { 
        private readonly Log4NetLoggerService owner; 
        private readonly string message; 
        private readonly Stopwatch stopwatch; 
        private bool disposed;

        public PerfBlock(Log4NetLoggerService owner, string message) 
        { 
            this.owner = owner; 
            this.message = message; 
            this.stopwatch = Stopwatch.StartNew(); 
        }

        public void Dispose() 
        { 
            if (!this.disposed) 
            { 
                this.disposed = true; 
                this.stopwatch.Stop(); 
                var messageWithTimingInfo = string.Format(CultureInfo.InvariantCulture, "{0} [{1}, {2}ms]", this.message, this.stopwatch.Elapsed, this.stopwatch.ElapsedMilliseconds); 
                this.owner.log.Logger.Log(typeof(Log4NetLoggerService), perfLevel, messageWithTimingInfo, null); 
            } 
        } 
    } 
}

It's all pretty straightforward - most of the code just delegates to log4net.

But notice how the constructor requires a log4net.ILog? Log4net provides various ways by which an ILog can be obtained, but we would like to use LogManager.GetLogger(Type ownerType), where ownerType is the type importing our service. How, then, can we expect MEF to provide instances of ILoggerService when our constructor has a dependency that it cannot satisfy?

MEF supports an abstraction called ExportProvider, which is an object that can dynamically provide exports to satisfy matching imports. The trick to making this all work seamlessly is a custom ExportProvider that creates instances of Log4NetLoggerService on the fly to satisfy imports of type ILoggerService. In order to create Log4NetLoggerService instances, the export provider must know the type of the object that is importing the service. Thankfully, MEF supports obtaining this information through its reflection services.

Here is the code for our custom ExportProvider:

public sealed class LoggerServiceExportProvider : ExportProvider 
{ 
    private static readonly ILoggerService log = new Log4NetLoggerService(LogManager.GetLogger(typeof(LoggerServiceExportProvider))); 
    private readonly IDictionary<Type, ILoggerService> loggerServiceCache;

    public LoggerServiceExportProvider() 
    { 
        this.loggerServiceCache = new Dictionary<Type, ILoggerService>(); 
    }

    protected override IEnumerable<Export> GetExportsCore(ImportDefinition definition, AtomicComposition atomicComposition) 
    { 
        var contractName = definition.ContractName;

        log.Verbose("Attempting to resolve contract name '{0}' to a log instance.", contractName);

        if (string.IsNullOrEmpty(contractName)) 
        { 
            log.Verbose("Contract name is null or empty - cannot resolve."); 
            yield break; 
        }

        if (!string.Equals(typeof(ILoggerService).FullName, contractName, StringComparison.Ordinal)) 
        { 
            log.Verbose("Incorrect contract - cannot resolve."); 
            yield break; 
        }

        if (definition.Cardinality != ImportCardinality.ExactlyOne) 
        { 
            log.Verbose("Cardinality is {0} - cannot resolve.", definition.Cardinality); 
            yield break; 
        }

        // in order to get a log4net logger, we need the type importing the logger facade 
        Type ownerType = null;

        if (ReflectionModelServices.IsImportingParameter(definition)) 
        { 
            log.Verbose("Parameter import detected.");

            var importingParameter = ReflectionModelServices.GetImportingParameter(definition); 
            ownerType = importingParameter.Value.Member.DeclaringType; 
        } 
        else 
        { 
            log.Verbose("Property import detected.");

            var setAccessor = ReflectionModelServices 
                .GetImportingMember(definition) 
                .GetAccessors() 
                .Where(x => x is MethodInfo) 
                .Select(x => x as MethodInfo) 
                .FirstOrDefault(x => (x.Attributes & MethodAttributes.SpecialName) == MethodAttributes.SpecialName && x.Name.StartsWith("set_", StringComparison.Ordinal));

            if (setAccessor == null) 
            { 
                log.Verbose("Set accessor for property not found - cannot resolve."); 
                yield break; 
            }

            ownerType = setAccessor.DeclaringType; 
        }

        if (ownerType == null) 
        { 
            log.Verbose("Owner type could not be determined - cannot resolve."); 
            yield break; 
        }

        log.Verbose("Owner type is '{0}'.", ownerType.FullName);

        ILoggerService loggerService;

        if (!this.loggerServiceCache.TryGetValue(ownerType, out loggerService)) 
        { 
            log.Verbose("Logger facade for owner type '{0}' is not yet cached - creating it.", ownerType.FullName);

            var logInstance = LogManager.GetLogger(ownerType); 
            loggerService = new Log4NetLoggerService(logInstance); 
            this.loggerServiceCache[ownerType] = loggerService; 
        }

        var export = new Export(contractName, () => loggerService); 
        yield return export; 
    } 
}

There are several things to note about this implementation:

  1. Log service instances are cached. This means that if different instances of the same type import a logger service, they will (quickly) get the exact same instance.
  2. Imports can be either via constructors or through properties. The export provider supports both.
  3. The export provider itself includes logging statements, but it must do so with an explicitly created ILog instance. Obviously the export provider cannot import an ILoggerService itself or we'd have a chicken and egg problem!

We can tell MEF to use our custom ExportProvider in the usual fashion:

var compositionContainer = new CompositionContainer(new LoggerServiceExportProvider());

With this infrastructure in place, we achieve our objectives entirely. The code I included right at the beginning of this post will work, and any log entries will include the details of the originating type. And it is ridiculously easy for us to imbue components created by MEF with logging statements. Simply add the import and then invoke the simple-to-use APIs.

For a working example, click below. Enjoy!