2

I'm currently coding a windows service (installed as LocalSystem) that monitors several things on a pc/server including processes. For the processes, I'm watching the memory usage and also "try" to get the number of GDI Objects per process (as can be seen in task manager).

Sadly, C# Process objects don't have the gdi count built-in so I'm using the GetGuiResources method from 'user32.dll' as shown in this example: https://www.pinvoke.net/default.aspx/user32.getguiresources.

Basically I have a list of executable names, for each of them I use GetProcessesByName to retrieve all process instances, and then for each unique process I take the handle and send it to the function to get the Gdi objects count back.

When I try this on my local machine as a simple console app (feeding a name through Console.ReadLine), it works no problem as long as the console app is launched as administrator; i get the same numbers as task manager. However, when the monitoring service calls this function, I get either 0s (returning error code 87) or worse: processes tied to services (no gui) return me some random numbers (12, 7, 4, etc.) when the task manager actually shows 0 (and last error = 0).

So in summary, every process that shows some GID objects in Task Manager returns 0 (error 87), and each process who has 0 returns me a number (no error, or error 183 for the monitoring service itself).

I've tried this on Windows 10, Windows Server 2012, Windows Server 2008, Windows Server 2003, Windows Server 2016. On windows 10 (my machine) I get 0s everywhere, on other OS I get the mentionned results.

Here's a shortened version of the code I use:

// Monitoring processes    exeName example: ssms, sqlbrowser
List<Process> result = Process.GetProcessesByName(exeName).ToList();
if (processes != null)
{
    for (int i = 0; i < processes.Count; i++)
    {
        int gdiCount = processes[i].GetGDIObjectsCount();  // extension method
        // logging and doing stuff with gdi count here (but i get 0s or random numbers as I told)
    }
}

// Process extension method
public static class CProcessExtensions
{
    [DllImport("User32", SetLastError = true)]
    extern private static int GetGuiResources(IntPtr hProcess, int uiFlags);

    private static int GetGDICount(IntPtr processHandle)
    {
        if (processHandle == IntPtr.Zero)
        {
            return -1;
        }

        int count = GetGuiResources(processHandle, 0);

        // Logging Marshal.GetLastWin32Error() here

        return count;
    }

    public static int GetGDIObjectsCount(this Process process)
    {
        IntPtr handle;
        process.Refresh();

        try
        {
            handle = process.Handle;
        }
        catch (Exception ex)
        {
            handle = IntPtr.Zero;
        }

        return GetGDICount(handle);
    }
}

I've also tried getting the process handles with the OpenProcess dll method but had the same results. Anyone faced this kind of problem before?

ianifique
  • 31
  • 4

1 Answers1

1

So, thanks to Jeremy Thompson's comment leading me to info about the session 0, and with further research, I was able to solve my problem.

References:

Application Loader to launch process in another session

Wait for process exit (ProcessWaitHandle)

Get Exit code

What I did is modify the sample code from the first reference to provide a process ID (the one I want the GDI objects count of) and launch my little console app (which takes the same process ID also, and returns the GDI count as exit code) in the same session by duplicating the token of the provided process and call CreateProcessAsUser. By launching the console app in the same session I was able to retrieve the correct info on GDI objects on every OS I previously tested except Win Server 2003, which I can totally live without.

ianifique
  • 31
  • 4