A cláusula de uso falha ao chamar Dispose?

Estou usando o Visual Studio 2010 para direcionar o .NET 4.0 Client Profile. Eu tenho uma classe c # para detectar quando um determinado processo inicia / termina. Para isso, a classe usa um ManagementEventWatcher, que é inicializado como abaixo;query, scope ewatcher são campos de classe:

query = new WqlEventQuery();
query.EventClassName = "__InstanceOperationEvent";
query.WithinInterval = new TimeSpan(0, 0, 1);
query.Condition = "TargetInstance ISA 'Win32_Process' AND TargetInstance.Name = 'notepad.exe'";

scope = new ManagementScope(@"\\.\root\CIMV2");

watcher = new ManagementEventWatcher(scope, query);
watcher.EventArrived += WatcherEventArrived;
watcher.Start();

O manipulador do evento EventArrived se parece com isto:

private void WatcherEventArrived(object sender, EventArrivedEventArgs e)
{
    string eventName;

    var mbo = e.NewEvent;
    eventName = mbo.ClassPath.ClassName;
    mbo.Dispose();

    if (eventName.CompareTo("__InstanceCreationEvent") == 0)
    {
        Console.WriteLine("Started");
    }
    else if (eventName.CompareTo("__InstanceDeletionEvent") == 0)
    {
        Console.WriteLine("Terminated");
    }
}

Este código é baseado emum artigo da CodeProject. Eu adicionei a chamada parambo.Dispose() porque vazou memória: cerca de 32 KB toda vez que o EventArrived é gerado, uma vez por segundo. O vazamento é óbvio em WinXP e Win7 (64 bits).

Por enquanto, tudo bem. Tentando ser consciencioso eu adicionei umtry-finally cláusula, assim:

var mbo = e.NewEvent;
try
{
    eventName = mbo.ClassPath.ClassName;
}
finally
{
    mbo.Dispose();
}

Não há problema aí. Melhor ainda, o c #using cláusula é mais compacta, mas equivalente:

using (var mbo = e.NewEvent)
{
    eventName = mbo.ClassPath.ClassName;
}

Ótimo, só agora o vazamento de memória está de volta. O que aconteceu?

Bem, eu não sei. Mas eu tentei desmontar as duas versões com o ILDASM, que são quase, mas não exatamente o mesmo.

IL detry-finally:

.try
{
  IL_0030:  nop
  IL_0031:  ldloc.s    mbo
  IL_0033:  callvirt   instance class [System.Management]System.Management.ManagementPath [System.Management]System.Management.ManagementBaseObject::get_ClassPath()
  IL_0038:  callvirt   instance string [System.Management]System.Management.ManagementPath::get_ClassName()
  IL_003d:  stloc.3
  IL_003e:  nop
  IL_003f:  leave.s    IL_004f
}  // end .try
finally
{
  IL_0041:  nop
  IL_0042:  ldloc.s    mbo
  IL_0044:  callvirt   instance void [System.Management]System.Management.ManagementBaseObject::Dispose()
  IL_0049:  nop
  IL_004a:  ldnull
  IL_004b:  stloc.s    mbo
  IL_004d:  nop
  IL_004e:  endfinally
}  // end handler
IL_004f:  nop

IL deusing:

.try
{
  IL_002d:  ldloc.2
  IL_002e:  callvirt   instance class [System.Management]System.Management.ManagementPath [System.Management]System.Management.ManagementBaseObject::get_ClassPath()
  IL_0033:  callvirt   instance string [System.Management]System.Management.ManagementPath::get_ClassName()
  IL_0038:  stloc.1
  IL_0039:  leave.s    IL_0045
}  // end .try
finally
{
  IL_003b:  ldloc.2
  IL_003c:  brfalse.s  IL_0044
  IL_003e:  ldloc.2
  IL_003f:  callvirt   instance void [mscorlib]System.IDisposable::Dispose()
  IL_0044:  endfinally
}  // end handler
IL_0045:  ldloc.1

Aparentemente, o problema é esta linha:

IL_003c:  brfalse.s  IL_0044

que é equivalente aif (mbo != null), assimmbo.Dispose() nunca é chamado. Mas como é possível que o mbo seja nulo se fosse capaz de acessar.ClassPath.ClassName?

Alguma idéia sobre isso?

Além disso, estou me perguntando se esse comportamento ajuda a explicar a discussão não resolvida aqui:Vazamento de memória no WMI ao consultar logs de eventos.

questionAnswers(3)

yourAnswerToTheQuestion