[The VC6 version of source code is kept just for reference in case if any one is still using VC6. Also, it does not include any modifications done after June-06-2008 mentioned in the Revision History section.]

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.

For every file opened in the system, there will be a handle associated with it. With the NtQuerySystemInformation API, we can retrieve all 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.

For an object of type file, the value bObjectType in SYSTEM_HANDLE is 28 in Windows XP, Windows 2000, and Window 7; 25 in Windows Vista; and 26 in Windows 2000. Since this application supports only XP/Windows 7, Windows 2000, and 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 driver part. This application accesses this memory using the driver "ListOpenedFileDrv_XX.sys". The only thing the driver does is copy the file name in the kernel memory and pass it to the user mode. 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. [Note: The new version of the application is not keeping the driver in the resource, but it directly uses the driver from the application folder. The below code is kept only for those who refer to the VC6 version of the source code.]

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.

Compatibility with UAC

As you have seen, this application makes use of many API calls that need Administrative privilege. But the Explorer.exe in Vista and later runs with restricted privilege so that the shell extension loaded by the explorer also cannot make any API calls that need Administrative privilege. Which means my current implementation, which had only one DLL that does all the job, is no longer going to work. So the only way is to spool another EXE from the shell extension in Administrative mode. So now, when you click on "List Opened Files", it launches OpenFileFinder.exe using ShellExecute, and the rest of the things are taken care by this process. But I was so lazy to move all the existing feature of in the shell extension DLL to the exe or another DLL. So what I did is I kept the functionality in the DLL itself and exported the function ShowOpenedFiles from the DLL, which when called, shows the opened files dialog. The diagram below show this architecture:

The Problem with Vista 64 Bit and Windows-7 64 Bit

In 64 bit versions of Windows, starting from Vista, Microsoft has enforced that any program that is supposed to be loaded into the kernel space should be digitally signed by Microsoft. This enforcement affect lots of free software and unfortunately to this software also. The core thing in this application is done by our tiny driver which I no longer will be able to load in those versions of Windows. So my initial plan was to discontinue this application in 64 bit Vista and later. However, because of the new APIs which are introduced from Windows Vista, there is still an option to make this work without the driver also. So this application still works in 64 bit Vista and Windows with limited results. The new API that I was talking about is GetFinalPathNameByHandle. However, using the API instead of our driver has some problems like:

This function accepts HANDLE, but the handles are not unique among all processes.

To solve the above problem, we have to duplicate the handle. To duplicate the handle, I have to get the process handle using OpenProcess. But OpenProcess fails for some processes like SYSTEM because of lack of access. Even if we successfully duplicate some other handles, the API still fails saying Access Denied.

Last and the worst, it hangs for certain kinds of handles. You can read mode about this problem in here: GetFinalPathNameByHandle API Hungs. At the time when I was checking this problem in Windows Vista, I remember atleast the GetFileInformationByHandleEx worked in all cases. But now I experience the same problem in Windows 7.

The only workaround to prevent the entire application from hanging is to call the API from a separate thread. So what I did in this application is, if the driver installation fails, it will switch to Plan B, which is actually to use the GetFinalPathNameByHandle in a separate thread. Meanwhile, the main thread will keep on checking whether the worker thread has hung or not using some events. After a time out period, if the main thread does not receive the event, it will terminate the worked thread and spool another worker thread. This is continued until all the the handles have been enumerated.

Compared to the driver method, this one is much slower and returns less results. You have to switch off the diver signature enforcement in 64 bit Vista if you still want this application to work using the driver.

How Third Party Applications Can Make Use of This Application

After the initial version of this application, I received many requests from people to export some functions so that other applications can also make use of this functionality. Starting from this release, OpenFileFinder.dll exports two C functions, which can be called from other binaries:

void ShowOpenedFiles( LPCWSTR lpPath )

You can call this function if you want to show the dialog and display the opened files in it.

If you call this function and pass a callback pointer, for each file handle that matches the input path, the callback function will be called.

The above functions plus the OF_TYPE enum and the OF_CALLBACK function pointer is defined in OpenedFiles.h.

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 was unsafe if it provides such an option because closing the handle may lead to unexpected results in that application. However, I later got into some situations like the folder I wanted to rename was used by service.exe. And if I wanted to rename the folder, the only option was to terminate the process. But terminating 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 works is, it duplicates the handle using the DuplicateHandle() function with the 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.

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 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 options to solve this, please let me know.

