C# – Get all files being accessed by a process in 64 bits

Today I would like to end my series working with some system APIs which should run on 64 bits operating system. This last post in this series will discuss how we get all files being accessed by a given process. For example, if I would like to find out which files Yahoo Messenger are accessing, I can use this code snippet as following

static void Main(string[] args)
{
	string strTemp = "";
	m_pTarget = Process.GetProcessesByName("YahooMessenger")[0];
	m_ipProcessHwnd = Win32API.OpenProcess(Win32API.ProcessAccessFlags.DupHandle, false, m_pTarget.Id);
	List<Win32API.SYSTEM_HANDLE_INFORMATION> lstHandles = CustomAPI.GetHandles(m_pTarget);
	for (int nIndex = 0; nIndex < lstHandles.Count; nIndex++)
	{
		strTemp = GetFileDetails(lstHandles[nIndex]).Name;
		if (strTemp != "") Console.WriteLine(strTemp);
	}
	Console.ReadLine();
}

You can see that I used the code snippet from the previous post http://hintdesk.com/c-get-all-handles-of-a-given-process-in-64-bits/ to get all handles of given process. After getting all handles I will see if that handle is relevant to file-handle. If it is a file-handle, I will give the path of that file back. To get these information, I follow 3 steps below.

1. Get basic information of handle

ipBasic = Marshal.AllocHGlobal(Marshal.SizeOf(objBasic));
Win32API.NtQueryObject(ipHandle, (int) Win32API.ObjectInformationClass.ObjectBasicInformation, ipBasic, Marshal.SizeOf(objBasic), ref nLength);
objBasic = (Win32API.OBJECT_BASIC_INFORMATION)Marshal.PtrToStructure(ipBasic, objBasic.GetType());
Marshal.FreeHGlobal(ipBasic);

2. Get object type of handle

ipObjectType = Marshal.AllocHGlobal(objBasic.TypeInformationLength);
nLength = objBasic.TypeInformationLength;
while ((uint)(nReturn = Win32API.NtQueryObject(ipHandle, (int)Win32API.ObjectInformationClass.ObjectTypeInformation, ipObjectType, nLength, ref nLength)) == Win32API.STATUS_INFO_LENGTH_MISMATCH)
{
	Marshal.FreeHGlobal(ipObjectType);
	ipObjectType = Marshal.AllocHGlobal(nLength);
}

3. Get name of object type and check if it is a file-handle

objObjectType = (Win32API.OBJECT_TYPE_INFORMATION)Marshal.PtrToStructure(ipObjectType, objObjectType.GetType());
if (Is64Bits())
{
	ipTemp = new IntPtr(Convert.ToInt64(objObjectType.Name.Buffer.ToString(), 10) >> 32);             
}
else
{
	ipTemp = objObjectType.Name.Buffer;          
}

strObjectTypeName = Marshal.PtrToStringUni(ipTemp, objObjectType.Name.Length >> 1);
Marshal.FreeHGlobal(ipObjectType);
if (strObjectTypeName != "File") return fd;


4. Get file name and path to it

nLength = objBasic.NameInformationLength;
ipObjectName = Marshal.AllocHGlobal(nLength);
while ((uint)(nReturn = Win32API.NtQueryObject(ipHandle, (int)Win32API.ObjectInformationClass.ObjectNameInformation, ipObjectName, nLength, ref nLength)) == Win32API.STATUS_INFO_LENGTH_MISMATCH)
{
	Marshal.FreeHGlobal(ipObjectName);
	ipObjectName = Marshal.AllocHGlobal(nLength);
}
objObjectName = (Win32API.OBJECT_NAME_INFORMATION) Marshal.PtrToStructure(ipObjectName, objObjectName.GetType());

if (Is64Bits())
{
	ipTemp = new IntPtr(Convert.ToInt64(objObjectName.Name.Buffer.ToString(), 10) >> 32);               
}
else
{
	ipTemp = objObjectName.Name.Buffer;           
}

byte[] baTemp = new byte[nLength];
Win32API.CopyMemory(baTemp, ipTemp, (uint)nLength);

if (Is64Bits())
{
	strObjectName = Marshal.PtrToStringUni(new IntPtr(ipTemp.ToInt64()));
}
else 
{
	strObjectName = Marshal.PtrToStringUni(new IntPtr(ipTemp.ToInt32()));
}

Marshal.FreeHGlobal(ipObjectName);

fd.Name = GetRegularFileNameFromDevice(strObjectName);
return fd;

I think it’s pretty easy to understand the code if you’ve read 2 posts before.
http://hintdesk.com/c-get-all-handles-of-a-given-process-in-64-bits/
http://hintdesk.com/c-get-object-type-number-in-windows-64-bits/
The complete source code you can download here “Get file details from handle

