Wednesday, May 29, 2013

Garbage Collector's sabotage of my message pump

I spent my last days with a boring problem. I implemented a Windows service listening to WM_DEVICECHANGE messages (which will be sent par example, when you plug-in a new USB device). For this I implemented a NativeWindow, whose only purpose was to implement the window procedure for some custom message processing:

internal sealed class DeviceChangeHandler : NativeWindow
{
  private const int WM_DEVICECHANGE = 0x219;
  private const int DBT_DEVNODES_CHANGED = 0x7;

  public DeviceChangeHandler()
  {
    CreateHandle(new CreateParams());
  }

  protected override void WndProc(ref Message msg)
  {
    if (msg.Msg == WM_DEVICECHANGE && 
        msg.WParam.ToInt32() == DBT_DEVNODES_CHANGED)
    {
      // device change detected
      ...
    }

    base.WndProc(ref msg);
  }
}

In my service itself, I started a new Thread with the following ThreadStart:

private void RunDeviceChangeHandler()
{
  // create window
  DeviceChangeHandler deviceChangeHandler = new DeviceChangeHandler();

  // run message pump
  Application.Run();
}  

When I debugged the code, it worked always. Also my release build worked - but unfortunately not always. Sometimes I got the messages, sometimes not. Testing was tedious, since Windows takes some time until sending the message, especially after disconnecting the device.

Finally I remembered a tool I used 10 years ago: Spy++, which is still part of Visual Studio. I saw that, when I didn't get messages, there was also no window. That explained to me, why I didn't get the messages. But the question was now: why was there no window?

Next I defined a caption for my window (via the CreateParams). And I implemented a loop in which I call every 5 seconds FindWindow with the just specified caption. Now I saw that FindWindow was able to find my window. But only for some iterations, sooner or later it returned only 0.

By chance, I added tracing to the finalizer of my NativeWindow implementation. Now I saw that the finalizer was called some time, and afterwards FindWindow returned only 0. My first idea was that there was an exception in Application.Run(). But this was to easy.

The problem is optimization done by the just-in-time compiler. Since the variable deviceChangeHandler is no longer referenced after Application.Run(), it is a candidate for garbage collection. This I could verify by a call of GC.Collect(). Afterwards the windows was always gone.

The solution is to make the variable ineligible for garbage collection. For this purpose exists the method GC.KeepAlive().
With my modified ThreadStart, everything worked as expected:

private void RunDeviceChangeHandler()
{
  // create window
  DeviceChangeHandler deviceChangeHandler = new DeviceChangeHandler();

  // run message pump
  Application.Run();

  // make deviceChangeHandler ineligible for garbage collection
  GC.KeepAlive(deviceChangeHandler);
}  
 
As with the most complex problems, the solution is only one line. The art is to insert it at the right place.

Wednesday, May 15, 2013

Pitfall with ASP.NET Web Api, OWIN and FxCop


As I wrote in Self-host ASP.NET Web API and SignalR together, you can easily combine ASP.NET Web Api and OWIN. My Startup class contained at least the Configuration method:
public void Configuration(IAppBuilder app)
{
  HttpConfiguration config = new HttpConfiguration();
  config.Routes.MapHttpRoute("API Default", "api/{controller}/{id}", new { id = RouteParameter.Optional });
  app.UseWebApi(config);
}
With this, everything worked fine.

(Unfortunately) I am a well-behaved man. Therefore I ran the code analysis (aka FxCop). It returned the warning
CA2000: Microsoft.Reliability: In method 'Startup.Configuration(IAppBuilder)', call System.IDisposable.Dispose on object 'config' before all references to it are out of scope.
Understandable for me. And since Microsoft suggests to fix it, I changed my method to:
public void Configuration(IAppBuilder app)
{
  using (HttpConfiguration config = new HttpConfiguration())
  {
    config.Routes.MapHttpRoute("API Default", "api/{controller}/{id}", new { id = RouteParameter.Optional });
    app.UseWebApi(config);
  }
}
Sadly, I didn’t test it immediately. Instead I fixed a lot of other FxCop warnings. And I improved my coding otherwise. Finally I forgot this special fix.

When I finally put all together, I got only HTTP 500 Internal Server Error, without any further information. A lot of people suggest in such a case to debug the server, but also there no exception was thrown. It simply didn’t work. I slimmed down my coding until I removed the using statement. And then it worked again!

So it’s better to keep the original code and to add only a SuppressMessage attribute:
[SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope",
                 Justification = "HttpConfiguration must not be disposed, otherwise web api will not work")]
public void Configuration(IAppBuilder app)
{
  HttpConfiguration config = new HttpConfiguration();
  config.Routes.MapHttpRoute("API Default", "api/{controller}/{id}", new { id = RouteParameter.Optional });
  app.UseWebApi(config);
}