using System; using System.IO; namespace Microsoft.AspNetCore.NodeServices { /// /// Makes it easier to pass script files to Node in a way that's sure to clean up after the process exits. /// public sealed class StringAsTempFile : IDisposable { private bool _disposedValue; private bool _hasDeletedTempFile; private object _fileDeletionLock = new object(); /// /// Create a new instance of . /// /// The contents of the temporary file to be created. public StringAsTempFile(string content) { FileName = Path.GetTempFileName(); File.WriteAllText(FileName, content); // Because .NET finalizers don't reliably run when the process is terminating, also // add event handlers for other shutdown scenarios. #if NET451 AppDomain.CurrentDomain.ProcessExit += HandleProcessExit; AppDomain.CurrentDomain.DomainUnload += HandleProcessExit; #else // Note that this still doesn't capture SIGKILL (at least on macOS) - there doesn't // appear to be a way of doing that. So in that case, the temporary file will be // left behind. System.Runtime.Loader.AssemblyLoadContext.Default.Unloading += HandleAssemblyUnloading; #endif } /// /// Specifies the filename of the temporary file. /// public string FileName { get; } /// /// Disposes the instance and deletes the associated temporary file. /// public void Dispose() { DisposeImpl(true); GC.SuppressFinalize(this); } private void DisposeImpl(bool disposing) { if (!_disposedValue) { if (disposing) { // Dispose managed state #if NET451 AppDomain.CurrentDomain.ProcessExit -= HandleProcessExit; AppDomain.CurrentDomain.DomainUnload -= HandleProcessExit; #else System.Runtime.Loader.AssemblyLoadContext.Default.Unloading -= HandleAssemblyUnloading; #endif } EnsureTempFileDeleted(); _disposedValue = true; } } private void EnsureTempFileDeleted() { lock (_fileDeletionLock) { if (!_hasDeletedTempFile) { File.Delete(FileName); _hasDeletedTempFile = true; } } } #if NET451 private void HandleProcessExit(object sender, EventArgs args) { EnsureTempFileDeleted(); } #else private void HandleAssemblyUnloading(System.Runtime.Loader.AssemblyLoadContext context) { EnsureTempFileDeleted(); } #endif /// /// Implements the finalization part of the IDisposable pattern by calling Dispose(false). /// ~StringAsTempFile() { DisposeImpl(false); } } }