Обнаружение удаления файла в открытом файле - PullRequest
4 голосов
/ 07 сентября 2010

Я открываю файл с доступом для чтения и разрешаю последующее чтение | запись | удаление доступа к файлу (доступ к файлу). Если файл был удален во время обработки, существует ли способ обнаружить, что файл ожидает удаления (см. Раздел «Файлы» http://msdn.microsoft.com/en-us/library/aa363858(v=VS.85).aspx)? Если какой-либо внешний процесс (процесс владения) выдал удаление, я хочу закрыть свой дескриптор как как можно скорее разрешить удаление файла, чтобы не мешать какой-либо логике в процессе владения.

Я нахожусь в C # и не вижу метода обнаружения ожидающего удаления. Файл был открыт с использованием объекта FileStream. Есть какой-то метод для обнаружения удаления в C # или в какой-либо другой функции Windows?

Ответы [ 5 ]

2 голосов
/ 12 февраля 2019

Вы можете использовать функцию Windows API GetFileInformationByHandleEx , чтобы обнаружить ожидающее удаление файла, который вы открыли. Второй аргумент - это значение перечисления, которое позволяет вам указать, какую информацию должна возвращать функция. Значение FileStandardInfo (1) заставит его возвращать структуру FILE_STANDARD_INFO , которая включает логическое значение DeletePending.

Вот демонстрационная утилита:

using System;
using System.Text;
using System.IO;
using System.Runtime.InteropServices;
using System.Threading;

internal static class Native
{
    [DllImport("kernel32.dll", SetLastError = true)]
    public extern static bool GetFileInformationByHandleEx(IntPtr  hFile,
                                                           int     FileInformationClass,
                                                           IntPtr  lpFileInformation,
                                                           uint    dwBufferSize);

    public struct FILE_STANDARD_INFO
    {
        public long AllocationSize;
        public long EndOfFile;
        public uint NumberOfLinks;
        public byte DeletePending;
        public byte Directory;
    }
    public const int FileStandardInfo = 1;
}

internal static class Program
{
    public static bool IsDeletePending(FileStream fs)
    {
        IntPtr buf = Marshal.AllocHGlobal(4096);
        try
        {
            IntPtr handle = fs.SafeFileHandle.DangerousGetHandle();
            if (!Native.GetFileInformationByHandleEx(handle,
                                                     Native.FileStandardInfo,
                                                     buf,
                                                     4096))
            {
                Exception ex = new Exception("GetFileInformationByHandleEx() failed");
                ex.Data["error"] = Marshal.GetLastWin32Error();
                throw ex;
            }
            else
            {
                Native.FILE_STANDARD_INFO info = Marshal.PtrToStructure<Native.FILE_STANDARD_INFO>(buf);
                return info.DeletePending != 0;
            }
        }
        finally
        {
            Marshal.FreeHGlobal(buf);
        }
    }

    public static int Main(string[] args)
    {
        TimeSpan MAX_WAIT_TIME = TimeSpan.FromSeconds(10);

        if (args.Length == 0)
        {
            args = new string[] { "deleteme.txt" };
        }

        for (int i = 0; i < args.Length; ++i)
        {
            string filename = args[i];
            FileStream fs = null;

            try
            {
                fs = File.Open(filename,
                               FileMode.CreateNew,
                               FileAccess.Write,
                               FileShare.ReadWrite | FileShare.Delete);

                byte[] buf = new byte[4096];
                UTF8Encoding utf8 = new UTF8Encoding(false);

                string text = "hello world!\r\n";
                int written = utf8.GetBytes(text, 0, text.Length, buf, 0);
                fs.Write(buf, 0, written);
                fs.Flush();

                Console.WriteLine("{0}: created and wrote line", filename);

                DateTime t0 = DateTime.UtcNow;
                for (;;)
                {
                    Thread.Sleep(16);
                    if (IsDeletePending(fs))
                    {
                        Console.WriteLine("{0}: detected pending delete", filename);
                        break;
                    }
                    if (DateTime.UtcNow - t0 > MAX_WAIT_TIME)
                    {
                        Console.WriteLine("{0}: timeout reached with no delete", filename);
                        break;
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine("{0}: {1}", filename, ex.Message);
            }
            finally
            {
                if (fs != null)
                {
                    Console.WriteLine("{0}: closing", filename);
                    fs.Dispose();
                }
            }
        }
        return 0;
    }
}
0 голосов
/ 08 сентября 2010

Нет, нет чистого способа сделать это.Если вы беспокоитесь о других процессах, открывающих и / или изменяющих файл, то вам могут помочь оплоки.Но если вы просто ищете уведомление о том, когда расположение удаления установлено как удаленное, не существует простого способа сделать это (без создания фильтра файловой системы, перехвата API-интерфейсов и т. Д., Что является пугающим для приложенияделаю без веской причины).

0 голосов
/ 08 сентября 2010

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

Единственное «решение» в этих пределах, о котором я могу подумать, это опрос о доступе к файлам и проверка исключения (если оно есть), которое вы получите.Возможно, есть что-то гораздо более сложное (на более низком уровне, чем файловый API win32?!?), Но это уже идет по «пути uhg»: -)

0 голосов
/ 08 сентября 2010

Если файл достаточно мал, ваше приложение может обработать копию файла, а не сам файл.Кроме того, если вашему приложению необходимо знать, удалил ли процесс владения исходный файл, установите для него FileSystemWatcher (FSW).Когда файл исчезает, FSW может установить флаг для прерывания обработки:

private bool _fileExists = true;

public void Process(string pathToOriginalFile, string pathToCopy)
{
    File.Copy(pathToOriginalFile, pathToCopy);

    FileSystemWatcher watcher = new FileSystemWatcher();
    watcher.Path = pathToOriginalFile;
    watcher.Deleted += new FileSystemEventHandler(OnFileDeleted);

    bool doneProcessing = false;
    watcher.EnableRaisingEvents = true;

    while(_fileExists && !doneProcessing)
    {
        // process the copy here
    }

    ...
}

private void OnFileDeleted(object source, FileSystemEventArgs e)
{
    _fileExists = false;
}
0 голосов
/ 07 сентября 2010

FileSystemWatcher, вероятно, будет самым близким, но он не может обнаружить «ожидающее» удаление;когда файл удален, в FileSystemWatcher будет инициировано событие, и вы можете прикрепить обработчик, который изящно прервет вашу обработку файла.Если блокировка (или ее отсутствие), открываемая вами при открытии файла, вообще позволяет удалить файл, простое закрытие доступного только для чтения FileStream, когда это происходит, не должно влиять на файловую систему.

Основные шаги наблюдателя файла - создать его, передав экземпляр объекта FileInfo в конструктор.FileInfos можно создать недорого, просто создав его экземпляр, передав ему путь и имя файла в виде строки.Затем установите для его параметра NotifyFilter типы изменений файловой системы, которые вы хотите отслеживать в этом файле.Наконец, присоедините обработчик событий вашего процесса к событию OnDeleted.Этот обработчик события, вероятно, может быть таким же простым, как установка где-нибудь битового флага, который может прочитать ваш основной процесс, и закрытие FileStream.Затем вы получите исключение при следующей попытке работать с потоком;поймайте его, прочитайте флаг, и если он установлен, просто изящно прекратите делать файлы.Вы также можете поместить обработку файла в отдельный рабочий поток, и обработчик событий может просто указать потоку, чтобы он умер, изящным методом.

...