UPDATE 09.08.2010
If you want to know if the function of system libraries exists or not, you can use CFF Explorer to open the library and look at the Export Section to find out the function you want. For example, in the image below you’ll see the RtlCopyMemory is exported from kernel32.dll and therefore can be called with DllImport.

CFF Explorer

15 thoughts on “C# – Get all files being accessed by a process in 64 bits”

  1. In VS 2008, fails on CopyMemory with “Unable to find an entry point named ‘RtlCopyMemory’ in DLL ‘kernel32.dll'”.

  2. @mjmeans: It does not depend on your VS. It depends maybe on operating system. Which OS are you using?

  3. Your code is fantastic and i had been running on those same problems for a while, all trough, i want to let you know that, in my tests, i wasn’t able to make this work in a 32 bit XP+SP2 box , it will just hang there and use 100% of the cpu.
    Besides that, its running perfectly in 64bits.

  4. @Smoke: I think it can be wrong with the STRUCT of DataType in Windows XP+SP2. Would you like to post your solution here if you find one for XP+SP2? The readers will appreciate for it and so do I.

  5. How do i close the file once i have the handle of the the file which is opened inside of process example if i have opened book1.xlsx in excel it would not allow me to delete this file if its open in excel so how do i close this file without going to excle . from the code i under stand i would have to write the below statement but this dose not work

    Win32API.CloseHandle((IntPtr)lstHandles[nIndex].Handle );

    help would be appriciated.

  6. @Smoke 🙂 the problem of hanging in XP-SP3 (I think it will also work correctly in SP2) because of the nLength is equal to zero in every case, just initialize it with 0x1000. and it works correctly In Shaa Allah :)now it’s working on XP-SP3 32bit and Vista 64bit.

    This is a portion of CODE

    nLength = 0x1000;//objBasic.NameInformationLength; HERE IS THE ERROR
    ipObjectName = Marshal.AllocHGlobal(nLength);
    while ((uint)(nReturn = Win32API.NtQueryObject(ipHandle, (int)Win32API.ObjectInformationClass.ObjectNameInformation, ipObjectName, nLength, ref nLength)) == Win32API.STATUS_INFO_LENGTH_MISMATCH)
    {
    Marshal.FreeHGlobal(ipObjectName);
    ipObjectName = Marshal.AllocHGlobal(nLength);
    }

  7. Hi Guys,

    I think we can run it on win32 by using the following code.In Win32 Api Class provided with in the zip file add the following lines

    [DllImport(“msvcrt.dll”, EntryPoint = “memcpy”, CallingConvention = CallingConvention.Cdecl, SetLastError = false)]
    public static extern IntPtr MemCopy(byte[] Destination, IntPtr src, uint count);

    Then In CustomApi Class Add

    if (Is64Bits())
    Win32API.CopyMemory(baTemp, ipHandlePointer, (uint)nLength);
    else
    Win32API.MemCopy(baTemp, ipHandlePointer, (uint)nLength);

    Same code should be added in GetFileDetails method. It worked on win7 win32. I hope it would work on other OS also as now we are using msvcrt.dll not kernel32.dll for copying memory.

    Thnx
    ritessh

  8. I noticed that sometimes the method was hanging. It was resolved by skipping files that were not of type ‘FileTypeDisk’. It appears there is a known issue with pipes which can cause NtQueryObject to freeze up.

    The solution was to add this definition to Win32API.cs:

    [DllImport(“kernel32.dll”)]
    public static extern FileType GetFileType(IntPtr hFile);
    public enum FileType : uint
    {
    FileTypeChar = 0x0002,
    FileTypeDisk = 0x0001,
    FileTypePipe = 0x0003,
    FileTypeRemote = 0x8000,
    FileTypeUnknown = 0x0000,
    }

    And then add the following code in the method GetFileDetails()

    nLength = objBasic.NameInformationLength; //existing line
    if (nLength == 0)
    {
    Win32API.FileType ft = Win32API.GetFileType(ipHandle);
    if (ft != Win32API.FileType.FileTypeDisk)
    {
    return fd;
    }
    }

  9. Great post!
    I’ve a question, How I can obtain the proccess that modified a file? Or the process that access to a folder?

    Thanks

  10. @Jesus: Loop all processes, list all files accessed by each process and then check if the process is blocking your files or folder.

  11. Hi!
    I’m triying to do it into a Windows Form, but when I execute de app:
    Warning:
    System.EntryPointNotFoundException

    Additional Information: Unable to find the entry point called “RtlCopyMemory” in the Dll File “Kernel32.dll”

    Any idea?

Leave a Reply

Your email address will not be published. Required fields are marked *