Jak usunąć plik, który jest zablokowany przez inny proces w C#?
Szukam sposobu na usunięcie pliku, który jest zablokowany przez inny proces za pomocą C#. Podejrzewam, że metoda musi być w stanie znaleźć, który proces blokuje plik (być może śledząc uchwyty, chociaż nie jestem pewien, jak to zrobić w C#), a następnie zamknąć ten proces, zanim będzie w stanie zakończyć usuwanie pliku za pomocą File.Delete()
.
8 answers
Zabijanie innych procesów nie jest rzeczą zdrową. Jeśli twój scenariusz wiąże się z czymś takim jak Deinstalacja, możesz użyć MoveFileEx
funkcja API oznaczająca plik do usunięcia przy następnym restarcie.
Jeśli okaże się, że naprawdę musisz usunąć plik używany przez inny proces, zalecam ponowne rozważenie rzeczywistego problemu przed rozważeniem jakichkolwiek rozwiązań.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-08-22 07:15:09
Typowa metoda jest następująca. Powiedziałeś, że chcesz to zrobić w C# więc zaczynamy...
- Jeśli nie wiesz, który proces ma zablokowany plik, musisz sprawdzić listę obsługiwanych procesów i odpytywać każdy z nich, aby ustalić, czy identyfikuje on zablokowany plik. Robienie tego w C# będzie prawdopodobnie wymagało P / Invoke lub pośredniego C++ / CLI do wywołania natywnych API, których będziesz potrzebować.
- Gdy już zorientujesz się, które procesy mają zablokowany plik, będziesz musiał bezpiecznie wstrzyknąć mały natywny DLL do procesu (możesz również wstrzyknąć zarządzaną bibliotekę DLL, ale jest to messier, ponieważ musisz uruchomić lub dołączyć do środowiska uruchomieniowego. NET).
- Ten Bootstrap DLL następnie zamyka uchwyt za pomocą CloseHandle, itd.
Zasadniczo: sposobem odblokowania "zablokowanego" pliku jest wstrzyknięcie pliku DLL do przestrzeni adresowej procesu i zamknięcie go samodzielnie. Możesz to zrobić za pomocą kodu natywnego lub zarządzanego. Bez względu na wszystko, będziesz potrzebował niewielkiej ilości kodu natywnego lub na najmniej P / wywołaj do tego samego.
Pomocne linki:
Powodzenia!
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-08-31 22:41:04
Jeśli chcesz to zrobić programowo. Nie jestem pewien... i naprawdę polecam. Jeśli tylko rozwiązujesz problemy na własnej maszynie, SysInternals Process Explorer może Ci pomóc
Uruchom go, użyj polecenia Znajdź Uchwyt (myślę, że jest to albo w menu Znajdź lub uchwyt) i wyszukaj nazwę pliku. Po znalezieniu uchwytu(uchwytów) można je siłą zamknąć.
Możesz następnie usunąć plik i tak dalej.
Strzeż się , robiąc to może program, który posiada uchwyty, zachowuje się dziwnie, ponieważ właśnie wyciągnąłeś przysłowiowy dywan spod niego, ale działa dobrze, gdy debugujesz swój własny błędny kod, lub gdy visual studio / windows explorer jest gówniany i nie zwalnia uchwytów plików, mimo że kazałeś im zamknąć plik wieki temu... westchnienie: -)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-08-04 06:12:11
Możesz użyć tego programu Handle , aby dowiedzieć się, który proces ma blokadę w Twoim pliku. To narzędzie wiersza poleceń, więc domyślam się, że używasz wyjścia z tego... Nie jestem pewien, czy uda mi się go znaleźć programowo.
Jeśli usunięcie pliku może poczekać, możesz określić go do usunięcia przy następnym uruchomieniu komputera:
-
Uruchom
REGEDT32 (W2K)
lubREGEDIT (WXP)
i przejdź do:HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager
-
W2K i WXP
W2K:
Edytuj
Dodaj wartość...
typ danych:REG_MULTI_SZ
Nazwa wartości:PendingFileRenameOperations
OKWXP:
Edit
New
Multi-String Value
enterPendingFileRenameOperations
-
W obszarze danych wpisz
"\??\" + filename
do usunięcia. LFNs maj być wprowadzane bez osadzania w cudzysłowach. Aby usunąćC:\Long Directory Name\Long File Name.exe
, wprowadź następujące dane:\??\C:\Long Directory Name\Long File Name.exe
Następnie naciśnij OK .
-
"nazwa pliku docelowego" jest łańcuchem null (zero). Wpisuje się jak następuje:
W2K:
Edit
Binary
select Data Format: Hex
kliknij na końcu ciągu szesnastkowego
enter 0000 (cztery zera)
OKWXP:
kliknij prawym przyciskiem myszy wartość
wybierz "Modyfikuj dane binarne"
kliknij na końcu ciągu szesnastkowego
wprowadź 0000 (cztery zera)
OK
Zamknij
REGEDT32/REGEDIT
i uruchom ponownie, aby usunąć plik.
(bezwstydnie skradzione z jakiegoś przypadkowego forum, dla potomności.)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-11-30 16:13:42
Korzystając z porad Oriona Edwardsa pobrałem Sysinternals Process Explorer co z kolei pozwoliło mi odkryć, że plik, który miałem problemy z usunięciem, w rzeczywistości nie był utrzymywany przez obiekt Excel.Applications
, o którym myślałem, ale raczej fakt, że mój kod C# send mail code stworzył obiekt załącznika, który zostawił uchwyt do tego pliku otwarty.
Kiedy to zobaczyłem, dość prosto wywołałem metodę usuwania obiektu mocującego i uchwyt został zwolniony.
The Sysinternals explorer pozwolił mi odkryć to używane w połączeniu z debugerem Visual Studio 2005.
Gorąco polecam to narzędzie!
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-08-31 22:44:03
Oh, jeden wielki hack, którego użyłem lata temu, jest to, że Windows nie pozwala Ci usunąć pliki, ale pozwala Ciprzenieść je.
Pseudo-sort-of-code:
mv %WINDIR%\System32\mfc42.dll %WINDIR\System32\mfc42.dll.old
Install new mfc42.dll
Tell user to save work and restart applications
Po ponownym uruchomieniu aplikacji (zauważ, że nie musieliśmy ponownie uruchamiać maszyny), załadowali nową mfc42.dll
i wszystko było dobrze. To, w połączeniu z PendingFileOperations
usunięciem starego przy następnym ponownym uruchomieniu całego systemu, działało całkiem nieźle.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-09-20 20:52:11
To wygląda obiecująco. Sposób na zabicie obsługi plików....
Http://www.timstall.com/2009/02/killing-file-handles-but-not-process.html
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-01-15 14:25:53
Możesz użyć kodu, do którego podasz pełną ścieżkę do pliku, a zwróci List<Processes>
wszystkiego, co blokuje ten plik:
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;
}
}
Następnie iteruj listę procesów, zamknij je i usuń pliki:
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);
}
I w zależności od tego, czy plik znajduje się na komputerze lokalnym:
public static void localProcessKill(string processName)
{
foreach (Process p in Process.GetProcessesByName(processName))
{
p.Kill();
}
}
Lub komputer sieciowy:
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();
}
}
}
Linki:
Skąd mam wiedzieć, który proces blokuje plik za pomocą. NET?
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:17:58