HandleまたはProcess Monitorの使用に関するいくつかの回答を見てきましたが、自分のコード(C#)でどのプロセスがファイルをロックしているかを確認できます。
私はwin32 APIを使いこなすつもりではないかと考えていますが、誰かがすでにこれを行っており、私を正しい軌道に乗せることができれば、本当に助かります。
HandleまたはProcess Monitorの使用に関するいくつかの回答を見てきましたが、自分のコード(C#)でどのプロセスがファイルをロックしているかを確認できます。
私はwin32 APIを使いこなすつもりではないかと考えていますが、誰かがすでにこれを行っており、私を正しい軌道に乗せることができれば、本当に助かります。
回答:
良い点の1つは、handle.exe
それをサブプロセスとして実行し、出力を解析できることです。
これはデプロイメントスクリプトで行います-魅力のように動作します。
Windowsは単にその情報を追跡していなかったため、ずっと前に、ファイルをロックしているプロセスのリストを確実に取得することは不可能でした。Restart Manager APIをサポートするために、その情報が追跡されるようになりました。
ファイルのパスを取得し、List<Process>
そのファイルをロックしているすべてのプロセスのを返すコードをまとめました。
using System.Runtime.InteropServices;
using System.Diagnostics;
using System;
using System.Collections.Generic;
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;
}
}
制限された権限からの使用(例:IIS)
この呼び出しはレジストリにアクセスします。プロセスがそうする権限を持っていない場合、あなたは、ERROR_WRITE_FAULTを取得する意味します An operation was unable to read or write to the registry
。あなたは可能性があり、選択、レジストリの必要な部分にあなたの制限付きアカウントに権限を付与します。ただし、制限付きアクセスプロセスでフラグを設定し(たとえば、データベースまたはファイルシステムで、またはキューや名前付きパイプなどのプロセス間通信メカニズムを使用して)、2番目のプロセスで再起動マネージャーAPIを呼び出す方が安全です。
IISユーザーに最小限以外のアクセス許可を与えることは、セキュリティ上のリスクです。
tasklist /m YourDllName.dll
、出力を使用して解析できます。stackoverflow.com/questions/152506/…を
C#からWin32を呼び出すのは非常に複雑です。
ツールHandle.exeを使用する必要があります。
その後、C#コードは次のようにする必要があります。
string fileName = @"c:\aaa.doc";//Path to locked file
Process tool = new Process();
tool.StartInfo.FileName = "handle.exe";
tool.StartInfo.Arguments = fileName+" /accepteula";
tool.StartInfo.UseShellExecute = false;
tool.StartInfo.RedirectStandardOutput = true;
tool.Start();
tool.WaitForExit();
string outputTool = tool.StandardOutput.ReadToEnd();
string matchPattern = @"(?<=\s+pid:\s+)\b(\d+)\b(?=\s+)";
foreach(Match match in Regex.Matches(outputTool, matchPattern))
{
Process.GetProcessById(int.Parse(match.Value)).Kill();
}
/accepteula
。ジェナディーの答えを変更して更新しました。
handle.exe
ステファンのソリューションに問題がありました。以下は修正されたバージョンで、うまく機能しているようです。
using System;
using System.Collections;
using System.Diagnostics;
using System.Management;
using System.IO;
static class Module1
{
static internal ArrayList myProcessArray = new ArrayList();
private static Process myProcess;
public static void Main()
{
string strFile = "c:\\windows\\system32\\msi.dll";
ArrayList a = getFileProcesses(strFile);
foreach (Process p in a)
{
Debug.Print(p.ProcessName);
}
}
private static ArrayList getFileProcesses(string strFile)
{
myProcessArray.Clear();
Process[] processes = Process.GetProcesses();
int i = 0;
for (i = 0; i <= processes.GetUpperBound(0) - 1; i++)
{
myProcess = processes[i];
//if (!myProcess.HasExited) //This will cause an "Access is denied" error
if (myProcess.Threads.Count > 0)
{
try
{
ProcessModuleCollection modules = myProcess.Modules;
int j = 0;
for (j = 0; j <= modules.Count - 1; j++)
{
if ((modules[j].FileName.ToLower().CompareTo(strFile.ToLower()) == 0))
{
myProcessArray.Add(myProcess);
break;
// TODO: might not be correct. Was : Exit For
}
}
}
catch (Exception exception)
{
//MsgBox(("Error : " & exception.Message))
}
}
}
return myProcessArray;
}
}
更新
特定のDLLをロックしているプロセスを知りたいだけの場合は、の出力を実行して解析できますtasklist /m YourDllName.dll
。Windows XP以降で動作します。見る
myProcessArray
クラスメンバーなのか(しかし、実際にはgetFileProcesses()からも返されますか?)を確認できませんでしmyProcess
た
これは、他のプロセスによってロックされたDLLに対して機能します。このルーチンは、たとえば、テキストファイルがワードプロセスによってロックされていることを検出しません。
C#:
using System.Management;
using System.IO;
static class Module1
{
static internal ArrayList myProcessArray = new ArrayList();
private static Process myProcess;
public static void Main()
{
string strFile = "c:\\windows\\system32\\msi.dll";
ArrayList a = getFileProcesses(strFile);
foreach (Process p in a) {
Debug.Print(p.ProcessName);
}
}
private static ArrayList getFileProcesses(string strFile)
{
myProcessArray.Clear();
Process[] processes = Process.GetProcesses;
int i = 0;
for (i = 0; i <= processes.GetUpperBound(0) - 1; i++) {
myProcess = processes(i);
if (!myProcess.HasExited) {
try {
ProcessModuleCollection modules = myProcess.Modules;
int j = 0;
for (j = 0; j <= modules.Count - 1; j++) {
if ((modules.Item(j).FileName.ToLower.CompareTo(strFile.ToLower) == 0)) {
myProcessArray.Add(myProcess);
break; // TODO: might not be correct. Was : Exit For
}
}
}
catch (Exception exception) {
}
//MsgBox(("Error : " & exception.Message))
}
}
return myProcessArray;
}
}
VB.Net:
Imports System.Management
Imports System.IO
Module Module1
Friend myProcessArray As New ArrayList
Private myProcess As Process
Sub Main()
Dim strFile As String = "c:\windows\system32\msi.dll"
Dim a As ArrayList = getFileProcesses(strFile)
For Each p As Process In a
Debug.Print(p.ProcessName)
Next
End Sub
Private Function getFileProcesses(ByVal strFile As String) As ArrayList
myProcessArray.Clear()
Dim processes As Process() = Process.GetProcesses
Dim i As Integer
For i = 0 To processes.GetUpperBound(0) - 1
myProcess = processes(i)
If Not myProcess.HasExited Then
Try
Dim modules As ProcessModuleCollection = myProcess.Modules
Dim j As Integer
For j = 0 To modules.Count - 1
If (modules.Item(j).FileName.ToLower.CompareTo(strFile.ToLower) = 0) Then
myProcessArray.Add(myProcess)
Exit For
End If
Next j
Catch exception As Exception
'MsgBox(("Error : " & exception.Message))
End Try
End If
Next i
Return myProcessArray
End Function
End Module
linqでより簡単:
public void KillProcessesAssociatedToFile(string file)
{
GetProcessesAssociatedToFile(file).ForEach(x =>
{
x.Kill();
x.WaitForExit(10000);
});
}
public List<Process> GetProcessesAssociatedToFile(string file)
{
return Process.GetProcesses()
.Where(x => !x.HasExited
&& x.Modules.Cast<ProcessModule>().ToList()
.Exists(y => y.FileName.ToLowerInvariant() == file.ToLowerInvariant())
).ToList();
}