Udostępnij za pośrednictwem


callbackOnCollectedDelegate MDA

Uwaga

Ten artykuł jest specyficzny dla programu .NET Framework. Nie ma zastosowania do nowszych implementacji platformy .NET, w tym .NET 6 i nowszych wersji.

callbackOnCollectedDelegate Asystent zarządzanego debugowania (MDA) jest aktywowany, jeśli delegat jest rozdzielany z zarządzanego do niezarządzanego kodu jako wskaźnika funkcji, a wywołanie zwrotne jest umieszczane w tym wskaźniku funkcji po usunięciu pamięci delegata.

Objawy

Naruszenia dostępu występują podczas próby wywołania kodu zarządzanego za pośrednictwem wskaźników funkcji uzyskanych z zarządzanych delegatów. Te błędy, chociaż nie są to usterki środowiska uruchomieniowego języka wspólnego (CLR), mogą wydawać się tak, ponieważ naruszenie dostępu występuje w kodzie CLR.

Awaria nie jest spójna; czasami wywołanie wskaźnika funkcji kończy się powodzeniem i czasami kończy się niepowodzeniem. Awaria może wystąpić tylko w przypadku dużego obciążenia lub losowej liczby prób.

Przyczyna

Delegat, z którego utworzono wskaźnik funkcji i uwidoczniony w kodzie niezarządzanym, został odśmiecany. Gdy składnik niezarządzany próbuje wywołać wskaźnik funkcji, generuje naruszenie dostępu.

Awaria jest wyświetlana losowo, ponieważ zależy od tego, kiedy nastąpi odzyskiwanie pamięci. Jeśli pełnomocnik kwalifikuje się do odbioru, odzyskiwanie pamięci może nastąpić po wywołaniu zwrotnym i wywołanie zakończy się powodzeniem. W innych przypadkach odzyskiwanie pamięci następuje przed wywołaniem zwrotnym, wywołanie zwrotne generuje naruszenie dostępu, a program zatrzymuje się.

Prawdopodobieństwo awarii zależy od czasu między rozmieściem delegata a wywołaniem zwrotnym w wskaźniku funkcji, a także częstotliwością odzyskiwania pamięci. Błąd jest sporadyczne, jeśli czas między marshaling delegata a wywołaniem zwrotnym jest krótki. Jest to zwykle przypadek, jeśli niezarządzana metoda odbierającej wskaźnik funkcji nie zapisuje wskaźnika funkcji do późniejszego użycia, ale zamiast tego wywołuje wskaźnik funkcji natychmiast, aby ukończyć operację przed zwróceniem. Podobnie więcej odzyskiwania pamięci występuje, gdy system jest pod dużym obciążeniem, co sprawia, że jest bardziej prawdopodobne, że odzyskiwanie pamięci nastąpi przed wywołaniem zwrotnym.

Rozwiązanie

Gdy delegat został rozdzielony jako wskaźnik funkcji niezarządzanej, moduł odśmiecanie pamięci nie może śledzić jego okresu istnienia. Zamiast tego kod musi przechowywać odwołanie do delegata przez okres istnienia wskaźnika funkcji niezarządzanej. Jednak zanim będzie można to zrobić, musisz najpierw zidentyfikować, który delegat został zebrany. Po aktywowaniu mda podaje nazwę typu delegata. Użyj tej nazwy, aby wyszukać kod dla wywołań platformy lub podpisów COM, które przekazują ten delegat do niezarządzanych kodów. Obiekt delegujący jest przekazywany za pośrednictwem jednej z tych witryn połączeń. Możesz również włączyć usługę gcUnmanagedToManaged MDA, aby wymusić odzyskiwanie pamięci przed każdym wywołaniem zwrotnym w środowisku uruchomieniowym. Spowoduje to usunięcie niepewności wprowadzonej przez odzyskiwanie pamięci przez zapewnienie, że odzyskiwanie pamięci zawsze występuje przed wywołaniem zwrotnym. Gdy już wiesz, jaki delegat został zebrany, zmień kod, aby zachować odwołanie do tego delegata po stronie zarządzanej przez okres istnienia wskaźnika funkcji niezarządzanej.

Wpływ na środowisko uruchomieniowe

Gdy delegaty są marshaled jako wskaźniki funkcji, środowisko uruchomieniowe przydziela thunk, który przechodzi z niezarządzanego do zarządzanego. Ten thunk jest to, co kod niezarządzany rzeczywiście wywołuje przed wywołanie zarządzanego delegata. Bez włączonego rozwiązania callbackOnCollectedDelegate MDA niezarządzany kod marshallingu jest usuwany po zebraniu delegata. Po włączeniu callbackOnCollectedDelegate mdA niezarządzany kod marshalling nie jest natychmiast usuwany po zebraniu delegata. Zamiast tego ostatnie 1000 wystąpień jest domyślnie zachowywanych i zmienianych w celu aktywowania mdA po wywołaniu. Thunk zostanie ostatecznie usunięty po zebraniu 1001 więcej delegatów marshalled.

Wyjście

Usługa MDA zgłasza nazwę typu delegata, który został zebrany przed próbą wywołania zwrotnego w wskaźniku funkcji niezarządzanej.

Konfigurowanie

W poniższym przykładzie przedstawiono opcje konfiguracji aplikacji. Ustawia liczbę thunks MDA utrzymuje przy życiu do 1500. Wartość domyślna listSize to 1000, wartość minimalna to 50, a wartość maksymalna to 2000.

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

Przykład

W poniższym przykładzie pokazano sytuację, która może aktywować tę usługę 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"); }
}

Zobacz też