Nota
O acesso a esta página requer autorização. Pode tentar iniciar sessão ou alterar os diretórios.
O acesso a esta página requer autorização. Pode tentar alterar os diretórios.
Note
Este artigo é específico do .NET Framework. Ele não se aplica a implementações mais recentes do .NET, incluindo o .NET 6 e versões posteriores.
O assistente callbackOnCollectedDelegate de depuração gerido (MDA) é ativado se um delegado for marshallado de código gerido para não gerido como ponteiro de função e um callback for colocado nesse ponteiro de função depois de o delegado ter sido recolhido.
Sintomas
Violações de acesso ocorrem ao tentar aceder a código gerido através de apontadores de função obtidos de delegados geridos. Estas falhas, embora não sejam bugs de runtime de linguagem comum (CLR), podem parecer assim porque a violação de acesso ocorre no código CLR.
A falha não é consistente; Por vezes, a chamada do ponteiro de função tem sucesso e outras vezes falha. A falha pode ocorrer apenas sob carga elevada ou num número aleatório de tentativas.
Motivo
O delegado a partir do qual o ponteiro de função foi criado e exposto a código não gerido foi recolhido pelo lixo. Quando o componente não gerido tenta ligar ao ponteiro da função, gera uma violação de acesso.
A falha parece aleatória porque depende de quando ocorre a recolha de lixo. Se um delegado for elegível para recolha, a recolha de lixo pode ocorrer após o callback e a chamada é bem-sucedida. Noutras ocasiões, a recolha de lixo ocorre antes do callback, o callback gera uma violação de acesso e o programa para.
A probabilidade da falha depende do tempo entre o marshalling do delegado e o callback no ponteiro de função, bem como da frequência das recolhas de lixo. A falha é esporádica se o tempo entre a organização do delegado e a chamada subsequente for curto. Isto acontece normalmente se o método não gerido que recebe o ponteiro de função não guardar o ponteiro para uso posterior, mas sim chamar imediatamente o ponteiro de função para completar a sua operação antes de regressar. De forma semelhante, mais recolhas de lixo ocorrem quando um sistema está sob carga pesada, o que torna mais provável que ocorra uma recolha de lixo antes do callback.
Resolução
Uma vez que um delegado foi desligado como um ponteiro de função não gerido, o coletor de lixo não consegue acompanhar a sua vida útil. Em vez disso, o seu código deve manter uma referência ao delegado durante toda a vida útil do ponteiro de função não gerido. Mas antes de o fazeres, primeiro tens de identificar qual delegado foi recolhido. Quando a MDA é ativada, fornece o nome tipo do delegado. Use este nome para pesquisar no seu código assinaturas platform invoke ou COM que passem essa delegação para código não gerido. O delegado infrator é distribuído através de um destes sites de chamadas. Também pode ativar o gcUnmanagedToManaged MDA para forçar uma recolha de lixo antes de cada callback para o runtime. Isto eliminará a incerteza introduzida pela recolha de lixo, garantindo que a recolha de lixo ocorra sempre antes do callback. Quando souber que delegado foi recolhido, altere o seu código para manter uma referência a esse delegado do lado gerido durante toda a vida do ponteiro de função não gerida marshalled.
Efeito no tempo de execução
Quando os delegados são marshallados como ponteiros de função, o tempo de execução aloca um thunk que faz a transição de não gerido para gerido. Este thunk é o que o código não gerido realmente chama antes de o delegado gerido ser finalmente invocado. Sem o callbackOnCollectedDelegate MDA ativado, o código de marshalling não gerido é eliminado quando o delegado é recolhido. Com o callbackOnCollectedDelegate MDA ativado, o código de marshalling não gerido não é imediatamente eliminado quando o delegado é recolhido. Em vez disso, as últimas 1.000 instâncias são mantidas vivas por defeito e alteradas para ativar a MDA quando chamadas. O thunk é eventualmente eliminado depois de serem recolhidos mais 1.001 delegados organizados.
Output
A MDA reporta o nome tipo do delegado que foi recolhido antes de ser tentada uma chamada de retorno no seu ponteiro de função não gerido.
Configuração
O exemplo seguinte mostra as opções de configuração da aplicação. Estabelece o número de thunks que a MDA mantém vivos para 1.500. O valor padrão listSize é 1.000, o mínimo é 50 e o máximo é 2.000.
<mdaConfig>
<assistants>
<callbackOnCollectedDelegate listSize="1500" />
</assistants>
</mdaConfig>
Example
O exemplo seguinte demonstra uma situação que pode ativar 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"); }
}