C#의 다른 프로세스에 의해 잠긴 파일을 어떻게 삭제합니까?
-
08-06-2019 - |
문제
C#을 사용하여 다른 프로세스에 의해 잠긴 파일을 삭제하는 방법을 찾고 있습니다.나는 이 방법이 어떤 프로세스가 파일을 잠그고 있는지 찾을 수 있어야 한다고 생각합니다. (C#에서 이 작업을 수행하는 방법을 잘 모르겠지만 핸들을 추적하여) 다음을 사용하여 파일 삭제를 완료하기 전에 해당 프로세스를 닫습니다. File.Delete()
.
해결책
다른 프로세스를 종료하는 것은 건전한 행동이 아닙니다.시나리오에 제거와 같은 내용이 포함된 경우 다음을 사용할 수 있습니다. MoveFileEx
API 기능 다음 재부팅 시 파일을 삭제하도록 표시합니다.
다른 프로세스에서 사용 중인 파일을 꼭 삭제해야 하는 경우 솔루션을 고려하기 전에 실제 문제를 다시 고려하는 것이 좋습니다.
다른 팁
대표적인 방법은 다음과 같습니다.C#에서 이 작업을 수행하고 싶다고 말씀하셨으니 다음과 같습니다.
- 어떤 프로세스가 파일을 잠갔는지 모르는 경우 각 프로세스의 핸들 목록을 검사하고 각 핸들을 쿼리하여 잠긴 파일을 식별하는지 확인해야 합니다.C#에서 이 작업을 수행하려면 필요한 기본 API를 호출하기 위해 P/Invoke 또는 중간 C++/CLI가 필요할 수 있습니다.
- 어떤 프로세스에 파일이 잠겨 있는지 파악한 후에는 작은 기본 DLL을 프로세스에 안전하게 주입해야 합니다(관리되는 DLL을 주입할 수도 있지만 이는 더 복잡합니다. 그런 다음 시작해야 하기 때문입니다). 또는 .NET 런타임에 연결).
- 그런 다음 해당 부트스트랩 DLL은 CloseHandle 등을 사용하여 핸들을 닫습니다.
기본적으로:"잠긴" 파일을 잠금 해제하는 방법은 문제가 있는 프로세스의 주소 공간에 DLL 파일을 삽입하고 직접 닫는 것입니다.네이티브 코드나 관리 코드를 사용하여 이 작업을 수행할 수 있습니다.무슨 일이 있어도 소량의 네이티브 코드나 최소한 P/Invoke가 필요합니다.
유용한 링크:
행운을 빌어요!
프로그래밍 방식으로 수행하려는 경우.잘 모르겠어요...그리고 나는 그것에 반대하는 것을 정말로 추천하고 싶습니다.자신의 컴퓨터에서 문제를 해결하는 중이라면, SysInternals 프로세스 탐색기 당신을 도울 수 있습니다
실행하고 핸들 찾기 명령(찾기 또는 핸들 메뉴에 있는 것 같습니다)을 사용하고 파일 이름을 검색하세요.핸들을 찾으면 강제로 닫을 수 있습니다.
그런 다음 파일 등을 삭제할 수 있습니다.
조심하세요, 이렇게 하면 핸들을 소유한 프로그램이 이상하게 동작할 수 있습니다. 방금 그 아래에서 속담의 깔개를 꺼냈기 때문입니다. 그러나 잘못된 코드를 디버깅할 때나 Visual Studio/Windows 탐색기가 오래 전에 파일을 닫으라고 말했는데도 파일 핸들을 공개하지 않는 쓰레기입니다 ...한숨을 쉬다 :-)
이 프로그램을 사용하시면 됩니다, 핸들, 을 클릭하여 파일에 잠금이 설정된 프로세스를 찾으세요.명령줄 도구이므로 그 출력을 사용하는 것 같습니다.프로그래밍 방식으로 찾는 것이 확실하지 않습니다.
파일 삭제가 기다릴 수 있는 경우 다음에 컴퓨터가 시작될 때 삭제되도록 지정할 수 있습니다.
시작
REGEDT32 (W2K)
또는REGEDIT (WXP)
다음으로 이동합니다.HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager
W2K 및 WXP
주2천:
편집하다
값을 추가...
데이터 형식:REG_MULTI_SZ
값 이름:PendingFileRenameOperations
좋아요경험치:
편집하다
새로운
다중 문자열 값
입력하다
PendingFileRenameOperations
데이터 영역에 다음을 입력합니다.
"\??\" + filename
삭제됩니다.LFN은 견적에 포함되지 않고 입력 할 수 있습니다.지우는 것C:\Long Directory Name\Long File Name.exe
, 다음 데이터를 입력합니다:\??\C:\Long Directory Name\Long File Name.exe
그런 다음 좋아요.
"대상 파일 이름"은 null(영) 문자열입니다.다음과 같이 입력됩니다.
주2천:
편집하다
바이너리
데이터 형식 선택:마녀
16진수 문자열 끝을 클릭하세요.
0000(0 4개)을 입력하세요.
좋아요경험치:
값을 마우스 오른쪽 버튼으로 클릭하세요.
"바이너리 데이터 수정"을 선택하십시오.
16진수 문자열 끝을 클릭하세요.
0000(0 4개)을 입력하세요.
좋아요
닫다
REGEDT32/REGEDIT
파일을 삭제하려면 재부팅하세요.
(뻔뻔하게도 훔친 임의의 포럼, 후손을 위해.)
Orion Edwards의 조언을 사용하여 Sysinternals를 다운로드했습니다. 프로세스 탐색기 그 결과 제가 삭제하는 데 어려움을 겪고 있던 파일이 실제로는 해당 파일이 아닌 다른 사람이 보관하고 있다는 사실을 알게 되었습니다. Excel.Applications
내 생각에는 내 C# 코드 전송 메일 코드가 이 파일에 대한 핸들을 열어 둔 첨부 파일 개체를 생성했다는 사실입니다.
이것을 본 후에는 Attachment 개체의 dispose 메서드를 아주 간단하게 호출하고 핸들을 해제했습니다.
Sysinternals 탐색기를 통해 Visual Studio 2005 디버거와 함께 사용되는 이 기능을 발견할 수 있었습니다.
이 도구를 적극 추천합니다!
아, 제가 몇 년 전에 사용한 큰 해킹 중 하나는 Windows가 허용하지 않는다는 것입니다. 삭제 파일을 다운로드할 수 있지만 그렇게 하면 이동하다 그들을.
의사 정렬 코드:
mv %WINDIR%\System32\mfc42.dll %WINDIR\System32\mfc42.dll.old
Install new mfc42.dll
Tell user to save work and restart applications
애플리케이션이 다시 시작되었을 때(머신을 재부팅할 필요가 없었음에 유의) 새 애플리케이션을 로드했습니다. mfc42.dll
, 모든 것이 잘되었습니다.그와 결합하여 PendingFileOperations
다음에 전체 시스템을 다시 시작할 때 이전 것을 삭제하는 것은 꽤 잘 작동했습니다.
이것은 유망 해 보입니다.파일 핸들을 죽이는 방법....
http://www.timstall.com/2009/02/killing-file-handles-but-not-process.html
전체 파일 경로를 제공하는 코드를 사용할 수 있으며 List<Processes>
해당 파일을 잠그는 모든 것:
using System.Runtime.InteropServices;
using System.Diagnostics;
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;
}
}
그런 다음 프로세스 목록을 반복하고 닫은 후 파일을 삭제합니다.
string[] files = Directory.GetFiles(target_dir);
List<Process> lstProcs = new List<Process>();
foreach (string file in files)
{
lstProcs = ProcessHandler.WhoIsLocking(file);
if (lstProcs.Count > 0) // deal with the file lock
{
foreach (Process p in lstProcs)
{
if (p.MachineName == ".")
ProcessHandler.localProcessKill(p.ProcessName);
else
ProcessHandler.remoteProcessKill(p.MachineName, txtUserName.Text, txtPassword.Password, p.ProcessName);
}
File.Delete(file);
}
else
File.Delete(file);
}
파일이 로컬 컴퓨터에 있는지 여부에 따라 다음과 같습니다.
public static void localProcessKill(string processName)
{
foreach (Process p in Process.GetProcessesByName(processName))
{
p.Kill();
}
}
또는 네트워크 컴퓨터:
public static void remoteProcessKill(string computerName, string fullUserName, string pword, string processName)
{
var connectoptions = new ConnectionOptions();
connectoptions.Username = fullUserName; // @"YourDomainName\UserName";
connectoptions.Password = pword;
ManagementScope scope = new ManagementScope(@"\\" + computerName + @"\root\cimv2", connectoptions);
// WMI query
var query = new SelectQuery("select * from Win32_process where name = '" + processName + "'");
using (var searcher = new ManagementObjectSearcher(scope, query))
{
foreach (ManagementObject process in searcher.Get())
{
process.InvokeMethod("Terminate", null);
process.Dispose();
}
}
}