2013-05-21 3 views
6

В C существует atexit функция, котораяВыполнение кода .NET при обычном выходе процесса?

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

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

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

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

+0

Вы можете попробовать CTRL_CLOSE_EVENT событие SetConsoleCtrlHandler. http://msdn.microsoft.com/en-us/library/windows/desktop/ms686016%28v=vs.85%29.aspx –

ответ

6

Нет прямого ответа, о котором я знаю.

Если вы хотите, чтобы написать надежный DLL, вы должны подготовить несколько сценариев:

  1. Ваш код размещается в приложении .NET, в AppDomain по умолчанию. (тривиальный сценарий)
  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; 

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

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

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

Во-первых, вы можете использовать метод 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 class (see MSDN here), наследуя его и помещая ваш код очистки в финализатор. Это требует, чтобы ваш код очистки придерживался руководящих принципов Constrained Execution Region.

+0

Я думаю, что либо я собираюсь пойти с «CriticalFinalizerObject», либо использовать «Critical/SafeHandle». Я на самом деле просто наткнулся на это сегодня, но не успел реализовать/протестировать его, но я думаю, что он должен обеспечить то, что я хочу. –

Смежные вопросы