Sebastian Southwell
Sebastian Southwell

Reputation: 31

Path.GetDirectoryName produces Win32Exception 'Access is denied'

Hi I am currently trying to obtain the directory of a running process and keep obtaining the Error System.ComponentModel.Win32Exception: 'Access is denied' The following code I am using below and I am currently using a try catch to prevent this error, however a try catch proves to be a slow method requiring valuable processing time, and I am here to ask if there is a method to detect if a process's data is accessible via boolean to prevent a try catch statement from being required and therefore decrease processing time.

string dir = System.IO.Path.GetDirectoryName(process.MainModule.FileName)

Upvotes: 0

Views: 345

Answers (1)

HTTP 410
HTTP 410

Reputation: 17618

If you look at the documentation for the Process.MainModule property, you can see that it throws a Win32Exception if a 32-bit process tries to access a 64-bit process. You can check whether this is the case by looking at the Win32Exception.ErrorCode (I assume 0x00000005 in this case) and Win32Exception.Message properties. And you can retrieve the bitness of your process by evaluating Environment.Is64BitProcess.

Even running as a 64-bit process your program won't be allowed to access MainModule of a System process (4) or a System Idle Process (0). This will throw a Win32Exception with the message:

Unable to enumerate the process modules.

You can use Process.Id to check for 0 or 4.

Alternatively the Win32 API allows you to query every process with the LimitedQueryInformation flag, which means you can check the path without receiving an exception. Unfortunately the .NET Base Class Library doesn't expose this flag, so you need P/Invoke to do that. The following C# console program never triggers an exception (when run on my local machine). If it's run with local admin privileges, it returns all path names except for process 0 and process 4. If it's run as a normal user, it understandably only lists the pathnames for those processes to which it has access.

using System;
using System.Text;
using System.Diagnostics;
using System.Runtime.InteropServices;

namespace NewCmd
{
    class Program
    {
        [Flags]
        private enum ProcessAccessFlags : uint
        {
            QueryLimitedInformation = 0x00001000
        }

        [DllImport("kernel32.dll", SetLastError = true)]
        private static extern bool QueryFullProcessImageName
            (
            [In] IntPtr hProcess, 
            [In] int dwFlags, 
            [Out] StringBuilder lpExeName, 
            ref int lpdwSize
            );

        [DllImport("kernel32.dll", SetLastError = true)]
        private static extern IntPtr OpenProcess
            (
            ProcessAccessFlags processAccess, 
            bool bInheritHandle, 
            int processId
            );

        static void Main(string[] args)
        {
            foreach (Process p in Process.GetProcesses())
            {
                Console.WriteLine(String.Format("Id: {0} Name: {1}", p.Id, p.ProcessName));
                Console.WriteLine(String.Format("Path: {0}", GetProcessFilename(p)));
            }
            Console.ReadLine();
        }

        static String GetProcessFilename(Process p)
        {
            int capacity = 2000;
            StringBuilder builder = new StringBuilder(capacity);
            IntPtr ptr = OpenProcess(ProcessAccessFlags.QueryLimitedInformation, false, p.Id);
            if (QueryFullProcessImageName(ptr, 0, builder, ref capacity))
            {
                return builder.ToString();
            }
            else
            {
                return "[Missing]";
            }
        }
    }
}

Upvotes: 2

Related Questions