I'm looking for a way to delete a file which is locked by another process using C#. I suspect the method must be able to find which process is locking the file (perhaps by tracking the handles, although I'm not sure how to do this in C#) then close that process before being able to complete the file delete using File.Delete()
.
我正在寻找一种方法来删除使用C#被另一个进程锁定的文件。我怀疑该方法必须能够找到锁定文件的进程(可能是通过跟踪句柄,虽然我不知道如何在C#中执行此操作)然后关闭该进程,然后才能使用File完成文件删除。删除()。
8 个解决方案
#1
34
Killing other processes is not a healthy thing to do. If your scenario involves something like uninstallation, you could use the MoveFileEx
API function to mark the file for deletion upon next reboot.
杀死其他进程并不是一件好事。如果您的方案涉及卸载等操作,则可以使用MoveFileEx API函数在下次重新引导时将文件标记为删除。
If it appears that you really need to delete a file in use by another process, I'd recommend re-considering the actual problem before considering any solutions.
如果您确实需要删除其他进程正在使用的文件,我建议您在考虑任何解决方案之前重新考虑实际问题。
#2
15
The typical method is as follows. You've said you want to do this in C# so here goes...
典型的方法如下。你说你想用C#做这个,所以这里......
- If you don't know which process has the file locked, you'll need to examine each process's handle list, and query each handle to determine if it identifies the locked file. Doing this in C# will likely require P/Invoke or an intermediary C++/CLI to call the native APIs you'll need.
- Once you've figured out which process(es) have the file locked, you'll need to safely inject a small native DLL into the process (you can also inject a managed DLL, but this is messier, as you then have to start or attach to the .NET runtime).
- That bootstrap DLL then closes the handle using CloseHandle, etc.
如果您不知道哪个进程锁定了文件,则需要检查每个进程的句柄列表,并查询每个句柄以确定它是否标识了锁定的文件。在C#中执行此操作可能需要P / Invoke或中间C ++ / CLI来调用您需要的本机API。
一旦你弄清楚哪个进程锁定了文件,你就需要安全地在进程中注入一个小的本机DLL(你也可以注入一个托管的DLL,但是这很麻烦,因为你必须开始或附加到.NET运行时)。
然后,该引导DLL使用CloseHandle等关闭句柄。
Essentially: the way to unlock a "locked" file is to inject a DLL file into the offending process's address space and close it yourself. You can do this using native or managed code. No matter what, you're going to need a small amount of native code or at least P/Invoke into the same.
基本上:解锁“锁定”文件的方法是将DLL文件注入违规进程的地址空间并自行关闭。您可以使用本机代码或托管代码执行此操作。无论如何,你将需要少量的本机代码或至少P / Invoke。
Helpful links:
- Three Ways to Inject Your Code into Another Process
- .NET Code Injection
将代码注入另一个进程的三种方法
.NET代码注入
Good luck!
#3
7
If you want to do it programatically. I'm not sure... and I'd really recommend against it. If you're just troubleshooting stuff on your own machine, SysInternals Process Explorer can help you
如果你想以编程方式进行。我不确定......我真的建议反对它。如果您只是在自己的机器上进行故障排除,SysInternals Process Explorer可以为您提供帮助
Run it, use the Find Handle command (I think it's either in the find or handle menu), and search for the name of your file. Once the handle(s) is found, you can forcibly close them.
运行它,使用Find Handle命令(我认为它位于查找或句柄菜单中),并搜索文件的名称。找到手柄后,您可以强行关闭它们。
You can then delete the file and so on.
然后,您可以删除该文件,依此类推。
Beware, doing this may cause the program which owns the handles to behave strangely, as you've just pulled the proverbial rug out from under it, but it works well when you are debugging your own errant code, or when visual studio / windows explorer is being crap and not releasing file handles even though you told them to close the file ages ago... sigh :-)
请注意,这样做可能会导致拥有句柄的程序表现得很奇怪,因为你只是从它下面拉出了众所周知的地毯,但是当你调试自己的错误代码时,或者当visual studio / windows explorer时,它运行良好即使你告诉他们在很久以前关闭文件,这是废话并且不释放文件句柄...叹气:-)
#4
4
You can use this program, Handle, to find which process has the lock on your file. It's a command-line tool, so I guess you use the output from that... I'm not sure about finding it programmatically.
您可以使用此程序Handle查找哪个进程锁定了您的文件。它是一个命令行工具,所以我猜你使用的输出......我不确定是否以编程方式找到它。
If deleting the file can wait, you could specify it for deletion when your computer next starts up:
如果删除文件可以等待,您可以在计算机下次启动时将其指定为删除:
-
Start
REGEDT32 (W2K)
orREGEDIT (WXP)
and navigate to:启动REGEDT32(W2K)或REGEDIT(WXP)并导航至:
HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager
-
W2K and WXP
W2K和WXP
-
W2K:
Edit
Add Value...
Data Type:REG_MULTI_SZ
Value Name:PendingFileRenameOperations
OKW2K:编辑添加值...数据类型:REG_MULTI_SZ值名称:PendingFileRenameOperations OK
-
WXP:
Edit
New
Multi-String Value
enterPendingFileRenameOperations
WXP:编辑新的多字符串值输入PendingFileRenameOperations
-
-
In the Data area, enter
"\??\" + filename
to be deleted. LFNs may be entered without being embedded in quotes. To deleteC:\Long Directory Name\Long File Name.exe
, enter the following data:在“数据”区域中,输入要删除的“\ ?? \”+文件名。可以在不嵌入引号的情况下输入LFN。要删除C:\ Long Directory Name \ Long File Name.exe,请输入以下数据:
\??\C:\Long Directory Name\Long File Name.exe
Then press OK.
然后按OK。
-
The "destination file name" is a null (zero) string. It is entered as follows:
“目标文件名”是空(零)字符串。输入如下:
-
W2K:
Edit
Binary
select Data Format: Hex
click at the end of the hex string
enter 0000 (four zeros)
OKW2K:编辑二进制选择数据格式:在十六进制字符串末尾点击十六进制输入0000(四个零)OK
-
WXP:
Right-click the value
choose "Modify Binary Data"
click at the end of the hex string
enter 0000 (four zeros)
OKWXP:右键单击值选择“修改二进制数据”,单击十六进制字符串末尾输入0000(四个零)OK
-
-
Close
REGEDT32/REGEDIT
and reboot to delete the file.关闭REGEDT32 / REGEDIT并重新启动以删除该文件。
(Shamelessly stolen from some random forum, for posterity's sake.)
(为了后人的缘故,从一些随机的论坛无耻地偷走了。)
#5
4
Using Orion Edwards advice I downloaded the Sysinternals Process Explorer which in turn allowed me to discover that the file I was having difficulties deleting was in fact being held not by the Excel.Applications
object I thought, but rather the fact that my C# code send mail code had created an Attachment object that left a handle to this file open.
使用Orion Edwards建议我下载了Sysinternals Process Explorer,这反过来让我发现我删除有困难的文件实际上不是由我认为的Excel.Applications对象所持有,而是我的C#代码发送邮件的事实代码创建了一个Attachment对象,该对象将打开此文件的句柄。
Once I saw this, I quite simple called on the dispose method of the Attachment object, and the handle was released.
一旦我看到这个,我就很简单地调用了Attachment对象的dispose方法,并释放了句柄。
The Sysinternals explorer allowed me to discover this used in conjunction with the Visual Studio 2005 debugger.
Sysinternals资源管理器允许我发现它与Visual Studio 2005调试器一起使用。
I highly recommend this tool!
我强烈推荐这个工具!
#6
3
Oh, one big hack I employed years ago, is that Windows won't let you delete files, but it does let you move them.
哦,我多年前聘请的一个大黑客,是Windows不会让你删除文件,但它确实让你移动它们。
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
When the applications restarted (note we didn't need to reboot the machine), they loaded the new mfc42.dll
, and all was well. That, coupled with PendingFileOperations
to delete the old one the next time the whole system restarted, worked pretty well.
当应用程序重新启动时(注意我们不需要重启机器),他们加载了新的mfc42.dll,一切都很顺利。再加上PendingFileOperations在下次整个系统重新启动时删除旧的,工作得很好。
#7
3
This looks promising. A way of killing the file handle....
这很有希望。一种杀死文件句柄的方法....
http://www.timstall.com/2009/02/killing-file-handles-but-not-process.html
#8
3
You can use code that you supply the full file path to, and it will return a List<Processes>
of anything locking that file:
您可以使用提供完整文件路径的代码,它将返回锁定该文件的任何内容的List
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;
}
}
Then, iterate the list of processes and close them and delete the files:
然后,迭代进程列表并关闭它们并删除文件:
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);
}
And depending on if the file is on the local computer:
并且取决于文件是否在本地计算机上:
public static void localProcessKill(string processName)
{
foreach (Process p in Process.GetProcessesByName(processName))
{
p.Kill();
}
}
or a network computer:
或网络计算机:
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();
}
}
}
References:
How do I find out which process is locking a file using .NET?
参考:如何找出使用.NET锁定文件的进程?
Delete a directory where someone has opened a file
删除某人打开文件的目录
#1
34
Killing other processes is not a healthy thing to do. If your scenario involves something like uninstallation, you could use the MoveFileEx
API function to mark the file for deletion upon next reboot.
杀死其他进程并不是一件好事。如果您的方案涉及卸载等操作,则可以使用MoveFileEx API函数在下次重新引导时将文件标记为删除。
If it appears that you really need to delete a file in use by another process, I'd recommend re-considering the actual problem before considering any solutions.
如果您确实需要删除其他进程正在使用的文件,我建议您在考虑任何解决方案之前重新考虑实际问题。
#2
15
The typical method is as follows. You've said you want to do this in C# so here goes...
典型的方法如下。你说你想用C#做这个,所以这里......
- If you don't know which process has the file locked, you'll need to examine each process's handle list, and query each handle to determine if it identifies the locked file. Doing this in C# will likely require P/Invoke or an intermediary C++/CLI to call the native APIs you'll need.
- Once you've figured out which process(es) have the file locked, you'll need to safely inject a small native DLL into the process (you can also inject a managed DLL, but this is messier, as you then have to start or attach to the .NET runtime).
- That bootstrap DLL then closes the handle using CloseHandle, etc.
如果您不知道哪个进程锁定了文件,则需要检查每个进程的句柄列表,并查询每个句柄以确定它是否标识了锁定的文件。在C#中执行此操作可能需要P / Invoke或中间C ++ / CLI来调用您需要的本机API。
一旦你弄清楚哪个进程锁定了文件,你就需要安全地在进程中注入一个小的本机DLL(你也可以注入一个托管的DLL,但是这很麻烦,因为你必须开始或附加到.NET运行时)。
然后,该引导DLL使用CloseHandle等关闭句柄。
Essentially: the way to unlock a "locked" file is to inject a DLL file into the offending process's address space and close it yourself. You can do this using native or managed code. No matter what, you're going to need a small amount of native code or at least P/Invoke into the same.
基本上:解锁“锁定”文件的方法是将DLL文件注入违规进程的地址空间并自行关闭。您可以使用本机代码或托管代码执行此操作。无论如何,你将需要少量的本机代码或至少P / Invoke。
Helpful links:
- Three Ways to Inject Your Code into Another Process
- .NET Code Injection
将代码注入另一个进程的三种方法
.NET代码注入
Good luck!
#3
7
If you want to do it programatically. I'm not sure... and I'd really recommend against it. If you're just troubleshooting stuff on your own machine, SysInternals Process Explorer can help you
如果你想以编程方式进行。我不确定......我真的建议反对它。如果您只是在自己的机器上进行故障排除,SysInternals Process Explorer可以为您提供帮助
Run it, use the Find Handle command (I think it's either in the find or handle menu), and search for the name of your file. Once the handle(s) is found, you can forcibly close them.
运行它,使用Find Handle命令(我认为它位于查找或句柄菜单中),并搜索文件的名称。找到手柄后,您可以强行关闭它们。
You can then delete the file and so on.
然后,您可以删除该文件,依此类推。
Beware, doing this may cause the program which owns the handles to behave strangely, as you've just pulled the proverbial rug out from under it, but it works well when you are debugging your own errant code, or when visual studio / windows explorer is being crap and not releasing file handles even though you told them to close the file ages ago... sigh :-)
请注意,这样做可能会导致拥有句柄的程序表现得很奇怪,因为你只是从它下面拉出了众所周知的地毯,但是当你调试自己的错误代码时,或者当visual studio / windows explorer时,它运行良好即使你告诉他们在很久以前关闭文件,这是废话并且不释放文件句柄...叹气:-)
#4
4
You can use this program, Handle, to find which process has the lock on your file. It's a command-line tool, so I guess you use the output from that... I'm not sure about finding it programmatically.
您可以使用此程序Handle查找哪个进程锁定了您的文件。它是一个命令行工具,所以我猜你使用的输出......我不确定是否以编程方式找到它。
If deleting the file can wait, you could specify it for deletion when your computer next starts up:
如果删除文件可以等待,您可以在计算机下次启动时将其指定为删除:
-
Start
REGEDT32 (W2K)
orREGEDIT (WXP)
and navigate to:启动REGEDT32(W2K)或REGEDIT(WXP)并导航至:
HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager
-
W2K and WXP
W2K和WXP
-
W2K:
Edit
Add Value...
Data Type:REG_MULTI_SZ
Value Name:PendingFileRenameOperations
OKW2K:编辑添加值...数据类型:REG_MULTI_SZ值名称:PendingFileRenameOperations OK
-
WXP:
Edit
New
Multi-String Value
enterPendingFileRenameOperations
WXP:编辑新的多字符串值输入PendingFileRenameOperations
-
-
In the Data area, enter
"\??\" + filename
to be deleted. LFNs may be entered without being embedded in quotes. To deleteC:\Long Directory Name\Long File Name.exe
, enter the following data:在“数据”区域中,输入要删除的“\ ?? \”+文件名。可以在不嵌入引号的情况下输入LFN。要删除C:\ Long Directory Name \ Long File Name.exe,请输入以下数据:
\??\C:\Long Directory Name\Long File Name.exe
Then press OK.
然后按OK。
-
The "destination file name" is a null (zero) string. It is entered as follows:
“目标文件名”是空(零)字符串。输入如下:
-
W2K:
Edit
Binary
select Data Format: Hex
click at the end of the hex string
enter 0000 (four zeros)
OKW2K:编辑二进制选择数据格式:在十六进制字符串末尾点击十六进制输入0000(四个零)OK
-
WXP:
Right-click the value
choose "Modify Binary Data"
click at the end of the hex string
enter 0000 (four zeros)
OKWXP:右键单击值选择“修改二进制数据”,单击十六进制字符串末尾输入0000(四个零)OK
-
-
Close
REGEDT32/REGEDIT
and reboot to delete the file.关闭REGEDT32 / REGEDIT并重新启动以删除该文件。
(Shamelessly stolen from some random forum, for posterity's sake.)
(为了后人的缘故,从一些随机的论坛无耻地偷走了。)
#5
4
Using Orion Edwards advice I downloaded the Sysinternals Process Explorer which in turn allowed me to discover that the file I was having difficulties deleting was in fact being held not by the Excel.Applications
object I thought, but rather the fact that my C# code send mail code had created an Attachment object that left a handle to this file open.
使用Orion Edwards建议我下载了Sysinternals Process Explorer,这反过来让我发现我删除有困难的文件实际上不是由我认为的Excel.Applications对象所持有,而是我的C#代码发送邮件的事实代码创建了一个Attachment对象,该对象将打开此文件的句柄。
Once I saw this, I quite simple called on the dispose method of the Attachment object, and the handle was released.
一旦我看到这个,我就很简单地调用了Attachment对象的dispose方法,并释放了句柄。
The Sysinternals explorer allowed me to discover this used in conjunction with the Visual Studio 2005 debugger.
Sysinternals资源管理器允许我发现它与Visual Studio 2005调试器一起使用。
I highly recommend this tool!
我强烈推荐这个工具!
#6
3
Oh, one big hack I employed years ago, is that Windows won't let you delete files, but it does let you move them.
哦,我多年前聘请的一个大黑客,是Windows不会让你删除文件,但它确实让你移动它们。
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
When the applications restarted (note we didn't need to reboot the machine), they loaded the new mfc42.dll
, and all was well. That, coupled with PendingFileOperations
to delete the old one the next time the whole system restarted, worked pretty well.
当应用程序重新启动时(注意我们不需要重启机器),他们加载了新的mfc42.dll,一切都很顺利。再加上PendingFileOperations在下次整个系统重新启动时删除旧的,工作得很好。
#7
3
This looks promising. A way of killing the file handle....
这很有希望。一种杀死文件句柄的方法....
http://www.timstall.com/2009/02/killing-file-handles-but-not-process.html
#8
3
You can use code that you supply the full file path to, and it will return a List<Processes>
of anything locking that file:
您可以使用提供完整文件路径的代码,它将返回锁定该文件的任何内容的List
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;
}
}
Then, iterate the list of processes and close them and delete the files:
然后,迭代进程列表并关闭它们并删除文件:
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);
}
And depending on if the file is on the local computer:
并且取决于文件是否在本地计算机上:
public static void localProcessKill(string processName)
{
foreach (Process p in Process.GetProcessesByName(processName))
{
p.Kill();
}
}
or a network computer:
或网络计算机:
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();
}
}
}
References:
How do I find out which process is locking a file using .NET?
参考:如何找出使用.NET锁定文件的进程?
Delete a directory where someone has opened a file
删除某人打开文件的目录