5,317,180 members and growing! (21,480 online)
Email Password   helpLost your password?
Desktop Development » Shell and IE programming » General     Intermediate License: The Code Project Open License (CPOL)

Listing Used Files

By Naveen

A ShellExtension that lists all the used files in a folder
VC6, C++, WindowsVS6, Visual Studio, Dev

Posted: 28 May 2007
Updated: 6 Jun 2008
Views: 31,717
Announcements
Want a new Job?



Search    
Advanced Search
Sitemap
42 votes for this Article.
Popularity: 7.43 Rating: 4.58 out of 5
1 vote, 2.4%
1
0 votes, 0.0%
2
1 vote, 2.4%
3
9 votes, 21.4%
4
31 votes, 73.8%
5
Screenshot - Application.jpg

Introduction

This is a utility to display all the files that are opened under a specific folder.

Background

It often happens to me that when I try to rename or delete my current working folder of a project, it gives an error message like, "Cannot rename BlaBlaBla: It is being used by another person or program". Almost at all times, I fail to find this other program/person and end up restarting the machine. Since then, I have been searching for a method to find the list of files that an application uses. Even though the ProcessExplorer of SysInternals lists the files opened by each process, it is hard to find opened files in a particular folder. So I created one that does this, purely because of my necessity.

Using the Code

Download the application and register it using RegSvr32 (RegSvr32 OpenedFiles.dll). This application basically contains two components: an extension DLL and a driver. The extension DLL is responsible for showing the context menu in the shell and the GUI you see. I will explain the role of the driver later in this article.

This application will install a menu on folders, the background of a folder and on a drive. I suggest you read The Complete Idiot's Guide to Writing Shell Extensions - Part I, article by Michael Dunn, if you are new to shell extensions. In fact, it was from there that I learned to write shell extensions.

Screenshot - context_menu.jpg

Enumerating Opened Files

When you click on the "List Opened Files" menu, it will first enumerate all of the opened files in the system. This is implemented in the following function:

