Nota:
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
Note
Este artículo es específico de .NET Framework. No se aplica a implementaciones más recientes de .NET, incluidas .NET 6 y versiones posteriores.
El callbackOnCollectedDelegate asistente de depuración administrada (MDA) se activa si un delegado se serializó desde código administrado a no administrado como puntero de función y se coloca una devolución de llamada en ese puntero de función después de que se haya recopilado el delegado.
Síntomas
Las infracciones de acceso se producen al intentar llamar al código administrado a través de punteros de función obtenidos de delegados administrados. Estos errores, aunque no los errores de Common Language Runtime (CLR), pueden parecer ser así porque la infracción de acceso se produce en el código CLR.
El error no es coherente; a veces, la llamada al puntero de función se realiza correctamente y a veces se produce un error. El error puede producirse solo bajo una carga pesada o en un número aleatorio de intentos.
Causa
Delegado desde el que se creó el puntero de función y se exponía al código no administrado. Cuando el componente no administrado intenta llamar a en el puntero de función, genera una infracción de acceso.
El error aparece aleatorio porque depende de cuándo se produce la recolección de elementos no utilizados. Si un delegado es apto para la recolección, la recolección de elementos no utilizados puede producirse después de la devolución de llamada y la llamada se realiza correctamente. En otras ocasiones, la recolección de elementos no utilizados se produce antes de la devolución de llamada, la devolución de llamada genera una infracción de acceso y el programa se detiene.
La probabilidad del error depende del tiempo entre serializar el delegado y la devolución de llamada en el puntero de función, así como la frecuencia de recolección de elementos no utilizados. El error es esporádico si el tiempo entre serializar el delegado y la devolución de llamada subsiguiente es breve. Este suele ser el caso si el método no administrado que recibe el puntero de función no guarda el puntero de función para su uso posterior, sino que llama de nuevo al puntero de función inmediatamente para completar su operación antes de devolver. De forma similar, se producen más recolecciones de elementos no utilizados cuando un sistema está bajo una carga pesada, lo que hace que sea más probable que se produzca una recolección de elementos no utilizados antes de la devolución de llamada.
Resolución
Una vez que un delegado se ha serializado como un puntero de función no administrado, el recolector de elementos no utilizados no puede realizar un seguimiento de su duración. En su lugar, el código debe mantener una referencia al delegado durante la vigencia del puntero de función no administrada. Pero antes de poder hacerlo, primero debe identificar qué delegado se recopiló. Cuando se activa el MDA, proporciona el nombre de tipo del delegado. Use este nombre para buscar el código de invocación de plataforma o firmas COM que pasen ese delegado al código no administrado. El delegado infractor se pasa a través de uno de estos sitios de llamada. También puede habilitar el gcUnmanagedToManaged MDA para forzar una recolección de elementos no utilizados antes de cada devolución de llamada en el tiempo de ejecución. Esto eliminará la incertidumbre introducida por la recolección de elementos no utilizados asegurándose de que una recolección de elementos no utilizados siempre se produce antes de la devolución de llamada. Una vez que sepa qué delegado se recopiló, cambie el código para mantener una referencia a ese delegado en el lado administrado durante la vigencia del puntero de función no administrada serializado.
Efecto en el tiempo de ejecución
Cuando los delegados se serializan como punteros de función, el tiempo de ejecución asigna un thunk que realiza la transición de no administrado a administrado. Este thunk es lo que realmente llama el código no administrado antes de que se invoque finalmente el delegado administrado. Sin el callbackOnCollectedDelegate MDA habilitado, el código de serialización no administrado se elimina cuando se recopila el delegado. Con el callbackOnCollectedDelegate MDA habilitado, el código de serialización no administrado no se elimina inmediatamente cuando se recopila el delegado. En su lugar, las últimas 1000 instancias se mantienen activas de forma predeterminada y se cambian para activar el MDA cuando se llama. El thunk se elimina finalmente después de que se recopilen 1001 delegados más serializado.
Output
El MDA informa del nombre de tipo del delegado que se recopiló antes de que se intentara una devolución de llamada en su puntero de función no administrado.
Configuration
En el ejemplo siguiente se muestran las opciones de configuración de la aplicación. Establece el número de matones que el MDA mantiene vivo a 1500. El valor predeterminado listSize es 1000, el mínimo es 50 y el máximo es 2000.
<mdaConfig>
<assistants>
<callbackOnCollectedDelegate listSize="1500" />
</assistants>
</mdaConfig>
Example
En el ejemplo siguiente se muestra una situación que puede activar este 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"); }
}