Using-Klausel kann Dispose nicht aufrufen?

Ich verwende Visual Studio 2010 als Ziel für das .NET 4.0-Clientprofil. Ich habe eine C # -Klasse zu erkennen, wann ein bestimmter Prozess startet / endet. Zu diesem Zweck verwendet die Klasse einen ManagementEventWatcher, der wie folgt initialisiert wird.query, scope undwatcher sind Klassenfelder:

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();

Der Handler für Event EventArrived sieht folgendermaßen aus:

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");
    }
}

Dieser Code basiert aufein CodeProject-Artikel. Ich habe den Anruf hinzugefügtmbo.Dispose() weil es Speicher durchgesickert ist: Jedes Mal, wenn EventArrived ausgelöst wird, werden ca. 32 KB pro Sekunde ausgegeben. Das Leck ist sowohl bei WinXP als auch bei Win7 (64-Bit) offensichtlich.

So weit, ist es gut. Aus Gewissensgründen fügte ich hinzu:try-finally Klausel, wie folgt:

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

Kein Problem dort. Besser noch, das C #using Klausel ist kompakter, aber äquivalent:

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

Toll, erst jetzt ist der Speicherleck wieder da. Was ist passiert?

Ich weiß es nicht. Aber ich habe versucht, die beiden Versionen mit ILDASM zu zerlegen, die fast, aber nicht ganz gleich sind.

IL austry-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 aususing:

.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

Anscheinend ist das Problem diese Zeile:

IL_003c:  brfalse.s  IL_0044

das ist äquivalent zuif (mbo != null), sombo.Dispose() wird nie gerufen. Aber wie ist es möglich, dass mbo null ist, wenn es zugreifen kann?.ClassPath.ClassName?

Irgendwelche Gedanken dazu?

Außerdem frage ich mich, ob dieses Verhalten die ungelöste Diskussion hier erklärt:Speicherverlust in WMI beim Abfragen von Ereignisprotokollen.

Antworten auf die Frage(3)

Ihre Antwort auf die Frage