void MainDlg::EnumerateOpenedFiles( HANDLE hDriver )
{
    ...............
    // Get the list of all handles in the system
    PSYSTEM_HANDLE_INFORMATION pSysHandleInformation =
        new SYSTEM_HANDLE_INFORMATION;
    DWORD size = sizeof(SYSTEM_HANDLE_INFORMATION);
    DWORD needed = 0;
    NTSTATUS status = NtQuerySystemInformation
        ( SystemHandleInformation, pSysHandleInformation, size, &needed );
    if( !NT_SUCCESS(status))
    {
        if( 0 == needed )
        {
            return;// some other error
        }
        // The previously supplied buffer wasn't enough.
        delete pSysHandleInformation;
        size = needed + 1024;
        pSysHandleInformation = (PSYSTEM_HANDLE_INFORMATION)new BYTE[size];
        status = NtQuerySystemInformation
            SystemHandleInformation, pSysHandleInformation, size, &needed );
        if( !NT_SUCCESS(status))
        {
            // some other error so quit.
            delete pSysHandleInformation;
            return;
        }
    }
    int nCount = m_list.GetItemCount() - 1;

    // Walk through the handle list
    for ( DWORD i = 0; i < pSysHandleInformation->dwCount; i++ )
    {
        SYSTEM_HANDLE& sh = pSysHandleInformation->Handles[i];
        if( sh.bObjectType != nFileType )
        // Under Windows XP value of file handle type is 28 and
        // in Windows Vista it is 25
        {
            continue;
        }
        HANDLE_INFO stHandle = {0};
        ADDRESS_INFO stAddress;
        stAddress.pAddress = sh.pAddress;
        DWORD dwReturn = 0;
        BOOL bSuccess = DeviceIoControl( hDriver,
            IOCTL_LISTDRV_BUFFERED_IO, &stAddress, sizeof(ADDRESS_INFO),
            &stHandle, sizeof(HANDLE_INFO), &dwReturn, NULL );
        ...

For every file opened in the system, there will be a handle associated with it. With the NtQuerySystemInformation API, we can retrieve all of the handles in the system. This includes file handles, Mutex handles, Event handles, etc. Actually, the NtQuerySystemInformation function returns an array of the structure SYSTEM_HANDLE.

typedef struct _SYSTEM_HANDLE
{
    DWORD    dwProcessId;
    BYTE     bObjectType;
    BYTE     bFlags;
    WORD     wValue;
    PVOID    pAddress;
    DWORD    GrantedAccess;
}
SYSTEM_HANDLE;

For an object of type file, the value bObjectType in SYSTEM_HANDLE is 28 in Windows XP, 25 in Windows Vista and 26 in Windows 2000.Since this application supports only XP/Vista, we can ignore all the items with values other than 28/25. In SYSTEM_HANDLE, there is another important member for us, the pAddress. For each file handle, there is a FILE_OBJECT associated with it and the pAddress is a pointer to that structure. Unfortunately, this address points to kernel memory space and a user mode application cannot access this memory. Here comes the role of a driver. Using the function DeviceIoControl, the pAddress is passed to the driver. The driver accepts this address and copies the file name from FILE_OBJECT, setting it in the out parameter of the DeviceIoControl function.

As I mentioned earlier, the process explorer shows the information about all the handles used by the application. However, the process explorer is a stand-alone application. So where is the driver? Or is it doing all this without the driver? This question puzzled me for many days until someone told me that the driver is added in the resource of the process explorer. Nice trick, isn't it? So I thought of implementing the same feature in my application too. The function given below performs this task:

HANDLE ExtractAndInstallDrv()
{
    SC_HANDLE hSCManager =
        OpenSCManager( NULL, NULL, SC_MANAGER_ALL_ACCESS );
    SC_HANDLE hService = OpenService
        ( hSCManager , DRV_NAME, SERVICE_ALL_ACCESS);
    CString csPath;
    if( 0 == hService )
    {
        // Service not installed. So install the service.
        // First extract the resource
        HINSTANCE hModule= AfxGetInstanceHandle();
        HRSRC hRsrc = FindResource
            (hModule, MAKEINTRESOURCE(IDR_XPDRIVER),_T("BINARY"));
        HGLOBAL hDrvRsrc = LoadResource(hModule, hRsrc);
        DWORD dwDriverSize = SizeofResource(hModule, hRsrc);
        LPVOID lpvDriver = LockResource(hDrvRsrc);
        CFile File;
        if( !File.Open( DRV_FILE_NAME, CFile::modeCreate|CFile::modeWrite ))
        {
            return 0;
        }
        File.Write( lpvDriver, dwDriverSize );
        csPath = File.GetFilePath();
        File.Close();
        ...
    }

I made an application that does the above things and started using it. Some days later, again that rename problem occurred and my application listed nothing. When I looked in the task manager, I found out that this time it was because of the applications that were running from my folder. So my next step was to enumerate the EXEs and any DLLs that have been loaded from my folder.

Enumerating Loaded Modules

Enumerating the modules loaded by applications was somewhat easy compared to the above task. It just made use of the following APIs. The EnumerateLoadedModules function handles this task in the application.

EnumProcesses()
CreateToolhelp32Snapshot()
Module32First()
Module32Next()

Close Handle Option

During the initial development of this utility, I purposefully ignored the option for closing any file handles opened by another process. I though it is unsafe if it provides such an option cause, closing the handle may lead to unexpected results in that application. However, I later got into some situations like the folder I want to rename is used by service.exe. And if I want to rename the folder, the only option is to terminate the process. But terminating the service.exe will make the entire system unstable. And so I was forced to include a Close Handle option too.

So how this option basically work is, it duplicates the handle using the DuplicateHandle() function with DUPLICATE_CLOSE_SOURCE flag specified. After that, it closes the duplicate handle also using the CloseHandle() function. This option will work only for loaded files, i.e. using this option on a DLL used by another process is just ignored.

HANDLE hDup = 0;
BOOL b = DuplicateHandle( hProcess, hFile, GetCurrentProcess(), 
    &hDup, DUPLICATE_SAME_ACCESS , FALSE, DUPLICATE_CLOSE_SOURCE );
if( hDup )
{
    CloseHandle( hDup );
}
CloseHandle( hProcess );

Basically with the above code, we can close the handles created by another process.After closing the handle, we can rename or delete that file or directory. But there are cases where after closing the handle, we can rename the folder but deleting is not possible. For example, consider the file C:\WINDOWS\system32\config\CstEvent.Evt. Normally this file is used by the Service.exe for event logging purposes. So if we try to close this file handle using the above option, the handle will be closed and we can rename too, but cannot delete!! I tried many ways to solve this, but couldn't come up with a working solution. If anybody knows any other options to solve this, please let me know.

Miscellaneous Features

Auto Complete in Combobox

Screenshot - AutoComplete.jpg

You have probably noticed that when we type some paths in the RUN Dialog of Windows, it will list the files and folders under that path. Similarly when you type a path in the combobox of this application, it will list the files and folder under that path. SHAutoComplete takes care of this feature. This function needs a handle of edit control. But I only have a combobox - how do I take the edit control inside it? Well, the edit control inside the combobox has control id 1001. So use the GetDlgItem() function with this ID to get the handle of the edit control. You can also specify a filename in that combobox.

BOOL MainDlg::OnInitDialog()
{
    .....
    CWnd* pEdit =  m_combobox.GetDlgItem( 1001 );
    if( pEdit )
    {
        SHAutoComplete( pEdit->m_hWnd, SHACF_FILESYSTEM );
    }
}

Find Target Option

When you right click on one item in the list control of this application, you will find a menu called "Find Target". This option is something like the find target option in Windows Explorer. It opens Windows Explorer window with a specified file selected in its folder. The SHOpenFolderAndSelectItems() is used for implementing this feature.

void MainDlg::OnMainFindtarget()
{
    ....
    CString csPath;
    csPath = m_list.GetItemText( nItem,2 );
    LPITEMIDLIST stId = 0;
    SFGAOF stSFGAOFIn = 0;
    SFGAOF stSFGAOFOut = 0;
    if( !FAILED(SHParseDisplayName( csPath, 0, &stId,stSFGAOFIn, &stSFGAOFOut )))
    {
        SHOpenFolderAndSelectItems( stId, 0, 0, 0 );
    }
}

Show Loaded Modules and Show Loaded Files - Options

As mentioned above, the list control in the application lists includes two types of items - loaded files and loaded modules (DLLs, OCX, EXE etc.). This option is to Show/Hide one type of item from the list.

Note

This application uses a drivers build for Windows XP/Vista and so it will run only on Windows XP/Vista. If you want, you can add support for other OS versions as well by building the driver.

Revision History

June-06-2008

  • The bug described in the below post[^] has been fixed
  • Close Handle and Close all handle options added
  • "Show Loaded Modules" and "Show Loaded Files" options added

October-12-2007

  • Application icon is displayed along with process
  • Miscellaneous Features - Section added

September-27-2007

  • "Find Target" menu added
  • Added driver for Windows Vista
  • Added VC8 solution

June-30-2007

  • Article moved

June-4-2007

  • Article updated

May-28-2007

  • Original version posted

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Naveen



Occupation: Software Developer
Location: India India

Other popular Shell and IE programming articles:

Article Top
Sign Up to vote for this article
You must Sign In to use this message board.
FAQ FAQ Noise ToleranceSearch Search Messages 
 Layout  Per page   
 Msgs 1 to 25 of 68 (Total in Forum: 68) (Refresh)FirstPrevNext
Subject  Author Date 
Generalproblem: opened wordpad filesmemberempass6:30 17 Jul '08  
GeneralRe: problem: opened wordpad filesmemberNaveen15:15 17 Jul '08  
GeneralRe: problem: opened wordpad filesmemberempass21:49 17 Jul '08  
Generalthanks and requestmemberempass5:29 16 Jul '08  
GeneralRe: thanks and requestmemberNaveen15:05 16 Jul '08  
QuestionIt doesn't work on cdrom drive? [modified]memberhanbear23:08 10 May '08  
AnswerRe: It doesn't work on cdrom drive?member Naveen17:49 11 May '08  
GeneralRe: It doesn't work on cdrom drive?memberhanbear16:12 13 May '08  
GeneralRe: It doesn't work on cdrom drive?member Naveen16:15 13 May '08  
GeneralRe: It doesn't work on cdrom drive?memberhanbear23:46 13 May '08  
GeneralWhy EnumerateOpenedFiles function almost not be called and ExtractAndInstallDrv return failed,but it works fine?memberoexpress0:46 23 Apr '08  
GeneralRe: Why EnumerateOpenedFiles function almost not be called and ExtractAndInstallDrv return failed,but it works fine?member Naveen0:53 23 Apr '08  
GeneralRe: Why EnumerateOpenedFiles function almost not be called and ExtractAndInstallDrv return failed,but it works fine?memberoexpress1:32 23 Apr '08  
GeneralRe: Why EnumerateOpenedFiles function almost not be called and ExtractAndInstallDrv return failed,but it works fine?member Naveen3:37 23 Apr '08  
GeneralRe: Why EnumerateOpenedFiles function almost not be called and ExtractAndInstallDrv return failed,but it works fine?memberoexpress4:29 23 Apr '08  
GeneralRe: Why EnumerateOpenedFiles function almost not be called and ExtractAndInstallDrv return failed,but it works fine?member Naveen15:17 23 Apr '08  
GeneralRe: Why EnumerateOpenedFiles function almost not be called and ExtractAndInstallDrv return failed,but it works fine?memberoexpress16:42 23 Apr '08  
GeneralRe: Why EnumerateOpenedFiles function almost not be called and ExtractAndInstallDrv return failed,but it works fine?member Naveen16:54 23 Apr '08  
Generalreagarding doubt with our project!!memberrowdy_vc++20:52 18 Mar '08  
GeneralRe: reagarding doubt with our project!!member Naveen21:11 18 Mar '08  
Generalmy vc6 can not complie the project,error C2065: 'ACTCTX' : undeclared identifiermemberNANYANGYEREN21:25 7 Jan '08  
GeneralRe: my vc6 can not complie the project,error C2065: 'ACTCTX' : undeclared identifiermember Naveen17:47 9 Jan '08  
QuestionRequest For NET 2003 Project Filesmembera carl5:40 28 Nov '07  
AnswerRe: Request For NET 2003 Project FilesmemberNaveen.R17:18 28 Nov '07  
GeneralHow to know process has opened which filemembersrajput18:07 22 Nov '07  

General General    News News    Question Question    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

<