Partilhar via


callbackOnCollectedDelegate MDA

O callbackOnCollectedDelegate Assistente de depuração gerenciada (MDA) está ativado, se um delegado empacotado do código gerenciado para como um ponteiro de função e um retorno de chamada é colocado no ponteiro de função que, após o delegado lixo coletado.

Sintomas

Violações de acesso ocorrerem ao tentar chamar código gerenciado através de ponteiros de função que foram obtidas de delegados gerenciados. Essas falhas, enquanto os bugs de runtime (CLR) de idioma não comum, podem parecer isso porque a violação de acesso ocorre no código CLR.

A falha não é consistente; às vezes, a chamada no ponteiro de função tiver êxito e às vezes falhar. A falha pode ocorrer somente sob carga pesada ou em um número aleatório de tentativas.

Causa

O delegado a partir do qual o ponteiro de função foi criado e exposto a código não gerenciado estava lixo coletado. Quando o componente não gerenciado tenta chamar no ponteiro de função, ele gera uma violação de acesso.

A falha aparece aleatória, porque ele depende quando ocorre a coleta de lixo. Se um delegado é qualificado para coleta, a coleta de lixo pode ocorrer após o retorno de chamada e a chamada for bem-sucedida. Em outros momentos, a coleta de lixo ocorre antes do retorno de chamada, o retorno de chamada gera uma violação de acesso e o programa pára.

A probabilidade da falha depende do tempo entre o representante e o retorno de chamada no ponteiro de função, bem como a freqüência das coletas de lixo de empacotamento. A falha é esporádica se o tempo entre o representante e o retorno de chamada correlatas de empacotamento é curto. Isso geralmente é o caso, se o método não gerenciado, recebendo o ponteiro de função não salva o ponteiro de função para uso posterior, mas em vez disso, chama de volta no ponteiro de função imediatamente para concluir sua operação antes de retornar. Da mesma forma, mais coletas de lixo ocorrem quando um sistema está sob carga pesada, o que torna mais provável que a coleta de lixo ocorrerá antes do retorno de chamada.

Resolução

Depois de um delegado foi empacotado fora como um ponteiro de função não gerenciada, o coletor de lixo não pode controlar o seu ciclo de vida. Em vez disso, seu código deve manter uma referência ao delegado para o tempo de vida do ponteiro de função não gerenciada. Mas, antes de fazer isso, você primeiro deve identificar qual delegado foi coletado. Quando o MDA é ativado, ele fornece o nome do tipo do delegado. Use esse nome para chamar de seu código para a plataforma de pesquisa ou assinaturas COM que passar esse delegado para código não gerenciado. O delegado ofensivo é passado para fora por meio de um desses sites de chamada. Você também pode ativar o gcUnmanagedToManaged MDA para forçar uma coleta de lixo antes de cada retorno de chamada em tempo de execução. Isso removerá a incerteza introduzida pela coleta de lixo, garantindo que sempre uma coleta de lixo ocorre antes do retorno de chamada. Você sabe qual delegado foi coletado, altere o seu código para manter uma referência a esse delegado no lado gerenciado para o tempo de vida do ponteiro empacotado de função não gerenciada.

Efeito sobre o tempo de execução.

Quando os delegados são empacotados como ponteiros de função, o runtime aloca uma conversão que faz a transição de não gerenciado para gerenciado. Essa conversão é o que o código não gerenciado realmente chama antes do gerenciado delegado finalmente é chamado. Sem o callbackOnCollectedDelegate MDA habilitado, o código de empacotamento não gerenciado é excluído quando o delegado é coletado. Com o callbackOnCollectedDelegate MDA habilitado, o código de empacotamento não gerenciado não é excluído imediatamente quando o delegado é coletado. Em vez disso, as última 1.000 instâncias são mantidas vivas por padrão e alteradas para ativar o MDA quando chamado. A conversão eventualmente é excluída após o 1.001 delegados mais empacotados são coletados.

Saída

O MDA informa o nome de tipo do delegado que foi coletado antes de um retorno de chamada foi tentado no seu ponteiro de função não gerenciada.

Configuração

O exemplo a seguir mostra as opções de configuração do aplicativo. Ele define o número de thunks que o MDA mantém 1.500 alive. O padrão listSize valor é 1.000, o mínimo é de 50 e o máximo é de 2.000.

<mdaConfig>
  <assistants>
    <callbackOnCollectedDelegate listSize="1500" />
  </assistants>
</mdaConfig>

Exemplo

O exemplo a seguir 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"); }
}

Consulte também

Referência

MarshalAsAttribute

gcUnmanagedToManaged MDA

Conceitos

Diagnosticar erros com assistentes de depuração gerenciada

Interop Marshaling

Outros recursos

Interoperabilidade