Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Note
Эта статья относится к .NET Framework. Он не применяется к более новым реализациям .NET, включая .NET 6 и более поздние версии.
Помощник callbackOnCollectedDelegate по управляемой отладке (MDA) активируется, если делегат маршалируется из управляемого в неуправляемый код в качестве указателя функции и обратный вызов помещается на этот указатель функции после того, как делегат был собран мусор.
Симптомы
Нарушения доступа возникают при попытке вызова управляемого кода с помощью указателей функций, полученных от управляемых делегатов. Эти ошибки, хотя и не clR, могут оказаться так, поскольку нарушение доступа возникает в коде CLR.
Сбой не согласован; Иногда вызов указателя функции завершается успешно, а иногда завершается сбоем. Сбой может возникать только при тяжелой нагрузке или случайном количестве попыток.
Причина
Делегат, из которого был создан указатель функции и предоставлен неуправляемому коду, был собран мусор. Когда неуправляемый компонент пытается вызвать указатель функции, он создает нарушение доступа.
Сбой появляется случайным образом, так как он зависит от того, когда происходит сборка мусора. Если делегат имеет право на сбор, сборка мусора может произойти после обратного вызова и вызова успешно. В других случаях сборка мусора возникает перед обратным вызовом, обратный вызов создает нарушение доступа, а программа останавливается.
Вероятность сбоя зависит от времени между маршалированием делегата и обратным вызовом на указателе функции, а также частотой сборок мусора. Сбой является спорадическим, если время между маршалированием делегата и последующим обратным вызовом является коротким. Обычно это происходит, если неуправляемый метод, получающий указатель функции, не сохраняет указатель функции для последующего использования, но вместо этого вызывает указатель функции немедленно, чтобы завершить свою операцию перед возвратом. Аналогичным образом, при большой нагрузке системы возникает больше сборок мусора, что делает его более вероятным, что сборка мусора будет происходить перед обратным вызовом.
Разрешение
После того как делегат был маршалирован как неуправляемый указатель функции, сборщик мусора не может отслеживать его время существования. Вместо этого код должен хранить ссылку на делегат в течение всего времени существования неуправляемого указателя функции. Но прежде чем это сделать, сначала необходимо определить, какой делегат был собран. При активации MDA он предоставляет имя типа делегата. Используйте это имя для поиска кода для вызова платформы или сигнатур COM, которые передают этот делегат в неуправляемый код. Делегат обижания передается через один из этих сайтов звонков. Кроме того, можно включить gcUnmanagedToManaged MDA для принудительной сборки мусора перед каждым обратным вызовом во время выполнения. Это приведет к удалению неопределенности, введенной сборкой мусора, гарантируя, что сборка мусора всегда происходит перед обратным вызовом. Когда вы знаете, какой делегат был собран, измените код, чтобы сохранить ссылку на этот делегат на управляемой стороне в течение времени существования маршалированного неуправляемого указателя функции.
Влияние на среду выполнения
Когда делегаты маршаллируются как указатели функций, среда выполнения выделяет thunk, которая выполняет переход от неуправляемого к управляемому. Это то, что неуправляемый код фактически вызывается перед вызовом управляемого делегата. Без включения MDA неуправляемый callbackOnCollectedDelegate код маршалинга удаляется при сборе делегата. При включении MDA неуправляемый callbackOnCollectedDelegate код маршалинга не сразу удаляется при сборе делегата. Вместо этого последние 1000 экземпляров сохраняются в живых по умолчанию и изменяются для активации MDA при вызове. Thunk в конечном итоге удаляется после сбора более 1001 более маршаллированных делегатов.
Выходные данные
MDA сообщает имя типа делегата, который был собран до попытки обратного вызова на неуправляемый указатель функции.
Конфигурация
В следующем примере показаны параметры конфигурации приложения. Он задает количество thunks MDA сохраняется в живых до 1500. Значение по умолчанию listSize — 1000, минимальное значение — 50, а максимальное — 2000.
<mdaConfig>
<assistants>
<callbackOnCollectedDelegate listSize="1500" />
</assistants>
</mdaConfig>
Example
В следующем примере показана ситуация, которая может активировать этот 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"); }
}