Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
Note
Dieser Artikel ist spezifisch für .NET Framework. Sie gilt nicht für neuere Implementierungen von .NET, einschließlich .NET 6 und höherer Versionen.
Der callbackOnCollectedDelegate verwaltete Debugging-Assistent (Managed Debugging Assistant, MDA) wird aktiviert, wenn eine Stellvertretung von verwaltetem zu nicht verwaltetem Code als Funktionszeiger ge marshallt wird, und ein Rückruf wird auf diesem Funktionszeiger platziert, nachdem die Stellvertretung garbage collection wurde.
Symptome
Zugriffsverletzungen treten auf, wenn Sie versuchen, verwalteten Code über Funktionszeiger aufzurufen, die von verwalteten Stellvertretungen abgerufen wurden. Diese Fehler, während keine ClR-Fehler (Common Language Runtime) auftreten, sind möglicherweise so, dass die Zugriffsverletzung im CLR-Code auftritt.
Der Fehler ist nicht konsistent; manchmal ist der Aufruf des Funktionszeigers erfolgreich und manchmal schlägt er fehl. Der Fehler kann nur bei hoher Auslastung oder bei einer zufälligen Anzahl von Versuchen auftreten.
Ursache
Der Delegat, aus dem der Funktionszeiger erstellt und für nicht verwalteten Code verfügbar gemacht wurde, wurde garbage collection. Wenn die nicht verwaltete Komponente versucht, den Funktionszeiger aufzurufen, wird eine Zugriffsverletzung generiert.
Der Fehler wird zufällig angezeigt, da er davon abhängt, wann die Garbage Collection auftritt. Wenn eine Stellvertretung für die Sammlung berechtigt ist, kann die Garbage Collection nach dem Rückruf auftreten und der Aufruf erfolgreich ausgeführt werden. Zu anderen Zeiten tritt die Garbage Collection vor dem Rückruf auf, der Rückruf generiert einen Zugriffsverstoß, und das Programm wird beendet.
Die Wahrscheinlichkeit des Fehlers hängt von der Zeit zwischen dem Marshalling des Delegaten und dem Rückruf für den Funktionszeiger sowie der Häufigkeit der Garbage Collections ab. Der Fehler ist sporadisch, wenn die Zeit zwischen dem Marshalling des Delegaten und dem anschließenden Rückruf kurz ist. Dies ist in der Regel der Fall, wenn die nicht verwaltete Methode, die den Funktionszeiger empfängt, den Funktionszeiger nicht für die spätere Verwendung speichert, sondern stattdessen sofort den Funktionszeiger aufruft, um den Vorgang abzuschließen, bevor er zurückgegeben wird. Ebenso treten mehr Garbage Collections auf, wenn ein System stark belastet wird, wodurch es wahrscheinlicher wird, dass eine Garbage Collection vor dem Rückruf auftritt.
Auflösung
Sobald eine Stellvertretung als nicht verwalteter Funktionszeiger ge marshallt wurde, kann der Garbage Collector seine Lebensdauer nicht nachverfolgen. Stattdessen muss Ihr Code einen Verweis auf den Delegaten für die Lebensdauer des nicht verwalteten Funktionszeigers beibehalten. Bevor Sie dies jedoch tun können, müssen Sie zuerst ermitteln, welche Stellvertretung gesammelt wurde. Wenn die MDA aktiviert wird, wird der Typname des Delegaten bereitgestellt. Verwenden Sie diesen Namen, um Ihren Code nach Plattformaufrufen oder COM-Signaturen zu durchsuchen, die diese Stellvertretung an nicht verwalteten Code übergeben. Die anstöbende Stellvertretung wird über eine dieser Anrufwebsites weitergegeben. Sie können die gcUnmanagedToManaged MDA auch aktivieren, um eine Garbage Collection vor jedem Rückruf in die Laufzeit zu erzwingen. Dadurch wird die Von der Garbage Collection eingeführte Unsicherheit aufgehoben, indem sichergestellt wird, dass eine Garbage Collection immer vor dem Rückruf auftritt. Sobald Sie wissen, welche Stellvertretung gesammelt wurde, ändern Sie Ihren Code, um einen Verweis auf diesen Delegaten auf der verwalteten Seite für die Lebensdauer des ge marshallten nicht verwalteten Funktionszeigers beizubehalten.
Effekt auf die Laufzeit
Wenn Stellvertretungen als Funktionszeiger ge marshallt werden, weist die Laufzeit einen Thunk zu, der den Übergang von nicht verwaltet zu verwaltet. Dieser Thunk ruft der nicht verwaltete Code tatsächlich auf, bevor der verwaltete Delegat schließlich aufgerufen wird. Ohne die callbackOnCollectedDelegate MDA-Aktivierung wird der nicht verwaltete Marshallcode gelöscht, wenn die Stellvertretung gesammelt wird. Wenn die callbackOnCollectedDelegate MDA aktiviert ist, wird der nicht verwaltete Marshallcode nicht sofort gelöscht, wenn die Stellvertretung gesammelt wird. Stattdessen werden die letzten 1.000 Instanzen standardmäßig aktiv gehalten und geändert, um die MDA beim Aufruf zu aktivieren. Die Thunk wird schließlich gelöscht, nachdem 1.001 weitere ge marshallte Delegierte gesammelt wurden.
Output
MDA meldet den Typnamen des Delegaten, der gesammelt wurde, bevor ein Rückruf für den nicht verwalteten Funktionszeiger versucht wurde.
Konfiguration
Das folgende Beispiel zeigt die Anwendungskonfigurationsoptionen. Es legt die Anzahl der Thunks fest, die die MDA auf 1.500 lebendig hält. Der Standardwert listSize ist 1.000, der Mindestwert ist 50, und das Maximum beträgt 2.000.
<mdaConfig>
<assistants>
<callbackOnCollectedDelegate listSize="1500" />
</assistants>
</mdaConfig>
Example
Das folgende Beispiel zeigt eine Situation, die diese MDA aktivieren kann:
// Library.cpp : Defines the unmanaged entry point for the DLL application.
#include "windows.h"
#include "stdio.h"
void (__stdcall *g_pfTarget)();
void __stdcall Initialize(void __stdcall pfTarget())
{
g_pfTarget = pfTarget;
}
void __stdcall Callback()
{
g_pfTarget();
}
// C# Client
using System;
using System.Runtime.InteropServices;
public class Entry
{
public delegate void DCallback();
public static void Main()
{
new Entry();
Initialize(Target);
GC.Collect();
GC.WaitForPendingFinalizers();
Callback();
}
public static void Target()
{
}
[DllImport("Library", CallingConvention = CallingConvention.StdCall)]
public static extern void Initialize(DCallback pfDelegate);
[DllImport ("Library", CallingConvention = CallingConvention.StdCall)]
public static extern void Callback();
~Entry() { Console.Error.WriteLine("Entry Collected"); }
}