시도/잡기 블록을 사용하지 않고 파일이 잠겼는지 확인할 수 있는 방법이 있나요?
현재 제가 아는 유일한 방법은 파일을 열고 System.IO.IOException
을 잡는 것뿐입니다.
문제에 직면했을 때, 나는 내가 비슷한 문제가 종료되고 다음 코드:
public bool IsFileLocked(string filePath)
{
try
{
using (File.Open(filePath, FileMode.Open)){}
}
catch (IOException e)
{
var errorCode = Marshal.GetHRForException(e) & ((1 << 16) - 1);
return errorCode == 32 || errorCode == 33;
}
return false;
}
얼마 전 것은 불가능하다는 안정적으로 목록을 가져올 수 있기 때문에 단순히 해당 정보를 추적되도록 프로세스활용 잠금식 파일을 Windows 않았다. [1], [재시작해야 관리자에서는 apiu] 지원할 수 있는 정보는 이제 추적됨. 다시 시작하는 관리자붃 API 를 사용할 수 있는 윈도우 비스타, 윈도우 서버 2008년 ([재시작해야 관리자: 런타임 요구사양에] [2]).
I put 함께 길을 코드 a ',' 필요한 모든 프로세스의 List<, 파일 및 되돌려줍니다 Process> 않는비즈니스 잠금식 해당 파일.
static public class FileUtil
{
[StructLayout(LayoutKind.Sequential)]
struct RM_UNIQUE_PROCESS
{
public int dwProcessId;
public System.Runtime.InteropServices.ComTypes.FILETIME ProcessStartTime;
}
const int RmRebootReasonNone = 0;
const int CCH_RM_MAX_APP_NAME = 255;
const int CCH_RM_MAX_SVC_NAME = 63;
enum RM_APP_TYPE
{
RmUnknownApp = 0,
RmMainWindow = 1,
RmOtherWindow = 2,
RmService = 3,
RmExplorer = 4,
RmConsole = 5,
RmCritical = 1000
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
struct RM_PROCESS_INFO
{
public RM_UNIQUE_PROCESS Process;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCH_RM_MAX_APP_NAME + 1)]
public string strAppName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCH_RM_MAX_SVC_NAME + 1)]
public string strServiceShortName;
public RM_APP_TYPE ApplicationType;
public uint AppStatus;
public uint TSSessionId;
[MarshalAs(UnmanagedType.Bool)]
public bool bRestartable;
}
[DllImport("rstrtmgr.dll", CharSet = CharSet.Unicode)]
static extern int RmRegisterResources(uint pSessionHandle,
UInt32 nFiles,
string[] rgsFilenames,
UInt32 nApplications,
[In] RM_UNIQUE_PROCESS[] rgApplications,
UInt32 nServices,
string[] rgsServiceNames);
[DllImport("rstrtmgr.dll", CharSet = CharSet.Auto)]
static extern int RmStartSession(out uint pSessionHandle, int dwSessionFlags, string strSessionKey);
[DllImport("rstrtmgr.dll")]
static extern int RmEndSession(uint pSessionHandle);
[DllImport("rstrtmgr.dll")]
static extern int RmGetList(uint dwSessionHandle,
out uint pnProcInfoNeeded,
ref uint pnProcInfo,
[In, Out] RM_PROCESS_INFO[] rgAffectedApps,
ref uint lpdwRebootReasons);
/// <summary>
/// Find out what process(es) have a lock on the specified file.
/// </summary>
/// <param name="path">Path of the file.</param>
/// <returns>Processes locking the file</returns>
/// <remarks>See also:
/// http://msdn.microsoft.com/en-us/library/windows/desktop/aa373661(v=vs.85).aspx
/// http://wyupdate.googlecode.com/svn-history/r401/trunk/frmFilesInUse.cs (no copyright in code at time of viewing)
///
/// </remarks>
static public List<Process> WhoIsLocking(string path)
{
uint handle;
string key = Guid.NewGuid().ToString();
List<Process> processes = new List<Process>();
int res = RmStartSession(out handle, 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 }; // Just checking on one resource.
res = RmRegisterResources(handle, (uint)resources.Length, resources, 0, null, 0, null);
if (res != 0)
throw new Exception("Could not register resource.");
//Note: there's a race condition here -- the first call to RmGetList() returns
// the total number of process. However, when we call RmGetList() again to get
// the actual processes this number may have increased.
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<Process>((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;
}
}
여기 다른 [협의를 샘플 코드] [3] 의 재시작해야 Manager 를 사용하는 방법을 제공한다.
[1]: 85%29.aspx http://msdn.microsoft.com/en-us/library/windows/desktop/aa373656%28v = vs. [2]: 이아스파스 https://msdn.microsoft.com/en-us/library/windows/desktop/cc948910 (v = vs. 85) [3]: https://blogs.msdn.microsoft.com/oldnewthing/20120217-00/? p = 8283
아니요, 안타깝게도 생각해 보면 파일이 바로 다음 순간에 잠길 수 있기 때문에(짧은 시간 동안) 해당 정보는 어차피 쓸모가 없습니다.
어쨌든 파일이 잠겼는지 여부를 구체적으로 알아야 하는 이유는 무엇일까요? 이를 알면 다른 방법으로 좋은 조언을 드릴 수 있습니다.
코드가 다음과 같다면
if not locked then
open and update file
그러면 두 줄 사이에 다른 프로세스가 파일을 쉽게 잠글 수 있으며, 애초에 피하려고 했던 것과 동일한 문제인 예외가 발생할 수 있습니다.
이 파일을 사용하는 경우 프로세스는 확인할 수 있습니다, 쇼 프로그램 바뀌엇어요 닫으십시오 등과 같은 설치자는 높여줍니까 계속 합니다.
public static string GetFileProcessName(string filePath)
{
Process[] procs = Process.GetProcesses();
string fileName = Path.GetFileName(filePath);
foreach (Process proc in procs)
{
if (proc.MainWindowHandle != new IntPtr(0) && !proc.HasExited)
{
ProcessModule[] arr = new ProcessModule[proc.Modules.Count];
foreach (ProcessModule pm in proc.Modules)
{
if (pm.ModuleName == fileName)
return proc.ProcessName;
}
}
}
return null;
}
닷넷 (.net) 기반 클래스 메서드를 사용하여 파일 및 잠금해제합니다 잠급니다 interop 대신 사용할 수 있습니다.
피레스트리아메드록 http://msdn.microsoft.com/en-us/library/system.io.filestream.lock.aspx
피레스트림.운록 http://msdn.microsoft.com/en-us/library/system.io.filestream.unlock.aspx
public static bool TryOpen(string path,
FileMode fileMode,
FileAccess fileAccess,
FileShare fileShare,
TimeSpan timeout,
out Stream stream)
{
var endTime = DateTime.Now + timeout;
while (DateTime.Now < endTime)
{
if (TryOpen(path, fileMode, fileAccess, fileShare, out stream))
return true;
}
stream = null;
return false;
}
public static bool TryOpen(string path,
FileMode fileMode,
FileAccess fileAccess,
FileShare fileShare,
out Stream stream)
{
try
{
stream = File.Open(path, fileMode, fileAccess, fileShare);
return true;
}
catch (IOException e)
{
if (!FileIsLocked(e))
throw;
stream = null;
return false;
}
}
private const uint HRFileLocked = 0x80070020;
private const uint HRPortionOfFileLocked = 0x80070021;
private static bool FileIsLocked(IOException ioException)
{
var errorCode = (uint)Marshal.GetHRForException(ioException);
return errorCode == HRFileLocked || errorCode == HRPortionOfFileLocked;
}
사용법:
private void Sample(string filePath)
{
Stream stream = null;
try
{
var timeOut = TimeSpan.FromSeconds(1);
if (!TryOpen(filePath,
FileMode.Open,
FileAccess.ReadWrite,
FileShare.ReadWrite,
timeOut,
out stream))
return;
// Use stream...
}
finally
{
if (stream != null)
stream.Close();
}
}
public bool IsFileLocked(string filePath, int secondsToWait)
{
bool isLocked = true;
int i = 0;
while (isLocked && ((i < secondsToWait) || (secondsToWait == 0)))
{
try
{
using (File.Open(filePath, FileMode.Open)) { }
return false;
}
catch (IOException e)
{
var errorCode = Marshal.GetHRForException(e) & ((1 << 16) - 1);
isLocked = errorCode == 32 || errorCode == 33;
i++;
if (secondsToWait !=0)
new System.Threading.ManualResetEvent(false).WaitOne(1000);
}
}
return isLocked;
}
if (!IsFileLocked(file, 10))
{
...
}
else
{
throw new Exception(...);
}
먼저 파일을 직접 읽거나 잠그면 파일이 잠겨 있는지 확인할 수 있습니다.
그러면 두 줄 사이에 다른 프로세스가 파일을 쉽게 잠글 수 있어 애초에 피하려고 했던 것과 동일한 문제인 예외가 발생할 수 있습니다.
하지만 이렇게 하면 문제가 일시적이라는 것을 알 수 있고 나중에 다시 시도할 수 있습니다. (예를 들어, 쓰기를 시도하는 동안 잠금이 발생하면 잠금이 해제될 때까지 계속 재시도하는 스레드를 작성할 수 있습니다.)
반면에 IOException은 그 자체로 잠금이 IO 실패의 원인이 될 만큼 구체적이지 않습니다. 일시적이지 않은 다른 이유가 있을 수 있습니다.
그러나 Powershell 같은 것입니다.
function Test-FileOpen
{
Param
([string]$FileToOpen)
try
{
$openFile =([system.io.file]::Open($FileToOpen,[system.io.filemode]::Open))
$open =$true
$openFile.close()
}
catch
{
$open = $false
}
$open
}
됐지 내가 할 수 있다.
internal void LoadExternalData() {
FileStream file;
if (TryOpenRead("filepath/filename", 5, out file)) {
using (file)
using (StreamReader reader = new StreamReader(file)) {
// do something
}
}
}
internal bool TryOpenRead(string path, int timeout, out FileStream file) {
bool isLocked = true;
bool condition = true;
do {
try {
file = File.OpenRead(path);
return true;
}
catch (IOException e) {
var errorCode = Marshal.GetHRForException(e) & ((1 << 16) - 1);
isLocked = errorCode == 32 || errorCode == 33;
condition = (isLocked && timeout > 0);
if (condition) {
// we only wait if the file is locked. If the exception is of any other type, there's no point on keep trying. just return false and null;
timeout--;
new System.Threading.ManualResetEvent(false).WaitOne(1000);
}
}
}
while (condition);
file = null;
return false;
}