Выполнение кода .NET при обычном завершении процесса?

В C есть функция atexit, которая

Функция atexit() регистрирует данную функцию для вызова при нормальном завершении процесса либо через выход (3), либо через возврат из основной() программы.

Python имеет аналогичную возможность.

Предоставляет ли .NET способ вызова кода при обычном завершении процесса? Я знаю, что есть определенные вещи, такие как DomainUnload и ProcessExit, но, по крайней мере, насколько я могу судить, они ненадежны - либо требуют, чтобы приложение было Windows Forms (или приложение WPF), либо что-то еще. Я пишу код для .dll, поэтому я не могу полагаться на такие вещи, как лажа с основной функцией программы - обертывание ее в try/catch.

Моя конечная цель — выполнить некоторую очистку файлов (например, очистить буферы и закрыть). Если я могу вызвать неуправляемый код, например. хук win32api или что-то в этом роде, меня это вполне устраивает.


person Wayne Werner    schedule 21.05.2013    source источник
comment
Вы можете попробовать событие CTRL_CLOSE_EVENT SetConsoleCtrlHandler. msdn.microsoft.com/ en-us/library/windows/desktop/   -  person    schedule 21.05.2013


Ответы (1)


Я не знаю прямого ответа.

Если вы хотите написать надежную DLL, вам следует подготовиться к нескольким сценариям:

  1. Ваш код размещается в приложении .NET в домене приложений по умолчанию. (тривиальный сценарий)
  2. Ваш код размещается в приложении .NET в AppDomain, созданном кодом хоста.
  3. Ваш код размещается в неуправляемом приложении (в котором размещается среда CLR).

С третьим сценарием сложнее всего справиться, поскольку CLR может быть отключена хостом, поэтому управляемый код больше не будет выполняться.

System.Windows.Forms.Application.ApplicationExit не годится, так как применяется только к приложениям WinForm.

System.AppDomain.DomainUnload сам по себе бесполезен, так как он никогда не поднимается для AppDomain по умолчанию.

AppDomain.ProcessExit сам по себе бесполезен: если ваш код размещен в отдельном AppDomain, хост может выгрузить этот AppDomain, поэтому событие никогда не возникнет.

Я бы начал с попытки охватить большинство случаев, используя что-то вроде:

if (AppDomain.CurrentDomain.IsDefaultAppDomain())
    AppDomain.CurrentDomain.ProcessExit += MyTerminationHandler;
else
    AppDomain.CurrentDomain.DomainUnload += MyTerminationHandler;

Но обратите внимание на следующее замечание (из MSDN):

Общее время выполнения всех обработчиков событий ProcessExit ограничено, так же как общее время выполнения всех финализаторов ограничено при завершении процесса. По умолчанию две секунды. Неуправляемый узел может изменить это время выполнения, вызвав метод ICLRPolicyManager::SetTimeout со значением перечисления OPR_ProcessExit.

Приведенный выше код по-прежнему оставляет третий сценарий без присмотра. Я знаю два метода работы с этим сценарием (вместе с первыми двумя).

Во-первых, вы можете использовать метод System.Runtime.CompilerServices.RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup следующим образом:

{
//  this goes at your code's entry point
    RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup(MyExecutionCode, MyCleanupCode, null);
}
static void MyExecutionCode(object data) { /* your execution code here */}
static void MyCleanupCode(object data, bool exceptionThrown) { /* your cleanup code here */ }

Во-вторых, вы можете использовать класс System.Runtime.ConstrainedExecution.CriticalFinalizerObject (см. MSDN здесь), унаследовав его и поместив код очистки в финализатор. Для этого код очистки должен соответствовать рекомендациям Ограниченная область выполнения.

person M.A. Hanin    schedule 21.05.2013
comment
Я думаю, что либо я собираюсь пойти с CriticalFinalizerObject, либо использовать Critical/SafeHandle. На самом деле я только что наткнулся на это сегодня, но у меня не было времени реализовать/протестировать его, но я думаю, что он должен предоставить то, что я хочу. - person Wayne Werner; 22.05.2013