#define TRACE using System; using System.Diagnostics; using System.Threading; namespace ArchestrAServices.Common; public class ServiceShutdownControl : IDisposable { public delegate void ShutdownCallback(); private readonly ShutdownCallback m_Callback; private bool disposed; public ServiceShutdownControl(ShutdownCallback callback = null) { m_Callback = callback; } ~ServiceShutdownControl() { Dispose(disposing: false); } public static void SignalShutdown(Process watchedServiceProcess, string serviceInstanceName) { SvcTrace.DiagDiagnostics.TraceEvent(TraceEventType.Information, 0, "ServiceShutdownControl: SignalShutdown called"); try { if (!string.IsNullOrWhiteSpace(serviceInstanceName)) { EventWaitHandle eventWaitHandle = EventWaitHandle.OpenExisting(serviceInstanceName); SvcTrace.DiagDiagnostics.TraceEvent(TraceEventType.Information, 0, $"ServiceShutdownControl: Setting shutdown signal for process {serviceInstanceName}"); eventWaitHandle.Set(); SvcTrace.DiagDiagnostics.TraceEvent(TraceEventType.Information, 0, $"ServiceShutdownControl: Closing the process {serviceInstanceName}"); watchedServiceProcess?.WaitForExit(15000); } else { SvcTrace.DiagDiagnostics.TraceEvent(TraceEventType.Warning, 0, "ServiceShutdownControl: Process name cannot be null/empty."); } } catch (WaitHandleCannotBeOpenedException) { SvcTrace.DiagException.TraceEvent(TraceEventType.Error, 0, "ServiceShutdownControl: Named event does not exist."); } catch (UnauthorizedAccessException ex2) { SvcTrace.DiagException.TraceEvent(TraceEventType.Error, 0, $"ServiceShutdownControl: Unauthorized access: {ex2.Message}"); } catch (Exception ex3) { SvcTrace.DiagException.TraceEvent(TraceEventType.Error, 0, $"ServiceShutdownControl: Exception in SignalShutdown: {ex3.Message}"); } if (watchedServiceProcess != null && !watchedServiceProcess.HasExited) { try { watchedServiceProcess.Kill(); } catch (Exception) { } } SvcTrace.DiagDiagnostics.TraceEvent(TraceEventType.Information, 0, "ServiceShutdownControl: SignalShutdown exited"); } public void WaitForShutdown(string serviceInstanceName) { SvcTrace.DiagDiagnostics.TraceEvent(TraceEventType.Information, 0, "ServiceShutdownControl: WaitForShutdown called."); try { if (!string.IsNullOrEmpty(serviceInstanceName)) { EventWaitHandle eventWaitHandle = new EventWaitHandle(initialState: false, EventResetMode.AutoReset, serviceInstanceName); SvcTrace.DiagDiagnostics.TraceEvent(TraceEventType.Information, 0, $"ServiceShutdownControl: Starting wait for shutdown signal for process {serviceInstanceName}"); eventWaitHandle.WaitOne(); SvcTrace.DiagDiagnostics.TraceEvent(TraceEventType.Information, 0, $"ServiceShutdownControl: Received shutdown signal, stopping process {serviceInstanceName}"); } else { SvcTrace.DiagDiagnostics.TraceEvent(TraceEventType.Warning, 0, "ServiceShutdownControl: Process name cannot be null/empty."); } } catch (WaitHandleCannotBeOpenedException) { SvcTrace.DiagException.TraceEvent(TraceEventType.Error, 0, $"ServiceShutdownControl: Named event does not exist for process {serviceInstanceName}"); } catch (UnauthorizedAccessException ex2) { SvcTrace.DiagException.TraceEvent(TraceEventType.Error, 0, $"ServiceShutdownControl: Unauthorized access: {ex2.Message}"); } catch (Exception ex3) { SvcTrace.DiagException.TraceEvent(TraceEventType.Error, 0, $"ServiceShutdownControl: Exception in WaitForShutdown: {ex3.Message}"); } try { if (m_Callback != null) { SvcTrace.DiagDiagnostics.TraceEvent(TraceEventType.Information, 0, "ServiceShutdownControl: Calling user-supplied shutdown callback"); m_Callback(); SvcTrace.DiagDiagnostics.TraceEvent(TraceEventType.Information, 0, "ServiceShutdownControl: Returned from user-supplied shutdown callback"); } } catch (Exception ex4) { SvcTrace.DiagException.TraceEvent(TraceEventType.Error, 0, $"ServiceShutdownControl: Exception calling user-supplied callback: {ex4.Message}"); } SvcTrace.DiagDiagnostics.TraceEvent(TraceEventType.Information, 0, "ServiceShutdownControl: WaitForShutdown exited."); } public void Dispose() { Dispose(disposing: true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { if (!disposed) { SvcTrace.DiagDiagnostics.TraceEvent(TraceEventType.Stop, 0, "ServiceShutdownControl disposing"); disposed = true; } } }