#c_sharp
Есть папка в сети, в которой периодически создаются файлы. Есть необходимость копировать данные файлы в другой каталог. Нужно проверять, доступен ли файл для копирования, т.е. не дописывается ли он. Есть ли возможность проверить доступен ли файл для записи? Для примера, я открываю файл через Adobe Reader, данный файл не должен копироваться. Мне нужен ответ скорее на вопрос "Как узнать, открыт ли файл другой программой для изменения?"
Ответы
Ответ 1
Для этого можно использовать библиотеку со странным названием Restart Manager. Пример получения списка процессов, занявших файл (https://code.msdn.microsoft.com/windowsapps/How-to-know-the-process-704839f4): using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Text; using System.Windows.Forms; using System.Diagnostics; using System.Runtime.InteropServices; namespace RMTest { public partial class Form1 : Form { [DllImport("Rstrtmgr.dll", CharSet = CharSet.Unicode, PreserveSig = true, SetLastError = true, ExactSpelling = true)] public static extern UInt32 RmStartSession(out UInt32 pSessionHandle, UInt32 dwSessionFlags, string strSessionKey); [DllImport("Rstrtmgr.dll", CharSet = CharSet.Unicode, PreserveSig = true, SetLastError = true, ExactSpelling = true)] public static extern UInt32 RmRegisterResources(UInt32 dwSessionHandle, UInt32 nFiles,string[] rgsFilenames,UInt32 nApplications, UInt32 rgApplications,UInt32 nServices,UInt32 rgsServiceNames); [DllImport("Rstrtmgr.dll", CharSet = CharSet.Unicode, PreserveSig = true, SetLastError = true, ExactSpelling = true)] public static extern UInt32 RmGetList(UInt32 dwSessionHandle, out UInt32 pnProcInfoNeeded, ref UInt32 pnProcInfo,[In, Out] RM_PROCESS_INFO[] rgAffectedApps,ref UInt32 lpdwRebootReasons); [DllImport("Rstrtmgr.dll", CharSet = CharSet.Unicode, PreserveSig = true, SetLastError = true, ExactSpelling = true)] public static extern UInt32 RmEndSession(UInt32 dwSessionHandle); const UInt32 RmRebootReasonNone = 0x0; /*Получение списка процессов, имеющих блокировку на файле*/ static public ListGetLockProcesses(string path) { uint handle; string key = Guid.NewGuid().ToString(); List processes = new List (); uint res = RmStartSession(out handle, (uint)0, key); if (res != 0) { throw new Exception("Could not begin restart session. " + "Unable to determine file locker."); } try { const int ERROR_MORE_DATA = 234; uint pnProcInfoNeeded = 0, pnProcInfo = 0, lpdwRebootReasons = RmRebootReasonNone; string[] resources = new string[] { path }; res = RmRegisterResources(handle, (uint)resources.Length, resources, 0, 0, 0, 0); if (res != 0) { throw new Exception("Could not register resource."); } res = RmGetList(handle, out pnProcInfoNeeded, ref pnProcInfo, null, ref lpdwRebootReasons); if (res == ERROR_MORE_DATA) { // Create an array to store the process results. RM_PROCESS_INFO[] processInfo = new RM_PROCESS_INFO[pnProcInfoNeeded]; pnProcInfo = pnProcInfoNeeded; // Get the list. res = RmGetList(handle, out pnProcInfoNeeded, ref pnProcInfo, processInfo, ref lpdwRebootReasons); if (res == 0) { processes = new List ((int)pnProcInfo); // Enumerate all of the results and add them to the // list to be returned. for (int i = 0; i < pnProcInfo; i++) { try { processes.Add(Process.GetProcessById(processInfo[i]. Process.dwProcessId)); } // Catch the error in case the process is no longer running. catch (ArgumentException) { } } } else { throw new Exception("Could not list processes locking resource"); } } else if (res != 0) { throw new Exception("Could not list processes locking resource." + "Failed to get size of result."); } } finally { RmEndSession(handle); } return processes; } public Form1() { InitializeComponent(); string f = "C:\\some_file.pdf"; var p = GetLockProcesses(f); textBox1.Text = ""; foreach (Process proc in p) { textBox1.Text += proc.ProcessName + Environment.NewLine; } ; } } [StructLayout(LayoutKind.Sequential)] public struct RM_UNIQUE_PROCESS { // The product identifier (PID). public int dwProcessId; // The creation time of the process. public System.Runtime.InteropServices.ComTypes.FILETIME ProcessStartTime; } /// /// Describes an application that is to be registered with the Restart Manager. /// [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] public struct RM_PROCESS_INFO { const int CCH_RM_MAX_APP_NAME = 255; const int CCH_RM_MAX_SVC_NAME = 63; // Contains an RM_UNIQUE_PROCESS structure that uniquely identifies the // application by its PID and the time the process began. public RM_UNIQUE_PROCESS Process; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCH_RM_MAX_APP_NAME + 1)] // If the process is a service, this parameter returns the // long name for the service. public string strAppName; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCH_RM_MAX_SVC_NAME + 1)] // If the process is a service, this is the short name for the service. public string strServiceShortName; // Contains an RM_APP_TYPE enumeration value. public RM_APP_TYPE ApplicationType; // Contains a bit mask that describes the current status of the application. public uint AppStatus; // Contains the Terminal Services session ID of the process. public uint TSSessionId; // TRUE if the application can be restarted by the // Restart Manager; otherwise, FALSE. [MarshalAs(UnmanagedType.Bool)] public bool bRestartable; } ////// Specifies the type of application that is described by /// the RM_PROCESS_INFO structure. /// public enum RM_APP_TYPE { // The application cannot be classified as any other type. RmUnknownApp = 0, // A Windows application run as a stand-alone process that // displays a top-level window. RmMainWindow = 1, // A Windows application that does not run as a stand-alone // process and does not display a top-level window. RmOtherWindow = 2, // The application is a Windows service. RmService = 3, // The application is Windows Explorer. RmExplorer = 4, // The application is a stand-alone console application. RmConsole = 5, // A system restart is required to complete the installation because // a process cannot be shut down. RmCritical = 1000 } } Работает на ОС начиная с Windows Vista.Ответ 2
Наиболее трезвый, на мой взгляд, способ понять занят ли файл другим процессом - это попытаться скопировать его и получить System.IO.IOException в случае ошибки ввода-вывода, которая как раз таки и будет возникать при отсутствии к нему доступа. Попытки реализовать метод что бы проверить блокирован файл или нет особого результата не дадут. Поскольку никто не гарантирует, что в промежутке между вызовом метода проверки на блокировку и копированием файла ни какой другой процесс его уже не занял.Ответ 3
Вот готовый проект. Все делается через системный openfiles.exe который находится в c:\Windows\System32 Он поддерживает множество операций таких как закрытие файла, запросы и т.д.Ответ 4
Если Вы хотите получить качественную копию файлов в другой папке, то вам надо изменить алгоритм. Вы можете потерять данные в процессе копирования, вы можете запросить состояние файла (теоретически) одновременно с процессом который этот файл начнет открывать и если тот процесс локальный, то он заблокирует файл, который вы считаете свободным. Правильный подход в этой задаче - просто попытаться сразу скопировать все, а после завершения копирования проверить результат. Например по длине файлов или по времени их модификации до копирования и после. Файлы, которые не прошли проверку скопировать повторно. Ну и далее - проанализировать и обработать возникшие ошибки.
Комментариев нет:
Отправить комментарий