callbackOnCollectedDelegate MDA
Notitie
Dit artikel is specifiek voor .NET Framework. Dit geldt niet voor nieuwere implementaties van .NET, waaronder .NET 6 en nieuwere versies.
De callbackOnCollectedDelegate
beheerde foutopsporingsassistent (MDA) wordt geactiveerd als een gemachtigde van beheerde naar niet-beheerde code als functieaanwijzer wordt geplaatst en er een callback op die functieaanwijzer wordt geplaatst nadat de gemachtigde garbagecollection is verzameld.
Symptomen
Toegangsschendingen treden op bij het aanroepen van beheerde code via functiepointers die zijn verkregen van beheerde gemachtigden. Deze fouten, terwijl er geen CLR-fouten (Common Language Runtime) optreden, kunnen dit zijn omdat de toegangsfout optreedt in de CLR-code.
De fout is niet consistent; soms slaagt de aanroep van de functiepointer en soms mislukt het. De fout kan alleen optreden onder zware belasting of bij een willekeurig aantal pogingen.
Oorzaak
De gemachtigde van waaruit de functiepointer is gemaakt en blootgesteld aan onbeheerde code, is garbagecollection verzameld. Wanneer het niet-beheerde onderdeel de functieaanwijzer probeert aan te roepen, wordt er een toegangsfout gegenereerd.
De fout wordt willekeurig weergegeven omdat deze afhankelijk is van wanneer de garbagecollection zich voordoet. Als een gemachtigde in aanmerking komt voor verzameling, kan de garbagecollection plaatsvinden nadat de callback is uitgevoerd en de aanroep is geslaagd. Op andere momenten vindt de garbagecollection plaats vóór de callback, genereert de callback een toegangsschending en stopt het programma.
De kans op de fout is afhankelijk van de tijd tussen het marshallen van de gemachtigde en de callback op de functieaanwijzer, evenals de frequentie van garbagecollections. De fout is sporadisch als de tijd tussen het marshallen van de gemachtigde en de daaruit voortvloeiende callback kort is. Dit is meestal het geval als de onbeheerde methode die de functie aanwijzer ontvangt, de functiepointer niet opslaat voor later gebruik, maar in plaats daarvan de functie-aanwijzer onmiddellijk weer aanroept om de bewerking te voltooien voordat de functie wordt geretourneerd. Op dezelfde manier treden er meer garbagecollection op wanneer een systeem zwaar wordt belast, waardoor het waarschijnlijker is dat een garbagecollection plaatsvindt vóór de callback.
Oplossing
Zodra een gemachtigde is uitgezet als een onbeheerde functieaanwijzer, kan de garbagecollector zijn levensduur niet bijhouden. In plaats daarvan moet uw code een verwijzing naar de gemachtigde behouden voor de levensduur van de onbeheerde functiepointer. Maar voordat u dat kunt doen, moet u eerst identificeren welke gemachtigde is verzameld. Wanneer de MDA is geactiveerd, wordt de typenaam van de gemachtigde opgegeven. Gebruik deze naam om uw code te doorzoeken op platform-aanroepen of COM-handtekeningen die delegeren doorgeven aan onbeheerde code. De gemachtigde wordt doorgegeven via een van deze oproepsites. U kunt de gcUnmanagedToManaged
MDA ook inschakelen om een garbagecollection af te dwingen voordat elke callback naar de runtime wordt uitgevoerd. Hierdoor wordt de onzekerheid die door de garbagecollection wordt geïntroduceerd, verwijderd door ervoor te zorgen dat een garbagecollection altijd plaatsvindt vóór de callback. Zodra u weet welke gemachtigde is verzameld, wijzigt u uw code om een verwijzing naar die gemachtigde aan de beheerde zijde te bewaren voor de levensduur van de niet-beheerde functieaanwijzer.
Effect op de runtime
Wanneer gemachtigden worden ge marshalld als functiepointers, wijst de runtime een thunk toe waarmee de overgang van niet-beheerde naar beheerde taken wordt uitgevoerd. Deze donk is wat de niet-beheerde code daadwerkelijk aanroept voordat de beheerde gemachtigde eindelijk wordt aangeroepen. Zonder de callbackOnCollectedDelegate
MDA ingeschakeld, wordt de niet-beheerde marshallcode verwijderd wanneer de gemachtigde wordt verzameld. Als de callbackOnCollectedDelegate
MDA is ingeschakeld, wordt de niet-beheerde marshallcode niet onmiddellijk verwijderd wanneer de gemachtigde wordt verzameld. In plaats daarvan worden de laatste 1000 exemplaren standaard in leven gehouden en gewijzigd om de MDA te activeren wanneer deze wordt aangeroepen. De thunk wordt uiteindelijk verwijderd nadat 1.001 meer marshall gedelegeerden zijn verzameld.
Uitvoer
De MDA rapporteert de typenaam van de gemachtigde die is verzameld voordat een callback is geprobeerd op de niet-beheerde functieaanwijzer.
Configuratie
In het volgende voorbeeld ziet u de configuratieopties voor toepassingen. Het stelt het aantal donks dat de MDA leeft tot 1500. De standaardwaarde listSize
is 1000, het minimum is 50 en het maximum is 2000.
<mdaConfig>
<assistants>
<callbackOnCollectedDelegate listSize="1500" />
</assistants>
</mdaConfig>
Opmerking
In het volgende voorbeeld ziet u een situatie waarin deze MDA kan worden geactiveerd:
// 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"); }
}