callbackOnCollectedDelegate MDA
Kommentar
Den här artikeln är specifik för .NET Framework. Det gäller inte för nyare implementeringar av .NET, inklusive .NET 6 och senare versioner.
Den callbackOnCollectedDelegate
hanterade felsökningsassistenten (MDA) aktiveras om ett ombud är kopplat från hanterad till ohanterad kod som en funktionspekare och en motringning placeras på funktionspekaren efter att ombudet har skräpinsamlats.
Symtom
Åtkomstöverträdelser uppstår när du försöker anropa hanterad kod via funktionspekare som hämtades från hanterade ombud. Dessa fel, även om det inte är vanliga CLR-buggar (language runtime), kan verka så eftersom åtkomstöverträdelsen inträffar i CLR-koden.
Felet är inte konsekvent. ibland lyckas anropet på funktionspekaren och misslyckas ibland. Felet kan bara inträffa under hög belastning eller vid ett slumpmässigt antal försök.
Orsak
Det ombud som funktionspekaren skapades från och exponerades för ohanterad kod var skräpinsamling. När den ohanterade komponenten försöker anropa funktionspekaren genererar den en åtkomstöverträdelse.
Felet visas slumpmässigt eftersom det beror på när skräpinsamling inträffar. Om ett ombud är berättigat till insamling kan skräpinsamlingen ske efter återanropet och anropet lyckas. Vid andra tillfällen sker skräpinsamlingen före återanropet, återanropet genererar en åtkomstöverträdelse och programmet stoppas.
Sannolikheten för felet beror på tiden mellan att delegera ombudet och återanropet på funktionspekaren samt frekvensen för skräpsamlingar. Felet är sporadiskt om tiden mellan att samla ombudet och det efterföljande återanropet är kort. Detta är vanligtvis fallet om den ohanterade metoden som tar emot funktionspekaren inte sparar funktionspekaren för senare användning, utan i stället anropar tillbaka på funktionspekaren omedelbart för att slutföra åtgärden innan den returneras. På samma sätt sker fler skräpsamlingar när ett system är hårt belastat, vilket gör det mer troligt att en skräpinsamling sker före återanropet.
Åtgärd
När ett ombud har ordnats som en ohanterad funktionspekare kan skräpinsamlaren inte spåra dess livslängd. I stället måste koden behålla en referens till ombudet under livslängden för den ohanterade funktionspekaren. Men innan du kan göra det måste du först identifiera vilket ombud som samlades in. När MDA aktiveras innehåller den typnamnet för ombudet. Använd det här namnet om du vill söka efter plattformsanrop eller COM-signaturer som skickar det delegerat till ohanterad kod. Den kränkande delegaten skickas via en av dessa samtalswebbplatser. Du kan också aktivera gcUnmanagedToManaged
MDA för att tvinga fram en skräpinsamling före varje återanrop till körningen. Detta tar bort den osäkerhet som införs av skräpinsamlingen genom att säkerställa att en skräpinsamling alltid inträffar före återanropet. När du vet vilket ombud som har samlats in ändrar du koden för att behålla en referens till ombudet på den hanterade sidan under livslängden för den ohanterade funktionspekaren.
Effekt på körningen
När ombud ordnas som funktionspekare allokerar körningen en thunk som gör övergången från ohanterad till hanterad. Den här thunken är vad den ohanterade koden faktiskt anropar innan det hanterade ombudet slutligen anropas. callbackOnCollectedDelegate
Utan MDA aktiverat tas den ohanterade marshallingkoden bort när ombudet samlas in. callbackOnCollectedDelegate
Med MDA aktiverat tas inte den ohanterade marshallingkoden bort omedelbart när ombudet samlas in. I stället hålls de sista 1 000 instanserna vid liv som standard och ändras för att aktivera MDA när de anropas. Thunken tas så småningom bort efter att ytterligare 1 001 marshalled delegater samlas in.
Output
MDA rapporterar typnamnet på ombudet som samlades in innan ett återanrop gjordes på dess ohanterade funktionspekare.
Konfiguration
I följande exempel visas alternativen för programkonfiguration. Det anger antalet thunks som MDA håller vid liv till 1 500. listSize
Standardvärdet är 1 000, minimivärdet är 50 och maxvärdet är 2 000.
<mdaConfig>
<assistants>
<callbackOnCollectedDelegate listSize="1500" />
</assistants>
</mdaConfig>
Exempel
I följande exempel visas en situation som kan aktivera denna MDA:
// 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"); }
}