callbackOnCollectedDelegate (MDA)
L'assistente al debug gestito callbackOnCollectedDelegate viene attivato se un delegato viene sottoposto a marshalling dal codice gestito a quello non gestito come puntatore a funzione e un callback viene inserito in tale puntatore dopo l'esecuzione della Garbage Collection del delegato.
Sintomi
Si verificano violazioni di accesso durante il tentativo di chiamata del codice gestito mediante puntatori a funzione ottenuti dai delegati gestiti. Questi errori, sebbene non siano bug di Common Language Runtime (CLR), possono apparire come tali in quanto la violazione di accesso avviene nel codice CLR.
L'errore non si verifica in modo coerente. In altre parole, a volte la chiamata sul puntatore a funzione riesce, altre no. È possibile che l'errore si verifichi solo con un carico pesante o in un numero casuale di tentativi.
Causa
Il delegato da cui il puntatore a funzione è stato creato ed esposto al codice non gestito è stato sottoposto a Garbage Collection. Quando il componente non gestito tenta la chiamata sul puntatore a funzione, genera una violazione di accesso.
L'errore sembra casuale in quanto dipende dal momento in cui si verifica la Garbage Collection. Se un delegato è idoneo per la Garbage Collection, questo processo si può verificare dopo il callback, caso in cui la chiamata riesce. Altre volte la Garbage Collection si verifica prima del callback, che genera una violazione di accesso, e il programma viene interrotto.
La probabilità dell'errore dipende dal periodo compreso tra il marshalling del delegato e il callback sul puntatore a funzione, nonché dalla frequenza di esecuzione delle Garbage Collection. L'errore si verifica raramente se il periodo compreso tra il marshalling del delegato e il callback è breve. Di solito, ciò avviene se il metodo non gestito che riceve il puntatore a funzione non salva quest'ultimo per utilizzi futuri, ma esegue il callback sul puntatore a funzione immediatamente per completare la relativa operazione prima di essere restituito. Analogamente, si verifica un numero maggiore di Garbage Collection quando un sistema è sottoposto a un carico eccessivo che rende più probabile l'esecuzione di una Garbage Collection prima del callback.
Risoluzione
Dopo aver eseguito il marshalling di un delegato come puntatore a funzione non gestito, il Garbage Collector non può tenere traccia del relativo ciclo di vita. Il codice deve invece conservare un riferimento al delegato per il ciclo di vita del puntatore a funzione non gestito, ma deve innanzitutto identificare il delegato sottoposto a Garbage Collection. Quando l'assistente al debug gestito viene attivato, specifica il nome del tipo del delegato. Utilizzare questo nome per cercare il codice relativo alla chiamata di P/Invoke o alle firme COM che passano il delegato in questione al codice non gestito. Il delegato all'origine dell'errore viene passato attraverso uno di questi siti di chiamata. È anche possibile attivare l'assistente al debug gestito gcUnmanagedToManaged per imporre una Garbage Collection prima di ogni callback nel runtime. In questo modo verranno eliminate le incertezze introdotte dalla Garbage Collection garantendo sempre l'esecuzione di una Garbage Collection prima del callback. Dopo aver individuato il delegato sottoposto a Garbage Collection, modificare il codice per conservare un riferimento al delegato in questione sul lato gestito del ciclo di vita del puntatore a funzione non gestito sottoposto a marshalling.
Effetto sul runtime
Quando viene eseguito il marshalling dei delegati come puntatori a funzione, il runtime alloca un thunk che effettua la transizione dal codice non gestito al codice gestito. Questo thunk è l'elemento chiamato dal codice gestito prima del richiamo finale del delegato gestito. Senza l'assistente al debug gestito callbackOnCollectedDelegate attivato, il codice di marshalling non gestito viene eliminato quando il delegato viene sottoposto a Garbage Collection. Con l'MDA callbackOnCollectedDelegate attivato, il codice di marshalling non gestito non viene eliminato immediatamente quando il delegato viene sottoposto a Garbage Collection. Le ultime 1.000 istanze vengono invece conservate per impostazione predefinita e modificate per attivare l'assistente quando chiamato. Il thunk verrà probabilmente eliminato dopo l'esecuzione della Garbage Collection di più di 1.001 delegati sottoposti a marshalling.
Output
L'assistente al debug gestito segnala il nome del tipo del delegato sottoposto a Garbage Collection prima di un tentativo di callback sul relativo puntatore a funzione non gestito.
Configurazione
Nell'esempio riportato di seguito vengono illustrate le opzioni di configurazione dell'applicazione. Viene impostato su 1.500 il numero di thunk conservato dall'assistente al debug gestito. I valori predefiniti, minimo e massimo di listSize sono rispettivamente 1.000, 50 e 2.000.
<mdaConfig>
<assistants>
<callbackOnCollectedDelegate listSize="1500" />
</assistants>
</mdaConfig>
Esempio
Nell'esempio riportato di seguito viene illustrata una situazione in cui è possibile attivare l'assistente al debug gestito di questo argomento.
// 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"); }
}
Vedere anche
Riferimenti
Concetti
Diagnostica degli errori tramite gli assistenti al debug gestito
Marshalling di interoperabilità