Introduction
This small utility lets you choose a file, list what processes have it locked open, and optionally, terminate any of the processes.
Background
Why would you want to do this? It is common during development to have a program crash and leave a file locked. Now you can't open this file to edit it for the next test, or whatever. Rebooting fixes this problem, but that takes a while. If you know which process has your file open, you can use task manager to terminate the process, but often it isn't obvious which process has your file locked.
Starting with Windows Vista, there is a system called RestartManager
that tracks which process has what file open. This service was added to help reduce the number of reboots required to install software, but we will use it to discover what process has out file locked.
The code to enumerate the processes is from: http://blogs.msdn.com/b/oldnewthing/archive/2012/02/17/10268840.aspx.
The terminate code is from MSDN.
C++ is used as it has easier access to low level win32
functions.
Using the Code
This is a small stand-alone utility for Windows Vista and later. Start it up, choose the locked file, click "Get Lock Info" to see if any processes have locked the file, then click "Unlock All" to be prompted to terminate each process.
dwError= RmStartSession(&dwSession, 0, fullProcessNameSessionKey);
if (dwError == ERROR_SUCCESS)
{
PCWSTR pfullProcessNameFile = fname.GetBuffer();
dwError = RmRegisterResources
(dwSession, 1, &pfullProcessNameFile, 0, NULL, 0, NULL);
if (dwError == ERROR_SUCCESS)
{
DWORD dwReason;
UINT nProcInfoNeeded=0;
UINT nProcInfo = 0;
RM_PROCESS_INFO *rgpi;
CString text;
rgpi=new RM_PROCESS_INFO;
dwError = RmGetList(dwSession, &nProcInfoNeeded,
&nProcInfo, rgpi, &dwReason);
delete rgpi;
if (dwError == ERROR_SUCCESS || dwError == ERROR_MORE_DATA )
{
if ( nProcInfoNeeded > 0 )
{
TCHAR fullProcessName[MAX_PATH];
DWORD cch = MAX_PATH;
nProcInfo=nProcInfoNeeded;
rgpi=new RM_PROCESS_INFO[nProcInfoNeeded];
dwError = RmGetList(dwSession, &nProcInfoNeeded,
&nProcInfo, rgpi, &dwReason);
if (dwError == ERROR_SUCCESS)
{
UINT i;
HANDLE hProcess;
FILETIME ftCreate, ftExit, ftKernel, ftUser;
m_ListLockInfo.ResetContent();
for (i = 0; i < nProcInfo; i++)
{
hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION,
FALSE, rgpi[i].Process.dwProcessId);
fullProcessName[0]=0;
if (hProcess)
{
if ( GetProcessTimes(hProcess, &ftCreate,
&ftExit,&ftKernel, &ftUser) &&
CompareFileTime(&rgpi[i].Process.ProcessStartTime,
&ftCreate) == 0 )
{
QueryFullProcessImageNameW
(hProcess, 0, fullProcessName, &cch);
}
CloseHandle(hProcess);
}
text.Format(_T("App name:
%ls Full Process Name: %ls App type: %d Process ID: %d"),
rgpi[i].strAppName,
fullProcessName,
rgpi[i].ApplicationType,
rgpi[i].Process.dwProcessId);
m_ListLockInfo.AddString(text);
}
SetHorizontalExtent();
}
else
{
MessageBox(_T("RMGetList failedfor the file.
[1]"), _T("Error"), MB_ICONERROR);
}
delete rgpi;
}
else
{
m_ListLockInfo.AddString(_T("No Locks"));
}
}
else
{
MessageBox(_T("RMGetList failedfor the file.
[0]"), _T("Error"), MB_ICONERROR);
}
}
else
{
MessageBox(_T("Unable to RegsiterResources for the file."),
_T("Error"), MB_ICONERROR);
}
}
else
{
MessageBox(_T("Unable to initialize the RestartManager."),
_T("Error"), MB_ICONERROR);
}
}
Points of Interest
I really don't miss using MFC. If you look at the code, you will see that simply setting the "Use Horz scrollbars on" doesn't actually turn on the Horz scrollbars. You have to set the horizontal extent after adding text to the list box.
I didn't know processes could have the same procID
. Yes they can! Windows reuses them. So if a process started up, and locked your file just as you were running this utility, it could have the same procID
as a process that had your file locked, but stopped thus freeing up the procID
to be used by the next process that then locked your file. Not likely, but this code checks the time the process started to prevent this 'race' condition.
History