Miscellaneous Features

Auto Complete in Combobox

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 folders under that path. SHAutoComplete takes care of this feature. This function needs a handle of the 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 a control ID 1001. Use the GetDlgItem() function with this ID to get the handle of the edit control. You can also specify a filename in that combobox.

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 a Windows Explorer window with a specified file selected in its folder. SHOpenFolderAndSelectItems() is used for implementing this feature.

Show Loaded Modules and Show Loaded Files - Options

As mentioned above, the list control in the application list 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 driver built for Windows XP/Win2003/Vista/Window 7, and so it will run only on those versions. If you want, you can add support for other OS versions as well by building the driver and making the necessary changes to the application.

Revision History

September-29-2010

Added 64 bit support

Added modifications mentioned in the section "Compatibility with UAC"

Added modifications mentioned in the section "The problem with Vista 64 bit and Windows-7 64 bit"

Comments and Discussions

When I restart my computer, the code execute normal,I don't why??
what is difference between EnumerateLoadedModules and EnumerateOpenedFiles?
Does the EnumerateLoadedModules function only can enumerate the user-mode files,and the
EnumerateOpenedFiles can enumerate user-mode and kernel-mode both?Is there the same patial part of these two function?

our project goal is to create one user dll(this dll create for VC 2005) and in this dll load another one java platform (via jni compiler.. it is possible)

first up all .i was create DLL when i attached this dll in JNI compiler its perfectly running in windows XP (it gives return value '0')but in vista it is also running but it gives the return value '255'

The dll with this project is com dll and it dosent exports any function. If you want it as a dll, I have to put this in dll and export the function( if you know VC++, you can do it easily ). Other wise send me your id I will send the project after making it a dll with export functions.

My idea is Unlocker+TaskKiller for Vista(my be and further...), but I have following difficulties:
1. MSDN: [This function may be changed or removed from Windows without further notice.](about NtQuerySystemInformation ...)
2. I write with C# and already tried to import these functions. However, this import works bad even with XP (EnumerateAllOpenFiles & even EnumerateCurrentProcessOpenFiles show not all!)
3. My "VC++" is poor - And I could not see my error, therefore would be glad to see your export-DLL!
4. I have VS 2008(!) by Vista x64(!!!)

Your VS

PS
skopin@versanet.de

PPS
- TEST -
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Runtime.CompilerServices;
using System.Runtime.ConstrainedExecution;
using System.Runtime.InteropServices;
using System.Security.Permissions;
using System.Text;
using System.Threading;
using Microsoft.Win32.SafeHandles;

EnumerateAllOpenFiles & even EnumerateCurrentProcessOpenFiles show not all!)

This is because the NtQueryObject will fail/hang when trying to query the information about some handles. For more information about this check the "Displaying Handle Information In User Mode" section of the article Debug Tutorial Part 5: Handle Leaks[^]

vovikdoc wrote:

4. I have VS 2008(!) by Vista x64(!!!)

We have a problem here. Actually I have build my driver for x86 processors. One thing is that you have to compile and build the driver program with DDK of x64. More over I am not sure the driver will work with out some modification. Any way first check my current application is working on windows vista x64 after rebuilding the driver.

whats the error? I dont have a vista64 machine . Did you check whether the binary file downloaded from this site is working in your machine? If the current binary is working no problem.

BTW I said you to rebuild the driver not the shell extension. I.e if you open the source code zip, you can find two folders inside it. Among that folders the driver project is actually situated in the "ListFileDrv" folder. For building that driver you need "WDK for vista 64". After that replace the IDR_VISTA_DRIVER with the new sys file in the shell extension project and build that too.

> find some one who know
I'm the "greatest" expert in my firm - it is not directly IT-firma, and system programming is only my hobby.

> convert it to c#
I've already tried to do it! But my Vista (XP - "OK" !) does not list any file. (++ EnableAdjustTokens) ("SYNC flag" ?)
To tell the truth, I have not translated ExtractAndInstallDrv - there can be a problem... Whether is it necessary to try to translate all "literally"? (++ I CANNOT use the System.Threading.NativeOverlapped structure by x64 & by x86 I MUST use it! ...)

++ problem
I could not see modules of "system", "audiodg"(!?), "idle" processes.