C++/MFC File Unlocker - CodeProject

:

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.

//
// start up the Start manager. This api was created for install utilities to minimize reboots
            dwError= RmStartSession(&dwSession, 0, fullProcessNameSessionKey);
            if (dwError == ERROR_SUCCESS) 
                {
                // tell the start  manager what file we want information for
                PCWSTR pfullProcessNameFile = fname.GetBuffer();
                dwError = RmRegisterResources
                (dwSession, 1, &pfullProcessNameFile, 0, NULL, 0, NULL);
                if (dwError == ERROR_SUCCESS) 
                    {
                    // call it with zero to get the number of processes, 
                    // yup, that is how you ask
                    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 )
                            {
                            // something does have our file open, get the details
                            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);
                                    }
                                // MFC doesn't automatically add the horz scrollbar, 
                                // this turns it on if required
                                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