// Copyright (c) .NET Foundation and contributors. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System; using System.IO; using System.Runtime.InteropServices; using Microsoft.Win32.SafeHandles; namespace Microsoft.DotNet.Tools.Test.Utilities { public sealed class DisposableFile : TempFile, IDisposable { public DisposableFile(string path) : base(path) { } public DisposableFile(string prefix = null, string extension = null, string directory = null, string callerSourcePath = null, int callerLineNumber = 0) : base(prefix, extension, directory, callerSourcePath, callerLineNumber) { } public void Dispose() { if (Path != null && File.Exists(Path)) { try { File.Delete(Path); } catch (UnauthorizedAccessException) { try { // the file might still be memory-mapped, delete on close: DeleteFileOnClose(Path); } catch (IOException ex) { throw new InvalidOperationException(string.Format(@" The file '{0}' seems to have been opened in a way that prevents us from deleting it on close. Is the file loaded as an assembly (e.g. via Assembly.LoadFile)? {1}: {2}", Path, ex.GetType().Name, ex.Message), ex); } catch (UnauthorizedAccessException) { // We should ignore this exception if we got it the second time, // the most important reason is that the file has already been // scheduled for deletion and will be deleted when all handles // are closed. } } } } [DllImport("kernel32.dll", PreserveSig = false)] private static extern void SetFileInformationByHandle(SafeFileHandle handle, int fileInformationClass, ref uint fileDispositionInfoDeleteFile, int bufferSize); private const int FileDispositionInfo = 4; internal static void PrepareDeleteOnCloseStreamForDisposal(FileStream stream) { // tomat: Set disposition to "delete" on the stream, so to avoid ForeFront EndPoint // Protection driver scanning the file. Note that after calling this on a file that's open with DeleteOnClose, // the file can't be opened again, not even by the same process. uint trueValue = 1; SetFileInformationByHandle(stream.SafeFileHandle, FileDispositionInfo, ref trueValue, sizeof(uint)); } /// /// Marks given file for automatic deletion when all its handles are closed. /// Note that after doing this the file can't be opened again, not even by the same process. /// internal static void DeleteFileOnClose(string fullPath) { using (var stream = new FileStream(fullPath, FileMode.Open, FileAccess.ReadWrite, FileShare.Delete | FileShare.ReadWrite, 8, FileOptions.DeleteOnClose)) { PrepareDeleteOnCloseStreamForDisposal(stream); } } } }